blob: 348d02cb36c5160d7fd40279cce00f4de55d64f3 [file] [log] [blame] [raw]
package li.cil.oc.common
import java.io.ByteArrayOutputStream
import java.io.DataOutputStream
import java.io.OutputStream
import java.util.zip.Deflater
import cpw.mods.fml.common.FMLCommonHandler
import cpw.mods.fml.common.network.internal.FMLProxyPacket
import io.netty.buffer.Unpooled
import li.cil.oc.OpenComputers
import li.cil.oc.api.network.EnvironmentHost
import net.minecraft.entity.Entity
import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.item.ItemStack
import net.minecraft.nbt.CompressedStreamTools
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.tileentity.TileEntity
import net.minecraft.world.World
import net.minecraftforge.common.util.ForgeDirection
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream
import org.apache.commons.compress.compressors.gzip.GzipParameters
import org.apache.logging.log4j.LogManager
import scala.collection.convert.WrapAsScala._
abstract class PacketBuilder(stream: OutputStream) extends DataOutputStream(stream) {
def writeTileEntity(t: TileEntity) {
writeInt(t.getWorldObj.provider.dimensionId)
writeInt(t.xCoord)
writeInt(t.yCoord)
writeInt(t.zCoord)
}
def writeEntity(e: Entity) {
writeInt(e.worldObj.provider.dimensionId)
writeInt(e.getEntityId)
}
def writeDirection(d: Option[ForgeDirection]) = d match {
case Some(side) => writeByte(side.ordinal.toByte)
case _ => writeByte(-1: Byte)
}
def writeItemStack(stack: ItemStack) = {
val haveStack = stack != null && stack.stackSize > 0
writeBoolean(haveStack)
if (haveStack) {
writeNBT(stack.writeToNBT(new NBTTagCompound()))
}
}
def writeNBT(nbt: NBTTagCompound) = CompressedStreamTools.write(nbt, this)
def writePacketType(pt: PacketType.Value) = writeByte(pt.id)
def sendToAllPlayers() = OpenComputers.channel.sendToAll(packet)
def sendToPlayersNearEntity(e: Entity, range: Option[Double] = None): Unit = sendToNearbyPlayers(e.worldObj, e.posX, e.posY, e.posZ, range)
def sendToPlayersNearTileEntity(t: TileEntity, range: Option[Double] = None): Unit = sendToNearbyPlayers(t.getWorldObj, t.xCoord + 0.5, t.yCoord + 0.5, t.zCoord + 0.5, range)
def sendToPlayersNearHost(host: EnvironmentHost, range: Option[Double] = None): Unit = sendToNearbyPlayers(host.world, host.xPosition, host.yPosition, host.zPosition, range)
def sendToNearbyPlayers(world: World, x: Double, y: Double, z: Double, range: Option[Double]) {
val dimension = world.provider.dimensionId
val server = FMLCommonHandler.instance.getMinecraftServerInstance
val manager = server.getConfigurationManager
for (player <- manager.playerEntityList.map(_.asInstanceOf[EntityPlayerMP]) if player.dimension == dimension) {
val playerRenderDistance = 16 // ObfuscationReflectionHelper.getPrivateValue(classOf[EntityPlayerMP], player, "renderDistance").asInstanceOf[Integer]
val playerSpecificRange = range.getOrElse((manager.getViewDistance min playerRenderDistance) * 16.0)
if (player.getDistanceSq(x, y, z) < playerSpecificRange * playerSpecificRange) {
sendToPlayer(player)
}
}
}
def sendToPlayer(player: EntityPlayerMP) = OpenComputers.channel.sendTo(packet, player)
def sendToServer() = OpenComputers.channel.sendToServer(packet)
protected def packet: FMLProxyPacket
}
// Necessary to keep track of the GZIP stream.
abstract class PacketBuilderBase[T <: OutputStream](protected val stream: T) extends PacketBuilder(stream) {
var tileEntity: Option[TileEntity] = None
override def writeTileEntity(t: TileEntity): Unit = {
super.writeTileEntity(t)
if (PacketBuilder.isProfilingEnabled) {
tileEntity = Option(t)
}
}
}
class SimplePacketBuilder(val packetType: PacketType.Value) extends PacketBuilderBase(PacketBuilder.newData(compressed = false)) {
writeByte(packetType.id)
override protected def packet = {
val payload = stream.toByteArray
PacketBuilder.logPacket(packetType, payload.length, tileEntity)
new FMLProxyPacket(Unpooled.wrappedBuffer(payload), "OpenComputers")
}
}
class CompressedPacketBuilder(val packetType: PacketType.Value, private val data: ByteArrayOutputStream = PacketBuilder.newData(compressed = true)) extends PacketBuilderBase(new GzipCompressorOutputStream(data, PacketBuilder.gzipParams)) {
writeByte(packetType.id)
override protected def packet = {
stream.finish()
val payload = data.toByteArray
PacketBuilder.logPacket(packetType, payload.length, tileEntity)
new FMLProxyPacket(Unpooled.wrappedBuffer(payload), "OpenComputers")
}
}
object PacketBuilder {
val log = LogManager.getLogger(OpenComputers.Name + "-PacketBuilder")
var isProfilingEnabled = false
val gzipParams = new GzipParameters()
gzipParams.setCompressionLevel(Deflater.BEST_SPEED)
def logPacket(packetType: PacketType.Value, payloadSize: Int, tileEntity: Option[TileEntity]): Unit = {
if (PacketBuilder.isProfilingEnabled) {
tileEntity match {
case Some(t) => PacketBuilder.log.info(s"Sending: $packetType @ $payloadSize bytes from (${t.xCoord}, ${t.yCoord}, ${t.zCoord}).")
case _ => PacketBuilder.log.info(s"Sending: $packetType @ $payloadSize bytes.")
}
}
}
def newData(compressed: Boolean) = {
val data = new ByteArrayOutputStream
data.write(if (compressed) 1 else 0)
data
}
}