diff --git a/src/dan200/computer/api/ComputerCraftAPI.java b/src/dan200/computer/api/ComputerCraftAPI.java
new file mode 100644
index 00000000..714ea587
--- /dev/null
+++ b/src/dan200/computer/api/ComputerCraftAPI.java
@@ -0,0 +1,86 @@
+
+package dan200.computer.api;
+import java.lang.reflect.Method;
+
+/**
+ * The static entry point to the ComputerCraft API.
+ * Members in this class must be called after mod_ComputerCraft has been initialised,
+ * but may be called before it is fully loaded.
+ */
+public class ComputerCraftAPI
+{
+ /**
+ * Get the creative mode tab that ComputerCraft items can be found on.
+ * Use this to add your peripherals to ComputerCraft's tab.
+ */
+ public static net.minecraft.creativetab.CreativeTabs getCreativeTab()
+ {
+ findCC();
+ if (computerCraft_getCreativeTab != null)
+ {
+ try {
+ return (net.minecraft.creativetab.CreativeTabs)( computerCraft_getCreativeTab.invoke(null) );
+ } catch (Exception e){
+ // It failed
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Registers a peripheral handler for a TileEntity that you do not have access to. Only
+ * use this if you want to expose IPeripheral on a TileEntity from another mod. For your own
+ * mod, just implement IPeripheral on the TileEntity directly.
+ * @see IPeripheral
+ * @see IPeripheralHandler
+ */
+ public static void registerExternalPeripheral( Class extends net.minecraft.tileentity.TileEntity> clazz, IPeripheralHandler handler )
+ {
+ findCC();
+ if (computerCraft_registerExternalPeripheral != null)
+ {
+ try {
+ computerCraft_registerExternalPeripheral.invoke(null, clazz, handler);
+ } catch (Exception e){
+ // It failed
+ }
+ }
+ }
+
+ // The functions below here are private, and are used to interface with the non-API ComputerCraft classes.
+ // Reflection is used here so you can develop your mod in MCP without decompiling ComputerCraft and including
+ // it in your solution.
+
+ private static void findCC()
+ {
+ if( !ccSearched ) {
+ try {
+ computerCraft = Class.forName( "dan200.ComputerCraft" );
+ computerCraft_getCreativeTab = findCCMethod( "getCreativeTab", new Class[] { } );
+ computerCraft_registerExternalPeripheral = findCCMethod( "registerExternalPeripheral", new Class[] {
+ Class.class, IPeripheralHandler.class
+ } );
+ } catch( Exception e ) {
+ System.out.println("ComputerCraftAPI: ComputerCraft not found.");
+ } finally {
+ ccSearched = true;
+ }
+ }
+ }
+
+ private static Method findCCMethod( String name, Class[] args )
+ {
+ try {
+ return computerCraft.getMethod( name, args );
+
+ } catch( NoSuchMethodException e ) {
+ System.out.println("ComputerCraftAPI: ComputerCraft method " + name + " not found.");
+ return null;
+ }
+ }
+
+ private static boolean ccSearched = false;
+ private static Class computerCraft = null;
+ private static Method computerCraft_registerExternalPeripheral = null;
+ private static Method computerCraft_getCreativeTab = null;
+}
diff --git a/src/dan200/computer/api/IComputerAccess.java b/src/dan200/computer/api/IComputerAccess.java
new file mode 100644
index 00000000..c9150530
--- /dev/null
+++ b/src/dan200/computer/api/IComputerAccess.java
@@ -0,0 +1,127 @@
+
+package dan200.computer.api;
+
+/**
+ * The interface passed to peripherals by computers or turtles, providing methods
+ * that they can call. This should not be implemented by your classes. Do not interact
+ * with computers except via this interface.
+ */
+public interface IComputerAccess
+{
+ /**
+ * Creates a new numbered directory in a subPath of the users game save, and return that number. To be used with mountSaveDir.
+ * For example: n = createNewSaveDir( "computer/cdrom" ), will create a new
+ * numbered folder in the "computer/cdrom" subdirectory of the users save file, and return that number.
+ * mountSaveDir( "computer/rom", n ) could then be used to mount that folder onto the computers directory
+ * structure, and the value n could be saved out and used again in future to give the peripheral
+ * persistant storage.
+ * @param subPath A relative file path from the users world save, where the directory should be located.
+ * @return The numeric represenation of the name of the folder created. Will be positive.
+ * @see #mountSaveDir(String, String, int, boolean, long)
+ */
+ public int createNewSaveDir( String subPath );
+
+ /**
+ * Mounts a directory into the computers file system, from a real directory a subPath of the users game save,
+ * with a numerical name. To be used with createNewSaveDir.
+ * For example: n = createNewSaveDir( "computer/cdrom" ), will create a new
+ * numbered folder in the "computer/cdrom" subdirectory of the users save file, and return that number.
+ * mountSaveDir( "computer/rom", n ) could then be used to mount that folder onto the computers directory
+ * structure, and the value n can be saved out by the peripheral and used again, to give the peripheral
+ * persistant storage.
+ * When a directory is mounted, it will appear in the computers file system, and the user will be
+ * able to use file operation to read from and write to the directory (unless readOnly, then only writes will be allowed).
+ * @param desiredLocation The desired location in the computers file system where you would like the directory to appear.
+ * If this location already exists, a number will be appended until a free name is found, and the
+ * actual location will be returned. eg: "cdrom" can become "cdrom2" if two peripherals attempt to
+ * mount "cdrom", or a "cdrom" folder already exists.
+ * @param subPath The real relative file path from the users world save, where the directory to mount can be located.
+ * @param id The numerical name of the folder to mount from the subPath: ex: mountSaveDir( "cdrom", "computer/cdrom", 7 )
+ * will mount the directory "computer/cdrom/7". Use createNewSaveDir to obtain a unique directory id.
+ * @param readOnly Whether the computer will be disallowed from making changes to the mounted directory and modifing or creating files therin.
+ * @param spaceLimit The size limit of the mount, in bytes. Specify 0 to have unlimited capacity.
+ * @return The location in the computers file system where the directory was mounted. This may differ from "desiredLocation", so the
+ * return value should be kept track of so the folder can be unmounted later.
+ * @see #createNewSaveDir(String)
+ * @see #mountFixedDir(String, String, boolean, long)
+ * @see #unmount(String)
+ */
+ public String mountSaveDir( String desiredLocation, String subPath, int id, boolean readOnly, long spaceLimit );
+
+ /**
+ * Mounts a directory into the computers file system, from a real directory in the Minecraft install folder.
+ * For example: mountFixedDir( "stuff", "mods/mymod/lua/stuff", true ), will mount the "lua/stuff" folder from
+ * your mod's directory into the computers filesystem at the location "stuff", with readonly permission, giving the
+ * computer access to those files.
+ * When a directory is mounted, it will appear in the computers file system, and the user will be
+ * able to use file operation to read from and write to the directory (unless readOnly, then only writes will be allowed).
+ * mountFixedDir can also be used to mount files, for example: mountFixedDir( "rom/apis/myapi", "mods/mymod/lua/myapi.lua", true ) can
+ * be used to have the peripheral install an API onto the computer it attaches to.
+ * @param desiredLocation The desired location in the computers file system where you would like the directory to appear.
+ * If this location already exists, a number will be appended until a free name is found, and the
+ * actual location will be returned. eg: "cdrom" can become "cdrom2" if two peripherals attempt to
+ * mount "cdrom", or a "cdrom" folder already exists.
+ * @param subPath The real relative file path from the minecraft install root, where the directory to mount can be located.
+ * @param readOnly Whether the computer will be disallowed from making changes to the mounted directory and modifing or creating files therin.
+ * @param spaceLimit The size limit of the mount, in bytes. Specify 0 to have unlimited capacity.
+ * @return The location in the computers file system where the directory was mounted. This may differ from "desiredLocation", so the
+ * return value should be kept track of so the folder can be unmounted later.
+ * @see #mountSaveDir(String, String, int, boolean, long)
+ * @see #unmount(String)
+ */
+ public String mountFixedDir( String desiredLocation, String path, boolean readOnly, long spaceLimit );
+
+ /**
+ * Unmounts a directory previously mounted onto the computers file system by mountSaveDir or mountFixedDir.
+ * When a directory is unmounted, it will disappear from the computers file system, and the user will no longer be able to
+ * access it. All directories mounted by a mountFixedDir or mountSaveDir are automatically unmounted when the peripheral
+ * is attached if they have not been explicitly unmounted.
+ * @param location The desired location in the computers file system of the directory to unmount.
+ * This must be the location of a directory previously mounted by mountFixedDir() or mountSaveDir(), as
+ * indicated by their return value.
+ * @see #mountSaveDir(String, String, int, boolean, long)
+ * @see #mountFixedDir(String, String, boolean, long)
+ */
+ public void unmount( String location );
+
+ /**
+ * Returns the numerical ID of this computer.
+ * This is the same number obtained by calling os.getComputerID() or running the "id" program from lua,
+ * and is guarunteed unique. This number will be positive.
+ * @return The identifier.
+ */
+ public int getID();
+
+ /**
+ * Equivalent to queueEvent( String event, Object[] arguments ) with an empty arguments array.
+ * @see #queueEvent(String, Object[])
+ */
+ public void queueEvent( String event );
+
+ /**
+ * Causes an event to be raised on this computer, which the computer can respond to by calling
+ * os.pullEvent(). This can be used to notify the computer when things happen in the world or to
+ * this peripheral.
+ * @param event A string identifying the type of event that has occurred, this will be
+ * returned as the first value from os.pullEvent(). It is recommended that you
+ * you choose a name that is unique, and recognisable as originating from your
+ * peripheral. eg: If your peripheral type is "button", a suitable event would be
+ * "button_pressed".
+ * @param arguments In addition to a name, you may pass an array of extra arguments to the event, that will
+ * be supplied as extra return values to os.pullEvent(). Objects in the array will be converted
+ * to lua data types in the same fashion as the return values of IPeripheral.callMethod().
+ * You may supply null to indicate that no arguments are to be supplied.
+ * @see IPeripheral#callMethod
+ */
+ public void queueEvent( String event, Object[] arguments );
+
+ /**
+ * Get a string indicating which "side" of the computer the IComputerAccess this peripheral
+ * has been created for is attached to, relative to the computers orientation. This can be used to
+ * uniquely identify the peripheral when raising events or returning values to the computer.
+ * The value returned by this function will be different for the IComputerAccess for each of
+ * the peripherals attached to the computer.
+ * @return One of "top", "bottom", "left", "right", "front" or "back"
+ */
+ public String getAttachmentSide();
+}
diff --git a/src/dan200/computer/api/IHostedPeripheral.java b/src/dan200/computer/api/IHostedPeripheral.java
new file mode 100644
index 00000000..b4ac9fa6
--- /dev/null
+++ b/src/dan200/computer/api/IHostedPeripheral.java
@@ -0,0 +1,39 @@
+
+package dan200.computer.api;
+import dan200.computer.api.IPeripheral;
+
+/**
+ * A subclass of IPeripheral specifically for peripherals
+ * created by ITurtleUpgrade's of type Peripheral. When an
+ * IHostedPeripheral is created, its IPeripheral methods will be called
+ * just as if the peripheral was a seperate adjacent block in the world,
+ * and update() will be called once per tick.
+ * @see ITurtleUpgrade
+ */
+public interface IHostedPeripheral extends IPeripheral
+{
+ /**
+ * A method called on each hosted peripheral once per tick, on the main thread
+ * over the lifetime of the turtle or block. May be used to update the state
+ * of the peripheral, and may interact with IComputerAccess or ITurtleAccess
+ * however it likes at this time.
+ */
+ public void update();
+
+ /**
+ * A method called whenever data is read from the Turtle's NBTTag,
+ * over the lifetime of the turtle. You should only use this for
+ * reading data you want to stay with the peripheral.
+ * @param nbttagcompound The peripheral's NBTTag
+ */
+ public void readFromNBT( net.minecraft.nbt.NBTTagCompound nbttagcompound );
+
+ /**
+ * A method called whenever data is written to the Turtle's NBTTag,
+ * over the lifetime of the turtle. You should only use this for
+ * writing data you want to stay with the peripheral.
+ * @param nbttagcompound The peripheral's NBTTag.
+ * @param ID The turtle's ID.
+ */
+ public void writeToNBT( net.minecraft.nbt.NBTTagCompound nbttagcompound );
+}
diff --git a/src/dan200/computer/api/IMedia.java b/src/dan200/computer/api/IMedia.java
new file mode 100644
index 00000000..9147b94f
--- /dev/null
+++ b/src/dan200/computer/api/IMedia.java
@@ -0,0 +1,16 @@
+
+package dan200.computer.api;
+
+/**
+ * TODO: Document me
+ */
+public interface IMedia
+{
+ public String getLabel( net.minecraft.item.ItemStack stack );
+ public boolean setLabel( net.minecraft.item.ItemStack stack, String label );
+
+ public String getAudioTitle( net.minecraft.item.ItemStack stack );
+ public String getAudioRecordName( net.minecraft.item.ItemStack stack );
+
+ public String mountData( net.minecraft.item.ItemStack stack, IComputerAccess computer );
+}
diff --git a/src/dan200/computer/api/IPeripheral.java b/src/dan200/computer/api/IPeripheral.java
new file mode 100644
index 00000000..9d565133
--- /dev/null
+++ b/src/dan200/computer/api/IPeripheral.java
@@ -0,0 +1,99 @@
+
+package dan200.computer.api;
+
+/**
+ * The interface that defines a peripheral. This should be implemented by the
+ * TileEntity of any block that you wish to be interacted with by
+ * computer or turtle.
+ */
+public interface IPeripheral
+{
+ /**
+ * Should return a string that uniquely identifies this type of peripheral.
+ * This can be queried from lua by calling peripheral.getType()
+ * @return A string identifying the type of peripheral.
+ */
+ public String getType();
+
+ /**
+ * Should return an array of strings that identify the methods that this
+ * peripheral exposes to Lua. This will be called once before each attachment,
+ * and should not change when called multiple times.
+ * @return An array of strings representing method names.
+ * @see #callMethod
+ */
+ public String[] getMethodNames();
+
+ /**
+ * This is called when a lua program on an attached computer calls peripheral.call() with
+ * one of the methods exposed by getMethodNames().
+ *
+ * Be aware that this will be called from the ComputerCraft Lua thread, and must be thread-safe
+ * when interacting with minecraft objects.
+ * @param computer The interface to the computer that is making the call. Remember that multiple
+ * computers can be attached to a peripheral at once.
+ * @param method An integer identifying which of the methods from getMethodNames() the computer
+ * wishes to call. The integer indicates the index into the getMethodNames() table
+ * that corresponds to the string passed into peripheral.call()
+ * @param arguments An array of objects, representing the arguments passed into peripheral.call().
+ * Lua values of type "string" will be represented by Object type String.
+ * Lua values of type "number" will be represented by Object type Double.
+ * Lua values of type "boolean" will be represented by Object type Boolean.
+ * Lua values of any other type will be represented by a null object.
+ * This array will be empty if no arguments are passed.
+ * @return An array of objects, representing values you wish to return to the lua program.
+ * Integers, Doubles, Floats, Strings, Booleans and null be converted to their corresponding lua type.
+ * All other types will be converted to nil.
+ * You may return null to indicate no values should be returned.
+ * @throws Exception If you throw any exception from this function, a lua error will be raised with the
+ * same message as your exception. Use this to throw appropriate errors if the wrong
+ * arguments are supplied to your method.
+ * @see #getMethodNames
+ */
+ public Object[] callMethod( IComputerAccess computer, int method, Object[] arguments ) throws Exception;
+
+ /**
+ * Is called before the computer attempts to attach to the peripheral, and should return whether to allow
+ * the attachment. Use this to restrict the number of computers that can attach, or to limit attachments to
+ * certain world directions.
+ * If true is returned, attach() will be called shortly afterwards, and the computer will be able to make method calls.
+ * If false is returned, attach() will not be called, and the peripheral will be invisible to the computer.
+ * @param side The world direction (0=bottom, 1=top, etc) that the computer lies relative to the peripheral.
+ * @return Whether to allow the attachment, as a boolean.
+ * @see #attach
+ */
+ public boolean canAttachToSide( int side );
+
+ /**
+ * Is called when canAttachToSide has returned true, and a computer is attaching to the peripheral.
+ * This will occur when a peripheral is placed next to an active computer, when a computer is turned on next to a peripheral,
+ * or when a turtle travels into a square next to a peripheral.
+ * Between calls to attach() and detach(), the attached computer can make method calls on the peripheral using peripheral.call().
+ * This method can be used to keep track of which computers are attached to the peripheral, or to take action when attachment
+ * occurs.
+ *
+ * Be aware that this will be called from the ComputerCraft Lua thread, and must be thread-safe
+ * when interacting with minecraft objects.
+ * @param computer The interface to the computer that is being attached. Remember that multiple
+ * computers can be attached to a peripheral at once.
+ * @see #canAttachToSide
+ * @see #detach
+ */
+ public void attach( IComputerAccess computer );
+
+ /**
+ * Is called when a computer is detaching from the peripheral.
+ * This will occur when a computer shuts down, when the peripheral is removed while attached to computers,
+ * or when a turtle moves away from a square attached to a peripheral.
+ * This method can be used to keep track of which computers are attached to the peripheral, or to take action when detachment
+ * occurs.
+ *
+ * Be aware that this will be called from the ComputerCraft Lua thread, and must be thread-safe
+ * when interacting with minecraft objects.
+ * @param computer The interface to the computer that is being detached. Remember that multiple
+ * computers can be attached to a peripheral at once.
+ * @see #canAttachToSide
+ * @see #detach
+ */
+ public void detach( IComputerAccess computer );
+}
diff --git a/src/dan200/computer/api/IPeripheralHandler.java b/src/dan200/computer/api/IPeripheralHandler.java
new file mode 100644
index 00000000..ef32ea00
--- /dev/null
+++ b/src/dan200/computer/api/IPeripheralHandler.java
@@ -0,0 +1,9 @@
+package dan200.computer.api;
+
+/**
+ * TODO: Document me
+ */
+public interface IPeripheralHandler
+{
+ public IHostedPeripheral getPeripheral( net.minecraft.tileentity.TileEntity tile );
+}
diff --git a/src/dan200/turtle/api/ITurtleAccess.java b/src/dan200/turtle/api/ITurtleAccess.java
new file mode 100644
index 00000000..1a83cdbe
--- /dev/null
+++ b/src/dan200/turtle/api/ITurtleAccess.java
@@ -0,0 +1,145 @@
+
+package dan200.turtle.api;
+import dan200.computer.api.*;
+
+/**
+ * The interface passed to upgrades by turtles, providing methods that they can call.
+ * This should not be implemented by your classes. Do not interact with turtles except via this interface and ITurtleUpgrade.
+ */
+public interface ITurtleAccess
+{
+ /**
+ * Returns the world in which the turtle resides.
+ * @return the world in which the turtle resides.
+ */
+ public net.minecraft.world.World getWorld();
+
+ /**
+ * Returns a vector containing the integer block co-ordinates at which the turtle resides.
+ * @return a vector containing the integer block co-ordinates at which the turtle resides.
+ */
+ public net.minecraft.util.Vec3 getPosition();
+
+ /**
+ * Returns the world direction the turtle is currently facing.
+ * @return the world direction the turtle is currently facing.
+ */
+ public int getFacingDir();
+
+ /**
+ * Returns the size of the turtles inventory, in number of slots. This will currently always be 16.
+ * @return the size of the turtles inventory, in number of slots. This will currently always be 16.
+ */
+ public int getInventorySize();
+
+ /**
+ * Returns which slot the turtle currently has selected in its inventory using turtle.select().
+ * Unlike the 1-based lua representation, this will be between 0 and getInventorySize() - 1.
+ * @return which slot the turtle currently has selected in its inventory
+ */
+ public int getSelectedSlot();
+
+ /**
+ * Returns the item stack that the turtle has in one of its inventory slots.
+ * @param index which inventory slot to retreive, should be between 0 and getInventorySize() - 1
+ * @return the item stack that the turtle has in one of its inventory slots. May be null.
+ */
+ public net.minecraft.item.ItemStack getSlotContents( int index );
+
+ /**
+ * Changes the item stack that the turtle has in one of its inventory slots.
+ * @param index which inventory slot to change, should be between 0 and getInventorySize() - 1
+ * @param stack an item stack to put in the slot. May be null.
+ */
+ public void setSlotContents( int index, net.minecraft.item.ItemStack stack );
+
+ /**
+ * Tries to store an item stack into the turtles current inventory, starting from the turtles
+ * currently selected inventory slot.
+ * @param stack The item stack to try and store.
+ * @return true if the stack was completely stored in the inventory, false if
+ * it was only partially stored, or could not fit at all. If false is returned
+ * and the stack was partially stored, the ItemStack passed into "stack" will now
+ * represent the stack of items that is left over.
+ */
+ public boolean storeItemStack( net.minecraft.item.ItemStack stack );
+
+ /**
+ * Drops an item stack from the turtle onto the floor, or into an inventory is there is one
+ * adjacent to the turtle in the direction specified.
+ * @param stack The item stack to drop.
+ * @param dir The world direction to drop the item
+ * @return true if the stack was dropped, or completely stored in the adjacent inventory, false if
+ * it was only partially stored in the adjacent inventory, or could not fit at all. If false is returned
+ * and the stack was partially stored, the ItemStack passed into "stack" will now
+ * represent the stack of items that is left over.
+ */
+ public boolean dropItemStack( net.minecraft.item.ItemStack stack, int dir );
+
+ /**
+ * "Deploys" an item stack in the direction specified. This simulates a player right clicking, and calls onItemUse() on the Item class.
+ * Will return true if some kind of deployment happened, and may modify the item stack. For block item types, this can be
+ * used to place blocks. Some kinds of items (such as shears when facing a sheep) may modify the turtles inventory during this call.
+ * @param stack The item stack to deploy
+ * @param dir The world direction to deploy the item
+ * @return true if the stack was deployed, false if it was not.
+ */
+ public boolean deployWithItemStack( net.minecraft.item.ItemStack stack, int dir );
+
+ /**
+ * Tries to "attack" entities with an item stack in the direction specified. This simulates a player left clicking, but will
+ * not affect blocks. If an entity is attacked and killed during this call, its dropped items will end up in the turtles
+ * inventory.
+ * @param stack The item stack to attack with
+ * @param dir The world direction to attack with the item
+ * @return true if something was attacked, false if it was not
+ */
+ public boolean attackWithItemStack( net.minecraft.item.ItemStack stack, int dir, float damageMultiplier );
+
+ /**
+ * Returns the current fuel level of the turtle, this is the same integer returned by turtle.getFuelLevel(),
+ * that decreases by 1 every time the turtle moves. Can be used to have your tool or peripheral require or supply
+ * fuel to the turtle.
+ * @return the fuel level
+ */
+ public int getFuelLevel();
+
+ /**
+ * Tries to increase the fuel level of a turtle by burning an item stack. If the item passed in is a fuel source, fuel
+ * will increase and true will be returned. Otherwise, nothing will happen and false will be returned.
+ * @param stack The stack to try to refuel with
+ * @return Whether the turtle was refueled
+ */
+ public boolean refuelWithItemStack( net.minecraft.item.ItemStack stack );
+
+ /**
+ * Removes some fuel from the turtles fuel supply. Negative numbers can be passed in to INCREASE the fuel level of the turtle.
+ * @return Whether the turtle was able to consume the ammount of fuel specified. Will return false if you supply a number
+ * greater than the current fuel level of the turtle.
+ */
+ public boolean consumeFuel( int fuel );
+
+ /**
+ * Adds a custom command to the turtles command queue. Unlike peripheral methods, these custom commands will be executed
+ * on the main thread, so are guaranteed to be able to access Minecraft objects safely, and will be queued up
+ * with the turtles standard movement and tool commands. An issued command will return an unique integer, which will
+ * be supplied as a parameter to a "turtle_response" event issued to the turtle after the command has completed. Look at the
+ * lua source code for "rom/apis/turtle" for how to build a lua wrapper around this functionality.
+ * @param handler an object which will execute the custom command when its point in the queue is reached
+ * @return the unique command identifier described above
+ * @see ITurtleCommandHandler
+ */
+ public int issueCommand( ITurtleCommandHandler handler );
+
+ /**
+ * Returns the upgrade on the specified side of the turtle, if there is one.
+ * @return the upgrade on the specified side of the turtle, if there is one.
+ */
+ public ITurtleUpgrade getUpgrade( TurtleSide side );
+
+ /**
+ * Returns the peripheral created by the upgrade on the specified side of the turtle, if there is one.
+ * @return the peripheral created by the upgrade on the specified side of the turtle, if there is one.
+ */
+ public IHostedPeripheral getPeripheral( TurtleSide side );
+}
diff --git a/src/dan200/turtle/api/ITurtleCommandHandler.java b/src/dan200/turtle/api/ITurtleCommandHandler.java
new file mode 100644
index 00000000..5d397c87
--- /dev/null
+++ b/src/dan200/turtle/api/ITurtleCommandHandler.java
@@ -0,0 +1,20 @@
+
+package dan200.turtle.api;
+
+/**
+ * An interface for objects executing custom turtle commands, used with ITurtleAccess.issueCommand
+ * @see ITurtleAccess#issueCommand( ITurtleCommandHandler )
+ */
+public interface ITurtleCommandHandler
+{
+ /**
+ * Will be called by the turtle on the main thread when it is time to execute the custom command.
+ * The handler should either perform the work of the command, and return true for success, or return
+ * false to indicate failure if the command cannot be executed at this time.
+ * @param turtle access to the turtle for whom the command was issued
+ * @return true for success, false for failure. If true is returned, the turtle will wait 0.4 seconds
+ * before executing the next command in its queue, as it does for the standard turtle commands.
+ * @see ITurtleAccess#issueCommand( ITurtleCommandHandler )
+ */
+ public boolean handleCommand( ITurtleAccess turtle );
+}
diff --git a/src/dan200/turtle/api/ITurtleUpgrade.java b/src/dan200/turtle/api/ITurtleUpgrade.java
new file mode 100644
index 00000000..673205a4
--- /dev/null
+++ b/src/dan200/turtle/api/ITurtleUpgrade.java
@@ -0,0 +1,98 @@
+
+package dan200.turtle.api;
+import dan200.computer.api.*;
+
+/**
+ * The primary interface for defining an upgrade for Turtles. A turtle upgrade
+ * can either be a new tool, or a new peripheral.
+ * @see TurtleAPI#registerUpgrade( ITurtleUpgrade )
+ */
+public interface ITurtleUpgrade
+{
+ /**
+ * Gets a unique numerical identifier representing this type of turtle upgrade.
+ * Like Minecraft block and item IDs, you should strive to make this number unique
+ * among all turtle upgrades that have been released for ComputerCraft.
+ * The ID must be in the range 64 to 255, as the ID is stored as an 8-bit value,
+ * and 0-64 is reserved for future use by ComputerCraft. The upgrade will
+ * fail registration if an already used ID is specified.
+ * @see TurtleAPI#registerUpgrade( ITurtleUpgrade )
+ */
+ public int getUpgradeID();
+
+ /**
+ * Return a String to describe this type of upgrade in turtle item names.
+ * Examples of built-in adjectives are "Wireless", "Mining" and "Crafty".
+ */
+ public String getAdjective();
+
+ /**
+ * Return whether this upgrade adds a tool or a peripheral to the turtle.
+ * Currently, turtle crafting is restricted to one tool & one peripheral per turtle.
+ * @see TurtleUpgradeType for the differences between the two.
+ */
+ public TurtleUpgradeType getType();
+
+ /**
+ * Return an item stack representing the type of item that a turtle must be crafted
+ * with to create a turtle which holds this upgrade.
+ * Currently, turtle crafting is restricted to one tool & one peripheral per turtle.
+ */
+ public net.minecraft.item.ItemStack getCraftingItem();
+
+ /**
+ * Return whether this turtle upgrade is an easter egg, and should be attempted to be hidden
+ * from the creative mode inventory and recipe book plugins.
+ */
+ public boolean isSecret();
+
+ /**
+ * Return which texture file should be used to render this upgrade when rendering a turtle
+ * in the world or as an item.
+ * @param turtle Access to the turtle that is being rendered, this will be null when
+ * the method is being called to render the turtle as an Item. For turtles in the world,
+ * this can be used to have the upgrade change appearance based on state.
+ * @param side Which side of the turtle (left or right) that the upgrade being rendered resides on.
+ * @see #getIconIndex
+ */
+ public String getIconTexture( ITurtleAccess turtle, TurtleSide side );
+
+ /**
+ * Return which icon index should be used to render this upgrade when rendering a turtle
+ * in the world or as an item.
+ * @param turtle Access to the turtle that is being rendered, this will be null when
+ * the method is being called to render the turtle as an Item. For turtles in the world,
+ * this can be used to have the upgrade change appearance based on state.
+ * @param side Which side of the turtle (left or right) that the upgrade being rendered resides on.
+ * @see #getIconTexture
+ */
+ public int getIconIndex( ITurtleAccess turtle, TurtleSide side );
+
+ /**
+ * Will only be called for Peripheral upgrades. Creates a peripheral for a turtle
+ * being placed using this upgrade. The peripheral created will be stored
+ * for the lifetime of the turtle, will have update() called once-per-tick, and will be
+ * attach'd detach'd and have methods called in the same manner as a Computer peripheral.
+ * @param turtle Access to the turtle that the peripheral is being created for.
+ * @param side Which side of the turtle (left or right) that the upgrade resides on.
+ * @returns The newly created peripheral. You may return null if this upgrade is a Tool
+ * and this method is not expected to be called.
+ */
+ public IHostedPeripheral createPeripheral( ITurtleAccess turtle, TurtleSide side );
+
+ /**
+ * Will only be called for Tool upgrades. Called when turtle.dig() or turtle.attack() is called
+ * by the turtle, and the tool is required to do some work.
+ * @param turtle Access to the turtle that the tool resides on.
+ * @param side Which side of the turtle (left or right) the tool resides on.
+ * @param verb Which action (dig or attack) the turtle is being called on to perform.
+ * @param direction Which world direction the action should be performed in, relative to the turtles
+ * position. This will either be up, down, or the direction the turtle is facing, depending on
+ * whether dig, digUp or digDown was called.
+ * @return Whether the turtle was able to perform the action, and hence whether the turtle.dig()
+ * or turtle.attack() lua method should return true. If true is returned, the tool will perform
+ * a swinging animation. You may return false if this upgrade is a Peripheral
+ * and this method is not expected to be called.
+ */
+ public boolean useTool( ITurtleAccess turtle, TurtleSide side, TurtleVerb verb, int direction );
+}
diff --git a/src/dan200/turtle/api/TurtleAPI.java b/src/dan200/turtle/api/TurtleAPI.java
new file mode 100644
index 00000000..f802ddd6
--- /dev/null
+++ b/src/dan200/turtle/api/TurtleAPI.java
@@ -0,0 +1,73 @@
+
+package dan200.turtle.api;
+import java.lang.reflect.Method;
+
+/**
+ * The static entry point to the ComputerCraft Turtle Upgrade API.
+ * Members in this class must be called after mod_CCTurtle has been initialised,
+ * but may be called before it is fully loaded.
+ */
+public class TurtleAPI
+{
+ /**
+ * Registers a new turtle upgrade for use in ComputerCraft. After calling this,
+ * users should be able to craft Turtles with your new upgrade. It is recommended to call
+ * this during the load() method of your mod.
+ * @throws Exception if you try to register an upgrade with an already used or reserved upgradeID
+ * @see ITurtleUpgrade
+ */
+ public static void registerUpgrade( ITurtleUpgrade upgrade )
+ {
+ if( upgrade != null )
+ {
+ findCCTurtle();
+ if( ccTurtle_registerTurtleUpgrade != null )
+ {
+ try {
+ ccTurtle_registerTurtleUpgrade.invoke( null, new Object[]{ upgrade } );
+ } catch( Exception e ) {
+ // It failed
+ }
+ }
+ }
+ }
+
+ // The functions below here are private, and are used to interface with the non-API ComputerCraft classes.
+ // Reflection is used here so you can develop your mod in MCP without decompiling ComputerCraft and including
+ // it in your solution.
+
+ private static void findCCTurtle()
+ {
+ if( !ccTurtleSearched ) {
+ // Search for CCTurtle
+ try {
+ ccTurtle = Class.forName( "dan200.CCTurtle" );
+ ccTurtle_registerTurtleUpgrade = findCCTurtleMethod( "registerTurtleUpgrade", new Class[] {
+ ITurtleUpgrade.class
+ } );
+
+ } catch( ClassNotFoundException e ) {
+ System.out.println("ComputerCraftAPI: CCTurtle not found.");
+
+ } finally {
+ ccTurtleSearched = true;
+
+ }
+ }
+ }
+
+ private static Method findCCTurtleMethod( String name, Class[] args )
+ {
+ try {
+ return ccTurtle.getMethod( name, args );
+
+ } catch( NoSuchMethodException e ) {
+ System.out.println("ComputerCraftAPI: CCTurtle method " + name + " not found.");
+ return null;
+ }
+ }
+
+ private static boolean ccTurtleSearched = false;
+ private static Class ccTurtle = null;
+ private static Method ccTurtle_registerTurtleUpgrade = null;
+}
diff --git a/src/dan200/turtle/api/TurtleSide.java b/src/dan200/turtle/api/TurtleSide.java
new file mode 100644
index 00000000..bc64f711
--- /dev/null
+++ b/src/dan200/turtle/api/TurtleSide.java
@@ -0,0 +1,18 @@
+
+package dan200.turtle.api;
+
+/**
+ * An enum representing the two sides of the turtle that a turtle upgrade might reside.
+ */
+public enum TurtleSide
+{
+ /**
+ * The turtles left side (where the pickaxe usually is on a Wireless Mining Turtle)
+ */
+ Left,
+
+ /**
+ * The turtles right side (where the modem usually is on a Wireless Mining Turtle)
+ */
+ Right,
+}
diff --git a/src/dan200/turtle/api/TurtleUpgradeType.java b/src/dan200/turtle/api/TurtleUpgradeType.java
new file mode 100644
index 00000000..d1b69499
--- /dev/null
+++ b/src/dan200/turtle/api/TurtleUpgradeType.java
@@ -0,0 +1,22 @@
+
+package dan200.turtle.api;
+
+/**
+ * An enum representing the two different types of upgrades that an ITurtleUpgrade
+ * implementation can add to a turtle.
+ * @see ITurtleUpgrade
+ */
+public enum TurtleUpgradeType
+{
+ /**
+ * A tool is rendered as an item on the side of the turtle, and responds to the turtle.dig()
+ * and turtle.attack() methods (Such as pickaxe or sword on Mining and Melee turtles).
+ */
+ Tool,
+
+ /**
+ * A peripheral adds a special peripheral which is attached to the side of the turtle,
+ * and can be interacted with the peripheral API (Such as the modem on Wireless Turtles).
+ */
+ Peripheral,
+}
diff --git a/src/dan200/turtle/api/TurtleVerb.java b/src/dan200/turtle/api/TurtleVerb.java
new file mode 100644
index 00000000..4da83c6c
--- /dev/null
+++ b/src/dan200/turtle/api/TurtleVerb.java
@@ -0,0 +1,21 @@
+
+package dan200.turtle.api;
+
+/**
+ * An enum representing the two different actions that an ITurtleUpgrade of type
+ * Tool may be called on to perform by a turtle.
+ * @see ITurtleUpgrade
+ * @see ITurtleUpgrade#useTool
+ */
+public enum TurtleVerb
+{
+ /**
+ * The turtle called turtle.dig(), turtle.digUp() or turtle.digDown()
+ */
+ Dig,
+
+ /**
+ * The turtle called turtle.attack(), turtle.attackUp() or turtle.attackDown()
+ */
+ Attack,
+}
diff --git a/src/dan200/turtle/api/events/TurtleEvent.java b/src/dan200/turtle/api/events/TurtleEvent.java
new file mode 100644
index 00000000..d5264ca3
--- /dev/null
+++ b/src/dan200/turtle/api/events/TurtleEvent.java
@@ -0,0 +1,16 @@
+package dan200.turtle.api.events;
+
+import dan200.turtle.api.ITurtleAccess;
+import net.minecraftforge.event.Event;
+
+public class TurtleEvent extends Event
+{
+
+ public final ITurtleAccess turtle;
+
+ public TurtleEvent (ITurtleAccess turtle)
+ {
+ this.turtle = turtle;
+ }
+
+}
diff --git a/src/dan200/turtle/api/events/TurtleRefuel.java b/src/dan200/turtle/api/events/TurtleRefuel.java
new file mode 100644
index 00000000..31fd30e2
--- /dev/null
+++ b/src/dan200/turtle/api/events/TurtleRefuel.java
@@ -0,0 +1,30 @@
+package dan200.turtle.api.events;
+
+import dan200.turtle.api.ITurtleAccess;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.event.Cancelable;
+
+@Cancelable
+public class TurtleRefuel extends TurtleEvent
+{
+ public ItemStack itemstack;
+ public int refuelAmount;
+ private boolean handled = false;
+
+ public TurtleRefuel (ITurtleAccess turtle, ItemStack itemstack, int fuelToGive )
+ {
+ super(turtle);
+ this.itemstack = itemstack;
+ this.refuelAmount = fuelToGive;
+ }
+
+ public boolean isHandled()
+ {
+ return handled;
+ }
+
+ public void setHandled()
+ {
+ handled = true;
+ }
+}