blob: cff654487cd488026a8ffad3eff3b78abe39d2da [file] [log] [blame] [raw]
package li.cil.oc.common
import java.io.DataInputStream
import java.io.InputStream
import java.util.zip.GZIPInputStream
import io.netty.buffer.ByteBuf
import io.netty.buffer.ByteBufInputStream
import li.cil.oc.OpenComputers
import li.cil.oc.api
import li.cil.oc.common.block.RobotAfterimage
import li.cil.oc.util.ItemUtils
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.nbt.CompressedStreamTools
import net.minecraft.world.World
import net.minecraftforge.common.util.ForgeDirection
import scala.reflect.ClassTag
import scala.reflect.classTag
abstract class PacketHandler {
/** Top level dispatcher based on packet type. */
protected def onPacketData(data: ByteBuf, player: EntityPlayer) {
// Don't crash on badly formatted packets (may have been altered by a
// malicious client, in which case we don't want to allow it to kill the
// server like this). Just spam the log a bit... ;)
try {
val stream = new ByteBufInputStream(data)
if (stream.read() == 0) dispatch(new PacketParser(stream, player))
else dispatch(new PacketParser(new GZIPInputStream(stream), player))
} catch {
case e: Throwable =>
OpenComputers.log.warn("Received a badly formatted packet.", e)
}
}
/**
* Gets the world for the specified dimension.
*
* For clients this returns the client's world if it is the specified
* dimension; None otherwise. For the server it returns the world for the
* specified dimension, if such a dimension exists; None otherwise.
*/
protected def world(player: EntityPlayer, dimension: Int): Option[World]
protected def dispatch(p: PacketParser)
protected class PacketParser(stream: InputStream, val player: EntityPlayer) extends DataInputStream(stream) {
val packetType = PacketType(readByte())
def getTileEntity[T: ClassTag](dimension: Int, x: Int, y: Int, z: Int): Option[T] = {
world(player, dimension) match {
case Some(world) if world.blockExists(x, y, z) =>
val t = world.getTileEntity(x, y, z)
if (t != null && classTag[T].runtimeClass.isAssignableFrom(t.getClass)) {
return Some(t.asInstanceOf[T])
}
// In case a robot moved away before the packet arrived. This is
// mostly used when the robot *starts* moving while the client sends
// a request to the server.
api.Items.get("robotAfterimage").block match {
case afterimage: RobotAfterimage => afterimage.findMovingRobot(world, x, y, z) match {
case Some(robot) if classTag[T].runtimeClass.isAssignableFrom(robot.proxy.getClass) =>
return Some(robot.proxy.asInstanceOf[T])
case _ =>
}
case _ =>
}
case _ => // Invalid dimension.
}
None
}
def getEntity[T: ClassTag](dimension: Int, id: Int): Option[T] = {
world(player, dimension) match {
case Some(world) =>
val e = world.getEntityByID(id)
if (e != null && classTag[T].runtimeClass.isAssignableFrom(e.getClass)) {
return Some(e.asInstanceOf[T])
}
case _ =>
}
None
}
def readTileEntity[T: ClassTag](): Option[T] = {
val dimension = readInt()
val x = readInt()
val y = readInt()
val z = readInt()
getTileEntity(dimension, x, y, z)
}
def readEntity[T: ClassTag](): Option[T] = {
val dimension = readInt()
val id = readInt()
getEntity[T](dimension, id)
}
def readDirection() = readByte() match {
case id if id < 0 => None
case id => Option(ForgeDirection.getOrientation(id))
}
def readItemStack() = {
val haveStack = readBoolean()
if (haveStack) {
ItemUtils.loadStack(readNBT())
}
else null
}
def readNBT() = CompressedStreamTools.readCompressed(this)
def readPacketType() = PacketType(readByte())
}
}