mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-14 09:46:53 -04:00
Added API for the ingame manual.
This commit is contained in:
parent
b0c302ff1c
commit
b28bb6b2ad
@ -4,6 +4,7 @@ import li.cil.oc.api.detail.DriverAPI;
|
||||
import li.cil.oc.api.detail.FileSystemAPI;
|
||||
import li.cil.oc.api.detail.ItemAPI;
|
||||
import li.cil.oc.api.detail.MachineAPI;
|
||||
import li.cil.oc.api.detail.ManualAPI;
|
||||
import li.cil.oc.api.detail.NetworkAPI;
|
||||
|
||||
/**
|
||||
@ -15,11 +16,12 @@ import li.cil.oc.api.detail.NetworkAPI;
|
||||
*/
|
||||
public class API {
|
||||
public static final String ID_OWNER = "OpenComputers|Core";
|
||||
public static final String VERSION = "5.0.0";
|
||||
public static final String VERSION = "5.1.0";
|
||||
|
||||
public static DriverAPI driver = null;
|
||||
public static FileSystemAPI fileSystem = null;
|
||||
public static ItemAPI items = null;
|
||||
public static MachineAPI machine = null;
|
||||
public static ManualAPI manual = null;
|
||||
public static NetworkAPI network = null;
|
||||
}
|
||||
|
142
src/main/java/li/cil/oc/api/Manual.java
Normal file
142
src/main/java/li/cil/oc/api/Manual.java
Normal file
@ -0,0 +1,142 @@
|
||||
package li.cil.oc.api;
|
||||
|
||||
import li.cil.oc.api.manual.ContentProvider;
|
||||
import li.cil.oc.api.manual.PathProvider;
|
||||
import li.cil.oc.api.manual.TabIconRenderer;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
/**
|
||||
* This API allows interfacing with the in-game manual of OpenComputers.
|
||||
* <p/>
|
||||
* It allows opening the manual at a desired specific page, as well as
|
||||
* registering custom tabs and content callback handlers.
|
||||
* <p/>
|
||||
* Note: this is a <em>client side only</em> API. It will do nothing on
|
||||
* dedicated servers (i.e. <tt>API.manual</tt> will be <tt>null</tt>).
|
||||
*/
|
||||
public class Manual {
|
||||
/**
|
||||
* Register a tab to be displayed next to the manual.
|
||||
* <p/>
|
||||
* These are intended to link to index pages, and for the time being there
|
||||
* a relatively low number of tabs that can be displayed, so I'd ask you to
|
||||
* only register as many tabs as actually, technically *needed*. Which will
|
||||
* usually be one, for your main index page.
|
||||
*
|
||||
* @param renderer the renderer used to render the icon on your tab.
|
||||
* @param path the path to the page to open when the tab is clicked.
|
||||
*/
|
||||
public static void addTab(TabIconRenderer renderer, String path) {
|
||||
if (API.manual != null)
|
||||
API.manual.addTab(renderer, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a path provider.
|
||||
* <p/>
|
||||
* Path providers are used to find documentation entries for item stacks
|
||||
* and blocks in the world.
|
||||
*
|
||||
* @param provider the provider to register.
|
||||
*/
|
||||
public static void addProvider(PathProvider provider) {
|
||||
if (API.manual != null)
|
||||
API.manual.addProvider(provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a content provider.
|
||||
* <p/>
|
||||
* Content providers are used to resolve paths to page content, if the
|
||||
* standard system (using Minecraft's resource loading facilities) fails.
|
||||
* <p/>
|
||||
* This can be useful for providing dynamic content, for example.
|
||||
*
|
||||
* @param provider the provider to register.
|
||||
*/
|
||||
public static void addProvider(ContentProvider provider) {
|
||||
if (API.manual != null)
|
||||
API.manual.addProvider(provider);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
/**
|
||||
* Look up the documentation path for the specified item stack.
|
||||
*
|
||||
* @param stack the stack to find the documentation path for.
|
||||
* @return the path to the page, <tt>null</tt> if none is known.
|
||||
*/
|
||||
public static String pathFor(ItemStack stack) {
|
||||
if (API.manual != null)
|
||||
return API.manual.pathFor(stack);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up the documentation for the specified block in the world.
|
||||
*
|
||||
* @param world the world containing the block.
|
||||
* @param x the X coordinate of the block.
|
||||
* @param y the Y coordinate of the block.
|
||||
* @param z the Z coordinate of the block.
|
||||
* @return the path to the page, <tt>null</tt> if none is known.
|
||||
*/
|
||||
public static String pathFor(World world, int x, int y, int z) {
|
||||
if (API.manual != null)
|
||||
return API.manual.pathFor(world, x, y, z);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content of the documentation page at the specified location.
|
||||
*
|
||||
* @param path the path of the page to get the content of.
|
||||
* @return the content of the page, or <tt>null</tt> if none exists.
|
||||
*/
|
||||
public static Iterable<String> contentFor(String path) {
|
||||
if (API.manual != null)
|
||||
return API.manual.contentFor(path);
|
||||
return null;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
/**
|
||||
* Open the manual for the specified player.
|
||||
* <p/>
|
||||
* If you wish to display a specific page, call {@link #navigate(String)}
|
||||
* after this function returns, with the path to the page to show.
|
||||
*
|
||||
* @param player the player to open the manual for.
|
||||
*/
|
||||
public static void openFor(EntityPlayer player) {
|
||||
if (API.manual != null)
|
||||
API.manual.openFor(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the history of the manual.
|
||||
*/
|
||||
public static void reset() {
|
||||
if (API.manual != null)
|
||||
API.manual.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to a page in the manual.
|
||||
*
|
||||
* @param path the path to navigate to.
|
||||
*/
|
||||
public static void navigate(String path) {
|
||||
if (API.manual != null)
|
||||
API.manual.navigate(path);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
private Manual() {
|
||||
}
|
||||
}
|
102
src/main/java/li/cil/oc/api/detail/ManualAPI.java
Normal file
102
src/main/java/li/cil/oc/api/detail/ManualAPI.java
Normal file
@ -0,0 +1,102 @@
|
||||
package li.cil.oc.api.detail;
|
||||
|
||||
import li.cil.oc.api.manual.ContentProvider;
|
||||
import li.cil.oc.api.manual.PathProvider;
|
||||
import li.cil.oc.api.manual.TabIconRenderer;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public interface ManualAPI {
|
||||
/**
|
||||
* Register a tab to be displayed next to the manual.
|
||||
* <p/>
|
||||
* These are intended to link to index pages, and for the time being there
|
||||
* a relatively low number of tabs that can be displayed, so I'd ask you to
|
||||
* only register as many tabs as actually, technically *needed*. Which will
|
||||
* usually be one, for your main index page.
|
||||
*
|
||||
* @param renderer the renderer used to render the icon on your tab.
|
||||
* @param path the path to the page to open when the tab is clicked.
|
||||
*/
|
||||
void addTab(TabIconRenderer renderer, String path);
|
||||
|
||||
/**
|
||||
* Register a path provider.
|
||||
* <p/>
|
||||
* Path providers are used to find documentation entries for item stacks
|
||||
* and blocks in the world.
|
||||
*
|
||||
* @param provider the provider to register.
|
||||
*/
|
||||
void addProvider(PathProvider provider);
|
||||
|
||||
/**
|
||||
* Register a content provider.
|
||||
* <p/>
|
||||
* Content providers are used to resolve paths to page content, if the
|
||||
* standard system (using Minecraft's resource loading facilities) fails.
|
||||
* <p/>
|
||||
* This can be useful for providing dynamic content, for example.
|
||||
*
|
||||
* @param provider the provider to register.
|
||||
*/
|
||||
void addProvider(ContentProvider provider);
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
/**
|
||||
* Look up the documentation path for the specified item stack.
|
||||
*
|
||||
* @param stack the stack to find the documentation path for.
|
||||
* @return the path to the page, <tt>null</tt> if none is known.
|
||||
*/
|
||||
String pathFor(ItemStack stack);
|
||||
|
||||
/**
|
||||
* Look up the documentation for the specified block in the world.
|
||||
*
|
||||
* @param world the world containing the block.
|
||||
* @param x the X coordinate of the block.
|
||||
* @param y the Y coordinate of the block.
|
||||
* @param z the Z coordinate of the block.
|
||||
* @return the path to the page, <tt>null</tt> if none is known.
|
||||
*/
|
||||
String pathFor(World world, int x, int y, int z);
|
||||
|
||||
/**
|
||||
* Get the content of the documentation page at the specified location.
|
||||
* <p/>
|
||||
* The provided path may contain the special variable <tt>%LANGUAGE%</tt>,
|
||||
* which will be resolved to the currently set language, falling back to
|
||||
* <tt>en_US</tt>.
|
||||
*
|
||||
* @param path the path of the page to get the content of.
|
||||
* @return the content of the page, or <tt>null</tt> if none exists.
|
||||
*/
|
||||
Iterable<String> contentFor(String path);
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
/**
|
||||
* Open the manual for the specified player.
|
||||
* <p/>
|
||||
* If you wish to display a specific page, call {@link #navigate(String)}
|
||||
* after this function returns, with the path to the page to show.
|
||||
*
|
||||
* @param player the player to open the manual for.
|
||||
*/
|
||||
void openFor(EntityPlayer player);
|
||||
|
||||
/**
|
||||
* Reset the history of the manual.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Navigate to a page in the manual.
|
||||
*
|
||||
* @param path the path to navigate to.
|
||||
*/
|
||||
void navigate(String path);
|
||||
}
|
26
src/main/java/li/cil/oc/api/manual/ContentProvider.java
Normal file
26
src/main/java/li/cil/oc/api/manual/ContentProvider.java
Normal file
@ -0,0 +1,26 @@
|
||||
package li.cil.oc.api.manual;
|
||||
|
||||
/**
|
||||
* This interface allows implementation of content providers for the manual.
|
||||
* <p/>
|
||||
* Content providers can be used to provide possibly dynamic page content for
|
||||
* arbitrary paths. Note that content providers have <em>lower</em> priority
|
||||
* than content found in resource packs, i.e. content providers will only be
|
||||
* queried for missing pages, so to speak.
|
||||
*/
|
||||
public interface ContentProvider {
|
||||
/**
|
||||
* Called to get the content of a path pointed to by the specified path.
|
||||
* <p/>
|
||||
* This should provide an iterable over the lines of a Markdown document
|
||||
* (with the formatting provided by the in-game manual, which is a small
|
||||
* subset of "normal" Markdown).
|
||||
* <p/>
|
||||
* If this provider cannot provide the requested path, it should return
|
||||
* <tt>null</tt> to indicate so, allowing other providers to be queried.
|
||||
*
|
||||
* @param path the path to the manual page we're looking for.
|
||||
* @return the content of the document at that path, or <tt>null</tt>.
|
||||
*/
|
||||
Iterable<String> getContent(String path);
|
||||
}
|
45
src/main/java/li/cil/oc/api/manual/PathProvider.java
Normal file
45
src/main/java/li/cil/oc/api/manual/PathProvider.java
Normal file
@ -0,0 +1,45 @@
|
||||
package li.cil.oc.api.manual;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
/**
|
||||
* Allows providing paths for item stacks and blocks in the world.
|
||||
* <p/>
|
||||
* This is used for generating NEI usage pages with a button opening the manual
|
||||
* on the page at the specified path, or for opening the manual when held in
|
||||
* hand and sneak-activating a block in the world.
|
||||
* <p/>
|
||||
* This way you can easily make entries in your documentation available the
|
||||
* same way OpenComputers does it itself.
|
||||
* <p/>
|
||||
* Note that you can use the special variable <tt>%LANGUAGE%</tt> in your
|
||||
* paths, for language agnostic paths. These will be resolved to the currently
|
||||
* set language, falling back to <tt>en_US</tt>, during actual content lookup.
|
||||
*/
|
||||
public interface PathProvider {
|
||||
/**
|
||||
* Get the path to the documentation page for the provided item stack.
|
||||
* <p/>
|
||||
* Return <tt>null</tt> if there is no known page for this item, allowing
|
||||
* other providers to be queried.
|
||||
*
|
||||
* @param stack the stack to get the documentation path to.
|
||||
* @return the path to the page, <tt>null</tt> if none is known.
|
||||
*/
|
||||
String pathFor(ItemStack stack);
|
||||
|
||||
/**
|
||||
* Get the path to the documentation page for the provided block.
|
||||
* <p/>
|
||||
* Return <tt>null</tt> if there is no known page for this item, allowing
|
||||
* other providers to be queried.
|
||||
*
|
||||
* @param world the world containing the block.
|
||||
* @param x the X coordinate of the block.
|
||||
* @param y the Y coordinate of the block.
|
||||
* @param z the Z coordinate of the block.
|
||||
* @return the path to the page, <tt>null</tt> if none is known.
|
||||
*/
|
||||
String pathFor(World world, int x, int y, int z);
|
||||
}
|
22
src/main/java/li/cil/oc/api/manual/TabIconRenderer.java
Normal file
22
src/main/java/li/cil/oc/api/manual/TabIconRenderer.java
Normal file
@ -0,0 +1,22 @@
|
||||
package li.cil.oc.api.manual;
|
||||
|
||||
/**
|
||||
* Allows defining a renderer for a manual tab.
|
||||
* <p/>
|
||||
* Each renderer instance represents the single graphic it is drawing. To
|
||||
* provide different graphics for different tabs you'll need to create
|
||||
* multiple tab renderer instances.
|
||||
* <p/>
|
||||
* See the prefabs {@link li.cil.oc.api.prefab.ItemStackTabIconRenderer} and
|
||||
* {@link li.cil.oc.api.prefab.IconTabIconRenderer} for simple standard
|
||||
* implementations.
|
||||
*/
|
||||
public interface TabIconRenderer {
|
||||
/**
|
||||
* Called when icon of a tab should be rendered.
|
||||
* <p/>
|
||||
* This should render something in a 16x16 area. The OpenGL state has been
|
||||
* adjusted so that drawing starts at (0,0,0), and should go to (16,16,0).
|
||||
*/
|
||||
void render();
|
||||
}
|
14
src/main/java/li/cil/oc/api/manual/package-info.java
Normal file
14
src/main/java/li/cil/oc/api/manual/package-info.java
Normal file
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* This package contains manual related interfaces.
|
||||
* <p/>
|
||||
* The manual represents the in-game documentation of OpenComputers and any
|
||||
* other mod that may choose to add its documentation to it, such as addon
|
||||
* mods.
|
||||
*/
|
||||
@cpw.mods.fml.common.API(
|
||||
owner = API.ID_OWNER,
|
||||
provides = "OpenComputersAPI|Manual",
|
||||
apiVersion = API.VERSION)
|
||||
package li.cil.oc.api.manual;
|
||||
|
||||
import li.cil.oc.api.API;
|
28
src/main/java/li/cil/oc/api/prefab/IconTabIconRenderer.java
Normal file
28
src/main/java/li/cil/oc/api/prefab/IconTabIconRenderer.java
Normal file
@ -0,0 +1,28 @@
|
||||
package li.cil.oc.api.prefab;
|
||||
|
||||
import li.cil.oc.api.manual.TabIconRenderer;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.util.IIcon;
|
||||
|
||||
/**
|
||||
* Simple implementation of a tab icon renderer using an icon as its graphic.
|
||||
*/
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public class IconTabIconRenderer implements TabIconRenderer {
|
||||
private final IIcon icon;
|
||||
|
||||
public IconTabIconRenderer(IIcon icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
final Tessellator t = Tessellator.instance;
|
||||
t.startDrawingQuads();
|
||||
t.addVertexWithUV(0, 16, 0, icon.getMinU(), icon.getMaxV());
|
||||
t.addVertexWithUV(16, 16, 0, icon.getMaxU(), icon.getMaxV());
|
||||
t.addVertexWithUV(16, 0, 0, icon.getMaxU(), icon.getMinV());
|
||||
t.addVertexWithUV(0, 0, 0, icon.getMinU(), icon.getMinV());
|
||||
t.draw();
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package li.cil.oc.api.prefab;
|
||||
|
||||
import li.cil.oc.api.manual.TabIconRenderer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.RenderHelper;
|
||||
import net.minecraft.client.renderer.entity.RenderItem;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL12;
|
||||
|
||||
/**
|
||||
* Simple implementation of a tab icon renderer using an item stack as its graphic.
|
||||
*/
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public class ItemStackTabIconRenderer implements TabIconRenderer {
|
||||
private final ItemStack stack;
|
||||
|
||||
public ItemStackTabIconRenderer(ItemStack stack) {
|
||||
this.stack = stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
|
||||
RenderHelper.enableGUIStandardItemLighting();
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240);
|
||||
RenderItem.getInstance().renderItemAndEffectIntoGUI(Minecraft.getMinecraft().fontRenderer, Minecraft.getMinecraft().getTextureManager(), stack, 0, 0);
|
||||
RenderHelper.disableStandardItemLighting();
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package li.cil.oc.api.prefab;
|
||||
|
||||
import li.cil.oc.api.manual.TabIconRenderer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
/**
|
||||
* Simple implementation of a tab icon renderer using a full texture as its graphic.
|
||||
*/
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public class TextureTabIconRenderer implements TabIconRenderer {
|
||||
private final ResourceLocation location;
|
||||
|
||||
public TextureTabIconRenderer(ResourceLocation location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
Minecraft.getMinecraft().getTextureManager().bindTexture(location);
|
||||
final Tessellator t = Tessellator.instance;
|
||||
t.startDrawingQuads();
|
||||
t.addVertexWithUV(0, 16, 0, 0, 1);
|
||||
t.addVertexWithUV(16, 16, 0, 1, 1);
|
||||
t.addVertexWithUV(16, 0, 0, 1, 0);
|
||||
t.addVertexWithUV(0, 0, 0, 0, 0);
|
||||
t.draw();
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 216 B |
131
src/main/scala/li/cil/oc/client/Manual.scala
Normal file
131
src/main/scala/li/cil/oc/client/Manual.scala
Normal file
@ -0,0 +1,131 @@
|
||||
package li.cil.oc.client
|
||||
|
||||
import cpw.mods.fml.common.FMLCommonHandler
|
||||
import li.cil.oc.OpenComputers
|
||||
import li.cil.oc.api.detail.ManualAPI
|
||||
import li.cil.oc.api.manual.ContentProvider
|
||||
import li.cil.oc.api.manual.PathProvider
|
||||
import li.cil.oc.api.manual.TabIconRenderer
|
||||
import li.cil.oc.common.GuiType
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.entity.player.EntityPlayer
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.world.World
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.convert.WrapAsJava._
|
||||
import scala.collection.convert.WrapAsScala._
|
||||
import scala.collection.mutable
|
||||
|
||||
object Manual extends ManualAPI {
|
||||
final val LanguageKey = "%LANGUAGE%"
|
||||
|
||||
final val FallbackLanguage = "en_US"
|
||||
|
||||
class History(val path: String, var offset: Int = 0)
|
||||
|
||||
class Tab(val renderer: TabIconRenderer, val path: String)
|
||||
|
||||
val tabs = mutable.Buffer.empty[Tab]
|
||||
|
||||
val pathProviders = mutable.Buffer.empty[PathProvider]
|
||||
|
||||
val contentProviders = mutable.Buffer.empty[ContentProvider]
|
||||
|
||||
val history = new mutable.Stack[History]
|
||||
|
||||
reset()
|
||||
|
||||
override def addTab(renderer: TabIconRenderer, path: String): Unit = {
|
||||
tabs += new Tab(renderer, path)
|
||||
}
|
||||
|
||||
override def addProvider(provider: PathProvider): Unit = {
|
||||
pathProviders += provider
|
||||
}
|
||||
|
||||
override def addProvider(provider: ContentProvider): Unit = {
|
||||
contentProviders += provider
|
||||
}
|
||||
|
||||
override def pathFor(stack: ItemStack): String = {
|
||||
for (provider <- pathProviders) {
|
||||
val path = try provider.pathFor(stack) catch {
|
||||
case t: Throwable =>
|
||||
OpenComputers.log.warn("A path provider threw an error when queried with an item.", t)
|
||||
null
|
||||
}
|
||||
if (path != null) return path
|
||||
}
|
||||
null
|
||||
}
|
||||
|
||||
override def pathFor(world: World, x: Int, y: Int, z: Int): String = {
|
||||
for (provider <- pathProviders) {
|
||||
val path = try provider.pathFor(world, x, y, z) catch {
|
||||
case t: Throwable =>
|
||||
OpenComputers.log.warn("A path provider threw an error when queried with a block.", t)
|
||||
null
|
||||
}
|
||||
if (path != null) return path
|
||||
}
|
||||
null
|
||||
}
|
||||
|
||||
override def contentFor(path: String): java.lang.Iterable[String] = {
|
||||
val language = FMLCommonHandler.instance.getCurrentLanguage
|
||||
contentForWithRedirects(path.replaceAll(LanguageKey, language)).
|
||||
orElse(contentForWithRedirects(path.replaceAll(LanguageKey, FallbackLanguage))).
|
||||
getOrElse(asJavaIterable(Iterable("Document not found: " + path)))
|
||||
}
|
||||
|
||||
override def openFor(player: EntityPlayer): Unit = {
|
||||
if (player.getEntityWorld.isRemote) {
|
||||
player.openGui(OpenComputers, GuiType.Manual.id, player.getEntityWorld, 0, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
def reset(): Unit = {
|
||||
history.clear()
|
||||
history.push(new History(s"doc/$LanguageKey/index.md"))
|
||||
}
|
||||
|
||||
override def navigate(path: String): Unit = {
|
||||
Minecraft.getMinecraft.currentScreen match {
|
||||
case manual: gui.Manual => manual.pushPage(path)
|
||||
case _ => history.push(new History(path))
|
||||
}
|
||||
}
|
||||
|
||||
def makeRelative(path: String, base: String): String =
|
||||
if (path.startsWith("/")) path
|
||||
else {
|
||||
val splitAt = base.lastIndexOf('/')
|
||||
if (splitAt >= 0) base.splitAt(splitAt)._1 + "/" + path
|
||||
else path
|
||||
}
|
||||
|
||||
@tailrec private def contentForWithRedirects(path: String, seen: List[String] = List.empty): Option[java.lang.Iterable[String]] = {
|
||||
if (seen.contains(path)) return Some(asJavaIterable(Iterable("Redirection loop: ") ++ seen ++ Iterable(path)))
|
||||
doContentLookup(path) match {
|
||||
case Some(content) => content.headOption match {
|
||||
case Some(line) if line.toLowerCase.startsWith("#redirect ") =>
|
||||
contentForWithRedirects(makeRelative(line.substring("#redirect ".length), path), seen :+ path)
|
||||
case _ => Some(content)
|
||||
}
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
private def doContentLookup(path: String): Option[java.lang.Iterable[String]] = {
|
||||
for (provider <- contentProviders) {
|
||||
val lines = try provider.getContent(path) catch {
|
||||
case t: Throwable =>
|
||||
OpenComputers.log.warn("A content provider threw an error when queried.", t)
|
||||
null
|
||||
}
|
||||
if (lines != null) return Some(lines)
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ import cpw.mods.fml.common.network.NetworkRegistry
|
||||
import li.cil.oc.Constants
|
||||
import li.cil.oc.OpenComputers
|
||||
import li.cil.oc.Settings
|
||||
import li.cil.oc.api
|
||||
import li.cil.oc.client
|
||||
import li.cil.oc.client.renderer.HighlightRenderer
|
||||
import li.cil.oc.client.renderer.PetRenderer
|
||||
@ -33,6 +34,8 @@ private[oc] class Proxy extends CommonProxy {
|
||||
override def preInit(e: FMLPreInitializationEvent) {
|
||||
super.preInit(e)
|
||||
|
||||
api.API.manual = client.Manual
|
||||
|
||||
MinecraftForge.EVENT_BUS.register(Sound)
|
||||
MinecraftForge.EVENT_BUS.register(gui.Icons)
|
||||
MinecraftForge.EVENT_BUS.register(HighlightRenderer)
|
||||
|
@ -26,6 +26,7 @@ object Textures {
|
||||
val guiDrone = new ResourceLocation(Settings.resourceDomain, "textures/gui/drone.png")
|
||||
val guiKeyboardMissing = new ResourceLocation(Settings.resourceDomain, "textures/gui/keyboard_missing.png")
|
||||
val guiManual = new ResourceLocation(Settings.resourceDomain, "textures/gui/manual.png")
|
||||
val guiManualTab = new ResourceLocation(Settings.resourceDomain, "textures/gui/manual_tab.png")
|
||||
val guiPrinter = new ResourceLocation(Settings.resourceDomain, "textures/gui/printer.png")
|
||||
val guiPrinterInk = new ResourceLocation(Settings.resourceDomain, "textures/gui/printer_ink.png")
|
||||
val guiPrinterMaterial = new ResourceLocation(Settings.resourceDomain, "textures/gui/printer_material.png")
|
||||
|
@ -1,44 +1,23 @@
|
||||
package li.cil.oc.client.gui
|
||||
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.InputStream
|
||||
import java.net.URI
|
||||
import java.util
|
||||
|
||||
import com.google.common.base.Charsets
|
||||
import cpw.mods.fml.common.FMLCommonHandler
|
||||
import li.cil.oc.Localization
|
||||
import li.cil.oc.Settings
|
||||
import li.cil.oc.api
|
||||
import li.cil.oc.client.Manual
|
||||
import li.cil.oc.client.Textures
|
||||
import li.cil.oc.util.PseudoMarkdown
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.Gui
|
||||
import net.minecraft.client.gui.GuiButton
|
||||
import net.minecraft.client.gui.GuiScreen
|
||||
import net.minecraft.client.gui.ScaledResolution
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import org.lwjgl.input.Mouse
|
||||
import org.lwjgl.opengl.GL11
|
||||
|
||||
import scala.collection.convert.WrapAsJava._
|
||||
import scala.collection.mutable
|
||||
import scala.io.Source
|
||||
|
||||
object Manual {
|
||||
final val LanguageKey = "%LANGUAGE%"
|
||||
|
||||
val history = new mutable.Stack[History]
|
||||
|
||||
reset()
|
||||
|
||||
def reset(): Unit = {
|
||||
history.clear()
|
||||
history.push(new History(s"doc/$LanguageKey/index.md"))
|
||||
}
|
||||
|
||||
class History(val path: String) {
|
||||
var offset = 0
|
||||
}
|
||||
|
||||
}
|
||||
import scala.collection.convert.WrapAsScala._
|
||||
|
||||
class Manual extends GuiScreen {
|
||||
final val documentMaxWidth = 230
|
||||
@ -47,6 +26,10 @@ class Manual extends GuiScreen {
|
||||
final val scrollPosY = 6
|
||||
final val scrollWidth = 6
|
||||
final val scrollHeight = 180
|
||||
final val tabPosX = -23
|
||||
final val tabPosY = 7
|
||||
final val tabWidth = 23
|
||||
final val tabHeight = 26
|
||||
|
||||
var guiLeft = 0
|
||||
var guiTop = 0
|
||||
@ -74,37 +57,8 @@ class Manual extends GuiScreen {
|
||||
else path
|
||||
}
|
||||
|
||||
def loadPage(path: String, localized: Boolean = false, seen: List[String] = List.empty): Iterator[String] = {
|
||||
val language = FMLCommonHandler.instance.getCurrentLanguage
|
||||
val resolvedPath = path.replaceAll(Manual.LanguageKey, language)
|
||||
if (seen.contains(resolvedPath)) return Iterator("Redirection loop: ") ++ seen.iterator ++ Iterator(path)
|
||||
val location = new ResourceLocation(Settings.resourceDomain, resolvedPath)
|
||||
var is: InputStream = null
|
||||
try {
|
||||
val resource = Minecraft.getMinecraft.getResourceManager.getResource(location)
|
||||
is = resource.getInputStream
|
||||
// Force resolving immediately via toArray, otherwise we return a read
|
||||
// iterator on a closed input stream (because of the finally).
|
||||
val lines = Source.fromInputStream(is)(Charsets.UTF_8).getLines().toArray
|
||||
lines.headOption match {
|
||||
case Some(line) if line.toLowerCase.startsWith("#redirect ") =>
|
||||
loadPage(resolveLink(line.substring("#redirect ".length), resolvedPath), localized = false, seen :+ path)
|
||||
case _ => lines.iterator
|
||||
}
|
||||
}
|
||||
catch {
|
||||
case e: FileNotFoundException if !localized && language != "en_US" =>
|
||||
loadPage(path.replaceAll(Manual.LanguageKey, "en_US"), localized = true, seen)
|
||||
case t: Throwable =>
|
||||
Iterator(s"Failed loading page '$path':") ++ t.toString.lines
|
||||
}
|
||||
finally {
|
||||
Option(is).foreach(_.close())
|
||||
}
|
||||
}
|
||||
|
||||
def refreshPage(): Unit = {
|
||||
document = PseudoMarkdown.parse(loadPage(Manual.history.top.path))
|
||||
document = PseudoMarkdown.parse(api.Manual.contentFor(Manual.history.top.path))
|
||||
documentHeight = PseudoMarkdown.height(document, documentMaxWidth, fontRendererObj)
|
||||
scrollTo(offset)
|
||||
}
|
||||
@ -129,6 +83,12 @@ class Manual extends GuiScreen {
|
||||
|
||||
override def doesGuiPauseGame = false
|
||||
|
||||
override def actionPerformed(button: GuiButton): Unit = {
|
||||
if (button.id >= 0 && button.id < Manual.tabs.length) {
|
||||
pushPage(Manual.tabs(button.id).path)
|
||||
}
|
||||
}
|
||||
|
||||
override def initGui(): Unit = {
|
||||
super.initGui()
|
||||
|
||||
@ -141,9 +101,15 @@ class Manual extends GuiScreen {
|
||||
xSize = guiSize.getScaledWidth
|
||||
ySize = guiSize.getScaledHeight
|
||||
|
||||
scrollButton = new ImageButton(1, guiLeft + scrollPosX, guiTop + scrollPosY, 6, 13, Textures.guiButtonScroll)
|
||||
scrollButton = new ImageButton(-1, guiLeft + scrollPosX, guiTop + scrollPosY, 6, 13, Textures.guiButtonScroll)
|
||||
add(buttonList, scrollButton)
|
||||
|
||||
for ((tab, i) <- Manual.tabs.zipWithIndex if i < 7) {
|
||||
val x = guiLeft + tabPosX
|
||||
val y = guiTop + tabPosY + i * (tabHeight - 1)
|
||||
add(buttonList, new ImageButton(0, x, y, tabWidth, tabHeight, Textures.guiManualTab))
|
||||
}
|
||||
|
||||
refreshPage()
|
||||
}
|
||||
|
||||
@ -156,6 +122,15 @@ class Manual extends GuiScreen {
|
||||
|
||||
super.drawScreen(mouseX, mouseY, dt)
|
||||
|
||||
for ((tab, i) <- Manual.tabs.zipWithIndex if i < 7) {
|
||||
val x = guiLeft + tabPosX
|
||||
val y = guiTop + tabPosY + i * (tabHeight - 1)
|
||||
GL11.glPushMatrix()
|
||||
GL11.glTranslated(x + 5, y + 5, zLevel)
|
||||
tab.renderer.render()
|
||||
GL11.glPopMatrix()
|
||||
}
|
||||
|
||||
PseudoMarkdown.render(document, guiLeft + 8, guiTop + 8, documentMaxWidth, documentMaxHeight, offset, fontRendererObj, mouseX, mouseY) match {
|
||||
case Some(segment) =>
|
||||
segment.tooltip match {
|
||||
|
@ -1,13 +1,7 @@
|
||||
package li.cil.oc.common.item
|
||||
|
||||
import li.cil.oc.OpenComputers
|
||||
import li.cil.oc.api
|
||||
import li.cil.oc.client.gui
|
||||
import li.cil.oc.common.GuiType
|
||||
import li.cil.oc.common.block.SimpleBlock
|
||||
import li.cil.oc.util.BlockPosition
|
||||
import li.cil.oc.util.ExtendedWorld._
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.entity.player.EntityPlayer
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.world.World
|
||||
@ -16,26 +10,21 @@ class Manual(val parent: Delegator) extends Delegate {
|
||||
override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ItemStack = {
|
||||
if (world.isRemote) {
|
||||
if (player.isSneaking) {
|
||||
gui.Manual.reset()
|
||||
api.Manual.reset()
|
||||
}
|
||||
player.openGui(OpenComputers, GuiType.Manual.id, world, 0, 0, 0)
|
||||
api.Manual.openFor(player)
|
||||
}
|
||||
super.onItemRightClick(stack, world, player)
|
||||
}
|
||||
|
||||
override def onItemUse(stack: ItemStack, player: EntityPlayer, position: BlockPosition, side: Int, hitX: Float, hitY: Float, hitZ: Float): Boolean = {
|
||||
val world = player.getEntityWorld
|
||||
world.getBlock(position) match {
|
||||
case block: SimpleBlock =>
|
||||
api.Manual.pathFor(world, position.x, position.y, position.z) match {
|
||||
case path: String =>
|
||||
if (world.isRemote) {
|
||||
player.openGui(OpenComputers, GuiType.Manual.id, world, 0, 0, 0)
|
||||
Minecraft.getMinecraft.currentScreen match {
|
||||
case manual: gui.Manual =>
|
||||
gui.Manual.reset()
|
||||
val descriptor = api.Items.get(new ItemStack(block))
|
||||
manual.pushPage("block/" + descriptor.name + ".md")
|
||||
case _ =>
|
||||
}
|
||||
api.Manual.openFor(player)
|
||||
api.Manual.reset()
|
||||
api.Manual.navigate(path)
|
||||
}
|
||||
true
|
||||
case _ => super.onItemUse(stack, player, position, side, hitX, hitY, hitZ)
|
||||
|
@ -8,10 +8,7 @@ import codechicken.nei.api.IOverlayHandler
|
||||
import codechicken.nei.api.IRecipeOverlayRenderer
|
||||
import codechicken.nei.recipe.GuiRecipe
|
||||
import codechicken.nei.recipe.IUsageHandler
|
||||
import li.cil.oc.OpenComputers
|
||||
import li.cil.oc.api
|
||||
import li.cil.oc.client.gui
|
||||
import li.cil.oc.common.GuiType
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiButton
|
||||
import net.minecraft.client.gui.inventory.GuiContainer
|
||||
@ -30,9 +27,8 @@ class ManualUsageHandler(path: Option[String]) extends IUsageHandler {
|
||||
override def getUsageHandler(input: String, ingredients: AnyRef*): IUsageHandler = {
|
||||
if (input == "item") {
|
||||
ingredients.collectFirst {
|
||||
case stack: ItemStack if api.Items.get(stack) != null =>
|
||||
val descriptor = api.Items.get(stack)
|
||||
new ManualUsageHandler(Option((if (descriptor.block != null) "block/" else "item/") + descriptor.name + ".md"))
|
||||
case stack: ItemStack if api.Manual.pathFor(stack) != null =>
|
||||
new ManualUsageHandler(Option(api.Manual.pathFor(stack)))
|
||||
}.getOrElse(this)
|
||||
}
|
||||
else this
|
||||
@ -76,11 +72,8 @@ class ManualUsageHandler(path: Option[String]) extends IUsageHandler {
|
||||
val pos = GuiDraw.getMousePosition
|
||||
val mc = Minecraft.getMinecraft
|
||||
if (button.mousePressed(mc, pos.x - container.guiLeft - 5, pos.y - container.guiTop - 16)) {
|
||||
mc.thePlayer.openGui(OpenComputers, GuiType.Manual.id, mc.theWorld, 0, 0, 0)
|
||||
mc.currentScreen match {
|
||||
case manual: gui.Manual => path.foreach(manual.pushPage)
|
||||
case _ =>
|
||||
}
|
||||
api.Manual.openFor(mc.thePlayer)
|
||||
path.foreach(api.Manual.navigate)
|
||||
true
|
||||
}
|
||||
else false
|
||||
|
@ -1,15 +1,24 @@
|
||||
package li.cil.oc.integration.opencomputers
|
||||
|
||||
import java.io.InputStream
|
||||
import java.lang.Iterable
|
||||
|
||||
import com.google.common.base.Charsets
|
||||
import cpw.mods.fml.common.FMLCommonHandler
|
||||
import cpw.mods.fml.common.event.FMLInterModComms
|
||||
import li.cil.oc.Constants
|
||||
import li.cil.oc.OpenComputers
|
||||
import li.cil.oc.Settings
|
||||
import li.cil.oc.api
|
||||
import li.cil.oc.api.detail.ItemInfo
|
||||
import li.cil.oc.api.internal
|
||||
import li.cil.oc.api.manual.ContentProvider
|
||||
import li.cil.oc.api.manual.PathProvider
|
||||
import li.cil.oc.common.EventHandler
|
||||
import li.cil.oc.common.Loot
|
||||
import li.cil.oc.common.SaveHandler
|
||||
import li.cil.oc.common.asm.SimpleComponentTickHandler
|
||||
import li.cil.oc.common.block.SimpleBlock
|
||||
import li.cil.oc.common.event._
|
||||
import li.cil.oc.common.item.Analyzer
|
||||
import li.cil.oc.common.item.Delegator
|
||||
@ -22,10 +31,17 @@ import li.cil.oc.integration.util.BundledRedstone
|
||||
import li.cil.oc.integration.util.WirelessRedstone
|
||||
import li.cil.oc.server.network.WirelessNetwork
|
||||
import li.cil.oc.util.ExtendedNBT._
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.nbt.NBTTagCompound
|
||||
import net.minecraft.util.ResourceLocation
|
||||
import net.minecraft.world.World
|
||||
import net.minecraftforge.common.ForgeChunkManager
|
||||
import net.minecraftforge.common.MinecraftForge
|
||||
|
||||
import scala.collection.convert.WrapAsJava._
|
||||
import scala.io.Source
|
||||
|
||||
object ModOpenComputers extends ModProxy {
|
||||
override def getMod = Mods.OpenComputers
|
||||
|
||||
@ -188,6 +204,9 @@ object ModOpenComputers extends ModProxy {
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
|
||||
api.Manual.addProvider(DefinitionPathProvider)
|
||||
api.Manual.addProvider(ResourceContentProvider)
|
||||
}
|
||||
|
||||
private def blacklistHost(host: Class[_], itemNames: String*) {
|
||||
@ -199,4 +218,46 @@ object ModOpenComputers extends ModProxy {
|
||||
FMLInterModComms.sendMessage("OpenComputers", "blacklistHost", nbt)
|
||||
}
|
||||
}
|
||||
|
||||
object DefinitionPathProvider extends PathProvider {
|
||||
private final val Blacklist = Set(
|
||||
"debugger"
|
||||
)
|
||||
|
||||
override def pathFor(stack: ItemStack): String = Option(api.Items.get(stack)) match {
|
||||
case Some(definition) => checkBlacklisted(definition)
|
||||
case _ => null
|
||||
}
|
||||
|
||||
override def pathFor(world: World, x: Int, y: Int, z: Int): String = world.getBlock(x, y, z) match {
|
||||
case block: SimpleBlock => checkBlacklisted(api.Items.get(new ItemStack(block)))
|
||||
case _ => null
|
||||
}
|
||||
|
||||
private def checkBlacklisted(info: ItemInfo): String =
|
||||
if (info == null || Blacklist.contains(info.name)) null
|
||||
else if (info.block != null) "block/" + info.name + ".md"
|
||||
else "item/" + info.name + ".md"
|
||||
}
|
||||
|
||||
object ResourceContentProvider extends ContentProvider {
|
||||
override def getContent(path: String): Iterable[String] = {
|
||||
val location = new ResourceLocation(Settings.resourceDomain, path)
|
||||
var is: InputStream = null
|
||||
try {
|
||||
val resource = Minecraft.getMinecraft.getResourceManager.getResource(location)
|
||||
is = resource.getInputStream
|
||||
// Force resolving immediately via toArray, otherwise we return a read
|
||||
// iterator on a closed input stream (because of the finally).
|
||||
asJavaIterable(Source.fromInputStream(is)(Charsets.UTF_8).getLines().toArray.toIterable)
|
||||
}
|
||||
catch {
|
||||
case t: Throwable => null
|
||||
}
|
||||
finally {
|
||||
Option(is).foreach(_.close())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ object PseudoMarkdown {
|
||||
/**
|
||||
* Parses a plain text document into a list of segments.
|
||||
*/
|
||||
def parse(document: Iterator[String]): Iterable[Segment] = {
|
||||
def parse(document: Iterable[String]): Iterable[Segment] = {
|
||||
var segments = document.flatMap(line => Iterable(new TextSegment(null, Option(line).getOrElse("")), new NewLineSegment())).toArray
|
||||
for ((pattern, factory) <- segmentTypes) {
|
||||
segments = segments.flatMap(_.refine(pattern, factory))
|
||||
|
Loading…
x
Reference in New Issue
Block a user