textures for hologram projector block; caching display list for hologram; fixed item bounds; added localization; added clear and fill callbacks; fixed pcall and xpcall being exploitable to avoid timeout enforcement

This commit is contained in:
Florian Nücke 2014-02-26 20:57:36 +01:00
parent eb09bdc0ba
commit e04a8767d6
11 changed files with 172 additions and 52 deletions

View File

@ -48,6 +48,7 @@ private[oc] class Proxy extends CommonProxy {
override def postInit(e: FMLPostInitializationEvent) {
super.postInit(e)
TickRegistry.registerTickHandler(HologramRenderer, Side.CLIENT)
TickRegistry.registerTickHandler(ScreenRenderer, Side.CLIENT)
if (Settings.get.rTreeDebugRenderer) {
MinecraftForge.EVENT_BUS.register(WirelessNetworkDebugRenderer)

View File

@ -1,29 +1,65 @@
package li.cil.oc.client.renderer.tileentity
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer
import net.minecraft.tileentity.TileEntity
import li.cil.oc.common.tileentity.Hologram
import net.minecraft.client.renderer.Tessellator
import org.lwjgl.opengl.GL11
import li.cil.oc.util.RenderState
import com.google.common.cache.{RemovalNotification, RemovalListener, CacheBuilder}
import cpw.mods.fml.common.{TickType, ITickHandler}
import java.util
import java.util.concurrent.{Callable, TimeUnit}
import li.cil.oc.client.TexturePreloader
import net.minecraftforge.client.MinecraftForgeClient
import li.cil.oc.common.tileentity.Hologram
import li.cil.oc.util.RenderState
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer
import net.minecraft.client.renderer.{GLAllocation, Tessellator}
import net.minecraft.tileentity.TileEntity
import org.lwjgl.opengl.GL11
import scala.util.Random
object HologramRenderer extends TileEntitySpecialRenderer with Callable[Int] with RemovalListener[TileEntity, Int] with ITickHandler {
val random = new Random()
/** We cache the display lists for the projectors we render for performance. */
val cache = com.google.common.cache.CacheBuilder.newBuilder().
expireAfterAccess(2, TimeUnit.SECONDS).
removalListener(this).
asInstanceOf[CacheBuilder[Hologram, Int]].
build[Hologram, Int]()
/** Used to pass the current screen along to call(). */
private var hologram: Hologram = null
object HologramRenderer extends TileEntitySpecialRenderer {
override def renderTileEntityAt(te: TileEntity, x: Double, y: Double, z: Double, f: Float) {
if (MinecraftForgeClient.getRenderPass != 0) return
val hologram = te.asInstanceOf[Hologram]
hologram = te.asInstanceOf[Hologram]
GL11.glPushAttrib(0xFFFFFFFF)
GL11.glDisable(GL11.GL_CULL_FACE)
// RenderState.disableLighting()
RenderState.makeItBlend()
GL11.glPushMatrix()
GL11.glScaled(1.01, 1.01, 1.01) // Avoid z-fighting with other blocks.
GL11.glTranslated(x - 1, y + 0.5, z - 1)
if (random.nextDouble() < 0.025) {
GL11.glScaled(1 + random.nextGaussian() * 0.01, 1 + random.nextGaussian() * 0.001, 1 + random.nextGaussian() * 0.01)
GL11.glTranslated(random.nextGaussian() * 0.01, random.nextGaussian() * 0.01, random.nextGaussian() * 0.01)
}
GL11.glColorMask(false, false, false, false)
val list = cache.get(hologram, this)
compileOrDraw(list)
GL11.glColorMask(true, true, true, true)
GL11.glDepthFunc(GL11.GL_EQUAL)
compileOrDraw(list)
GL11.glPopMatrix()
GL11.glPopAttrib()
}
def compileOrDraw(list: Int) = if (hologram.dirty) {
val doCompile = !RenderState.compilingDisplayList
if (doCompile) {
hologram.dirty = false
GL11.glNewList(list, GL11.GL_COMPILE_AND_EXECUTE)
}
def isSolid(hx: Int, hy: Int, hz: Int) = {
hx >= 0 && hy >= 0 && hz >= 0 && hx < hologram.width && hy < hologram.height && hz < hologram.width &&
(hologram.volume(hx + hz * hologram.width) & (1 << hy)) != 0
@ -56,54 +92,53 @@ object HologramRenderer extends TileEntitySpecialRenderer {
// South
if (!isSolid(hx, hy, hz + 1)) {
t.setNormal(0, 0, 1)
t.addVertex(wx + s, wy + s, wz + s) // 5
t.addVertex(wx + 0, wy + s, wz + s) // 4
t.addVertex(wx + 0, wy + 0, wz + s) // 7
t.addVertex(wx + s, wy + 0, wz + s) // 6
t.addVertexWithUV(wx + s, wy + s, wz + s, 0, 0) // 5
t.addVertexWithUV(wx + 0, wy + s, wz + s, 1, 0) // 4
t.addVertexWithUV(wx + 0, wy + 0, wz + s, 1, 1) // 7
t.addVertexWithUV(wx + s, wy + 0, wz + s, 0, 1) // 6
}
// North
if (!isSolid(hx, hy, hz - 1)) {
t.setNormal(0, 0, -1)
t.addVertex(wx + s, wy + 0, wz + 0) // 3
t.addVertex(wx + 0, wy + 0, wz + 0) // 2
t.addVertex(wx + 0, wy + s, wz + 0) // 1
t.addVertex(wx + s, wy + s, wz + 0) // 0
t.addVertexWithUV(wx + s, wy + 0, wz + 0, 0, 0) // 3
t.addVertexWithUV(wx + 0, wy + 0, wz + 0, 1, 0) // 2
t.addVertexWithUV(wx + 0, wy + s, wz + 0, 1, 1) // 1
t.addVertexWithUV(wx + s, wy + s, wz + 0, 0, 1) // 0
}
// East
if (!isSolid(hx + 1, hy, hz)) {
t.setNormal(1, 0, 0)
t.addVertex(wx + s, wy + s, wz + s) // 5
t.addVertex(wx + s, wy + 0, wz + s) // 6
t.addVertex(wx + s, wy + 0, wz + 0) // 3
t.addVertex(wx + s, wy + s, wz + 0) // 0
t.addVertexWithUV(wx + s, wy + s, wz + s, 1, 0) // 5
t.addVertexWithUV(wx + s, wy + 0, wz + s, 1, 1) // 6
t.addVertexWithUV(wx + s, wy + 0, wz + 0, 0, 1) // 3
t.addVertexWithUV(wx + s, wy + s, wz + 0, 0, 0) // 0
}
// West
if (!isSolid(hx - 1, hy, hz)) {
t.setNormal(-1, 0, 0)
t.addVertex(wx + 0, wy + 0, wz + s) // 7
t.addVertex(wx + 0, wy + s, wz + s) // 4
t.addVertex(wx + 0, wy + s, wz + 0) // 1
t.addVertex(wx + 0, wy + 0, wz + 0) // 2
t.addVertexWithUV(wx + 0, wy + 0, wz + s, 1, 0) // 7
t.addVertexWithUV(wx + 0, wy + s, wz + s, 1, 1) // 4
t.addVertexWithUV(wx + 0, wy + s, wz + 0, 0, 1) // 1
t.addVertexWithUV(wx + 0, wy + 0, wz + 0, 0, 0) // 2
}
// Up
if (!isSolid(hx, hy + 1, hz)) {
t.setNormal(0, 1, 0)
t.addVertex(wx + s, wy + s, wz + 0) // 0
t.addVertex(wx + 0, wy + s, wz + 0) // 1
t.addVertex(wx + 0, wy + s, wz + s) // 4
t.addVertex(wx + s, wy + s, wz + s) // 5
t.addVertexWithUV(wx + s, wy + s, wz + 0, 0, 0) // 0
t.addVertexWithUV(wx + 0, wy + s, wz + 0, 1, 0) // 1
t.addVertexWithUV(wx + 0, wy + s, wz + s, 1, 1) // 4
t.addVertexWithUV(wx + s, wy + s, wz + s, 0, 1) // 5
}
// Down
if (!isSolid(hx, hy - 1, hz)) {
t.setNormal(0, -1, 0)
t.addVertex(wx + s, wy + 0, wz + s) // 6
t.addVertex(wx + 0, wy + 0, wz + s) // 7
t.addVertex(wx + 0, wy + 0, wz + 0) // 2
t.addVertex(wx + s, wy + 0, wz + 0) // 3
t.addVertexWithUV(wx + s, wy + 0, wz + s, 0, 0) // 6
t.addVertexWithUV(wx + 0, wy + 0, wz + s, 1, 0) // 7
t.addVertexWithUV(wx + 0, wy + 0, wz + 0, 1, 1) // 2
t.addVertexWithUV(wx + s, wy + 0, wz + 0, 0, 1) // 3
}
}
}
}
@ -111,7 +146,37 @@ object HologramRenderer extends TileEntitySpecialRenderer {
t.draw()
GL11.glPopMatrix()
GL11.glPopAttrib()
if (doCompile) {
GL11.glEndList()
}
true
}
else GL11.glCallList(list)
// ----------------------------------------------------------------------- //
// Cache
// ----------------------------------------------------------------------- //
def call = {
val list = GLAllocation.generateDisplayLists(1)
hologram.dirty = true // Force compilation.
list
}
def onRemoval(e: RemovalNotification[TileEntity, Int]) {
GLAllocation.deleteDisplayLists(e.getValue)
}
// ----------------------------------------------------------------------- //
// ITickHandler
// ----------------------------------------------------------------------- //
def getLabel = "OpenComputers.Hologram"
def ticks() = util.EnumSet.of(TickType.CLIENT)
def tickStart(tickType: util.EnumSet[TickType], tickData: AnyRef*) = cache.cleanUp()
def tickEnd(tickType: util.EnumSet[TickType], tickData: AnyRef*) {}
}

View File

@ -74,8 +74,7 @@ object ScreenRenderer extends TileEntitySpecialRenderer with Callable[Int] with
if (screen.hasPower) {
MonospaceFontRenderer.init(tileEntityRenderer.renderEngine)
val list = cache.get(screen, this)
compileOrDraw(list)
compileOrDraw(cache.get(screen, this))
}
GL11.glPopMatrix()

View File

@ -7,23 +7,43 @@ import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.{ItemStack, EnumRarity}
import net.minecraft.world.{World, IBlockAccess}
import net.minecraftforge.common.ForgeDirection
import net.minecraft.util.AxisAlignedBB
import net.minecraft.util.{Icon, AxisAlignedBB}
import net.minecraft.client.renderer.texture.IconRegister
import li.cil.oc.Settings
class Hologram(val parent: SpecialDelegator) extends SpecialDelegate {
val unlocalizedName = "Hologram"
private val icons = Array.fill[Icon](6)(null)
override def rarity = EnumRarity.rare
override def tooltipLines(stack: ItemStack, player: EntityPlayer, tooltip: util.List[String], advanced: Boolean) {
tooltip.addAll(Tooltip.get(unlocalizedName))
}
override def icon(side: ForgeDirection) = Some(icons(side.ordinal()))
override def luminance(world: IBlockAccess, x: Int, y: Int, z: Int) = 15
override def isSolid(world: IBlockAccess, x: Int, y: Int, z: Int, side: ForgeDirection) = side == ForgeDirection.DOWN
override def bounds(world: IBlockAccess, x: Int, y: Int, z: Int) =
AxisAlignedBB.getAABBPool.getAABB(0, 0, 0, 1, 0.2f, 1)
AxisAlignedBB.getAABBPool.getAABB(0, 0, 0, 1, 3 / 16f, 1)
override def itemBounds() {
parent.setBlockBounds(AxisAlignedBB.getAABBPool.getAABB(0, 0, 0, 1, 3 / 16f, 1))
}
override def registerIcons(iconRegister: IconRegister) = {
icons(ForgeDirection.DOWN.ordinal) = iconRegister.registerIcon(Settings.resourceDomain + ":generic_top")
icons(ForgeDirection.UP.ordinal) = iconRegister.registerIcon(Settings.resourceDomain + ":hologram_top")
icons(ForgeDirection.NORTH.ordinal) = iconRegister.registerIcon(Settings.resourceDomain + ":hologram_side")
icons(ForgeDirection.SOUTH.ordinal) = icons(ForgeDirection.NORTH.ordinal)
icons(ForgeDirection.WEST.ordinal) = icons(ForgeDirection.NORTH.ordinal)
icons(ForgeDirection.EAST.ordinal) = icons(ForgeDirection.NORTH.ordinal)
}
// ----------------------------------------------------------------------- //

View File

@ -34,15 +34,22 @@ class Hologram extends Environment with SidedEnvironment {
// ----------------------------------------------------------------------- //
@Callback(doc = """function() -- Clears the hologram.""")
def clear(computer: Context, args: Arguments): Array[AnyRef] = this.synchronized {
for (i <- 0 until volume.length) volume(i) = 0
dirty = true
null
}
@Callback(direct = true, doc = """function(x:number, z:number):number -- Returns the bit mask representing the specified column.""")
def get(computer: Context, args: Arguments): Array[AnyRef] = {
def get(computer: Context, args: Arguments): Array[AnyRef] = this.synchronized {
val x = args.checkInteger(0) - 1
val z = args.checkInteger(1) - 1
result(volume(x + z * width))
}
@Callback(direct = true, limit = 256, doc = """function(x:number, z:number, value:number) -- Set the bit mask for the specified column.""")
def set(computer: Context, args: Arguments): Array[AnyRef] = {
def set(computer: Context, args: Arguments): Array[AnyRef] = this.synchronized {
val x = args.checkInteger(0) - 1
val z = args.checkInteger(1) - 1
val value = args.checkInteger(2)
@ -51,6 +58,18 @@ class Hologram extends Environment with SidedEnvironment {
null
}
@Callback(direct = true, limit = 128, doc = """function(x:number, z:number, height:number) -- Fills a column to the specified height.""")
def fill(computer: Context, args: Arguments): Array[AnyRef] = this.synchronized {
val x = args.checkInteger(0) - 1
val z = args.checkInteger(1) - 1
val height = math.min(32, math.max(0, args.checkInteger(2)))
// Bit shifts in the JVM only use the lowest five bits... so we have to
// manually check the height, to avoid the shift being a no-op.
volume(x + z * width) = if (height > 0) 0xFFFFFFFF >>> (32 - height) else 0
dirty = true
null
}
// ----------------------------------------------------------------------- //
override def updateEntity() {
@ -67,14 +86,18 @@ class Hologram extends Environment with SidedEnvironment {
// ----------------------------------------------------------------------- //
override def getRenderBoundingBox = AxisAlignedBB.getAABBPool.getAABB(xCoord - 1, yCoord, zCoord - 1, xCoord + 2, yCoord + 3, zCoord + 2)
override def shouldRenderInPass(pass: Int) = pass == 1
override def getRenderBoundingBox = AxisAlignedBB.getAABBPool.getAABB(xCoord - 1, yCoord, zCoord - 1, xCoord + 2, yCoord + 2.25, zCoord + 2)
// ----------------------------------------------------------------------- //
override def readFromNBT(nbt: NBTTagCompound) {
super.readFromNBT(nbt)
nbt.getIntArray(Settings.namespace + "volume").copyToArray(volume)
}
override def writeToNBT(nbt: NBTTagCompound) {
override def writeToNBT(nbt: NBTTagCompound) = this.synchronized {
super.writeToNBT(nbt)
nbt.setIntArray(Settings.namespace + "volume", volume)
}

View File

@ -12,6 +12,7 @@ oc:tile.Case2.name=Computergehäuse (Stufe 3)
oc:tile.Charger.name=Ladestation
oc:tile.DiskDrive.name=Diskettenlaufwerk
oc:tile.Keyboard.name=Tastatur
oc:tile.Hologram.name=Hologrammprojektor
oc:tile.PowerConverter.name=Leistungswandler
oc:tile.PowerDistributor.name=Stromverteiler
oc:tile.Redstone.name=Redstone-I/O
@ -136,6 +137,7 @@ oc:tooltip.GraphicsCard=Erlaubt es, den angezeigten Inhalt von Bildschirmen zu
oc:tooltip.InternetCard=Diese Karte erlaubt es, HTTP-Anfragen zu senden und echte TCP Sockets zu verwenden.
oc:tooltip.IronNugget=Ein Nugget, das aus Eisen besteht, darum wird es ja auch Eisennugget genannt, duh...
oc:tooltip.Keyboard=Kann an Bildschirmen befestigt werden, um auf ihnen zu tippen.
oc:tooltip.Hologram=Ein Volumendisplay das beliebige, von Computern festgelegte Voxelstrukturen anzeigt.[nl] Auflösung: §f48x32x48§7. [nl] Farbtiefe: §fMonochrom§7.
oc:tooltip.Memory=Braucht ein jeder Computer, um zu starten. Je mehr vorhanden, desto komplexere Programme können ausgeführt werden.
oc:tooltip.Microchip=Tritt auch unter dem Alias Integrierter Schaltkreis auf. Keine Ahnung, warum das auch mit Redstone klappt, aber es funktioniert.
oc:tooltip.NetworkCard=Erlaubt es Computern, die über mehrere Blöcke miteinander verbunden sind (z.B. Kabel), mittels Netzwerknachrichten zu kommunizieren.

View File

@ -12,6 +12,7 @@ oc:tile.Case2.name=Computer Case (Tier 3)
oc:tile.Charger.name=Charger
oc:tile.DiskDrive.name=Disk Drive
oc:tile.Keyboard.name=Keyboard
oc:tile.Hologram.name=Hologram Projector
oc:tile.PowerConverter.name=Power Converter
oc:tile.PowerDistributor.name=Power Distributor
oc:tile.Redstone.name=Redstone I/O
@ -136,6 +137,7 @@ oc:tooltip.GraphicsCard=Used to change what's displayed on screens.[nl] Maximum
oc:tooltip.InternetCard=This card allows making HTTP requests and using real TCP sockets.
oc:tooltip.IronNugget=A nugget made of iron, that's why it's called an Iron Nugget, duh...
oc:tooltip.Keyboard=Can be attached to screens to allow typing on them.
oc:tooltip.Hologram=A volumetric display that can be controlled by computers to display arbitrary voxel structures.[nl] Resolution: §f48x32x48§7. [nl] Color depth: §fMonochrome§7.
oc:tooltip.Memory=Required to get computers to run. The more you have, the more complex the programs you can run.
oc:tooltip.Microchip=The chip formerly known as Integrated Circuit. I have no idea why this works with redstone, but it does.
oc:tooltip.NetworkCard=Allows distant computers connected by other blocks (such as cable) to communicate by sending messages to each other.

View File

@ -1,5 +1,5 @@
local hookInterval = 100
local deadline = 0
local deadline = math.huge
local function checkDeadline()
if computer.realTime() > deadline then
debug.sethook(coroutine.running(), checkDeadline, "", 1)
@ -91,7 +91,11 @@ sandbox = {
loadfile = nil, -- in boot/*_base.lua
next = next,
pairs = pairs,
pcall = pcall,
pcall = function(...)
local result = table.pack(pcall(...))
checkDeadline()
return table.unpack(result, 1, result.n)
end,
print = nil, -- in boot/*_base.lua
rawequal = rawequal,
rawget = rawget,
@ -103,7 +107,11 @@ sandbox = {
tostring = tostring,
type = type,
_VERSION = "Lua 5.2",
xpcall = xpcall,
xpcall = function(...)
local result = table.pack(xpcall(...))
checkDeadline()
return table.unpack(result, 1, result.n)
end,
coroutine = {
create = function(f)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 B

After

Width:  |  Height:  |  Size: 149 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B