Added API for the ingame manual.

This commit is contained in:
Florian Nücke 2015-04-09 18:06:35 +02:00
parent b0c302ff1c
commit b28bb6b2ad
19 changed files with 683 additions and 88 deletions

View File

@ -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;
}

View 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() {
}
}

View 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);
}

View 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);
}

View 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);
}

View 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();
}

View 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;

View 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();
}
}

View File

@ -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();
}
}

View File

@ -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

View 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
}
}

View File

@ -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)

View File

@ -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")

View File

@ -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 {

View File

@ -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)

View File

@ -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

View File

@ -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())
}
}
}
}

View File

@ -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))