mirror of
https://github.com/GTNewHorizons/ForgeMultipart.git
synced 2026-11-13 00:47:52 +00:00
212 lines
9.0 KiB
Scala
212 lines
9.0 KiB
Scala
package codechicken.multipart
|
|
|
|
import net.minecraft.tileentity.TileEntity
|
|
import scala.collection.immutable.Map
|
|
import net.minecraft.world.World
|
|
import codechicken.lib.vec.BlockCoord
|
|
import codechicken.multipart.handler.MultipartProxy
|
|
import cpw.mods.fml.common.FMLCommonHandler
|
|
import cpw.mods.fml.relauncher.Side
|
|
import codechicken.lib.packet.PacketCustom
|
|
import net.minecraft.network.packet.Packet53BlockChange
|
|
import scala.collection.mutable.ListBuffer
|
|
import scala.collection.mutable.ListBuffer
|
|
import codechicken.multipart.asm.IMultipartFactory
|
|
import codechicken.multipart.asm.ASMMixinFactory
|
|
|
|
/**
|
|
* This class manages the dynamic construction and allocation of container TileMultipart instances.
|
|
*
|
|
* Classes that extend TileMultipart, adding tile centric logic, optimisations or interfaces, can be registered to a marker interface on a part instance.
|
|
* When a part is added to the tile that implements the certain marker interface, the container tile will be replaced with a class that includes the functionality from the corresponding mixin class.
|
|
*
|
|
* Classes are generated in a similar fashion to the way scala traits are compiled. To see the output, simply enable the config option and look in the asm/multipart folder of you .minecraft directory.
|
|
*
|
|
* There are several mixin traits that come with the API included in the scalatraits package. TPartialOcclusionTile is defined as class instead of trait to give an example for Java programmers.
|
|
*/
|
|
object MultipartGenerator
|
|
{
|
|
private var tileTraitMap:Map[Class[_], Set[String]] = Map()
|
|
private var interfaceTraitMap_c:Map[String, String] = Map()
|
|
private var interfaceTraitMap_s:Map[String, String] = Map()
|
|
private var partTraitMap_c:Map[Class[_], Seq[String]] = Map()
|
|
private var partTraitMap_s:Map[Class[_], Seq[String]] = Map()
|
|
|
|
var factory:IMultipartFactory = ASMMixinFactory
|
|
|
|
def partTraitMap(client:Boolean) = if(client) partTraitMap_c else partTraitMap_s
|
|
|
|
def interfaceTraitMap(client:Boolean) = if(client) partTraitMap_c else interfaceTraitMap_s
|
|
|
|
def traitsForPart(part:TMultiPart, client:Boolean):Seq[String] =
|
|
{
|
|
var ret = partTraitMap(client).getOrElse(part.getClass, null)
|
|
if(ret == null)
|
|
{
|
|
def heirachy(clazz:Class[_]):Seq[Class[_]] =
|
|
{
|
|
var superClasses:Seq[Class[_]] = clazz.getInterfaces.flatMap(c => heirachy(c)):+clazz
|
|
if(clazz.getSuperclass != null)
|
|
superClasses = superClasses++heirachy(clazz.getSuperclass)
|
|
return superClasses
|
|
}
|
|
|
|
val interfaceTraitMap = if(client) interfaceTraitMap_c else interfaceTraitMap_s
|
|
ret = heirachy(part.getClass).flatMap(c => interfaceTraitMap.get(c.getName)).distinct
|
|
if(client)
|
|
partTraitMap_c = partTraitMap_c+(part.getClass -> ret)
|
|
else
|
|
partTraitMap_s = partTraitMap_s+(part.getClass -> ret)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
/**
|
|
* Check if part adds any new interfaces to tile, if so, replace tile with a new copy and call tile.addPart(part)
|
|
* returns true if tile was replaced
|
|
*/
|
|
private[multipart] def addPart(world:World, pos:BlockCoord, part:TMultiPart):TileMultipart =
|
|
{
|
|
val (tile, converted) = TileMultipart.getOrConvertTile2(world, pos)
|
|
var partTraits = traitsForPart(part, world.isRemote)
|
|
var ntile = tile
|
|
if(ntile != null)
|
|
{
|
|
if(converted)//perform client conversion
|
|
{
|
|
ntile.partList(0).invalidateConvertedTile()
|
|
world.setBlock(pos.x, pos.y, pos.z, MultipartProxy.block.blockID, 0, 0)
|
|
world.setBlockTileEntity(pos.x, pos.y, pos.z, ntile)
|
|
PacketCustom.sendToChunk(new Packet53BlockChange(pos.x, pos.y, pos.z, world), world, pos.x>>4, pos.z>>4)
|
|
ntile.partList(0).onConverted()
|
|
ntile.writeAddPart(ntile.partList(0))
|
|
}
|
|
|
|
val tileTraits = tileTraitMap(tile.getClass)
|
|
partTraits = partTraits.filter(!tileTraits(_))
|
|
if(!partTraits.isEmpty)
|
|
{
|
|
ntile = factory.generateTile(partTraits++tileTraits, world.isRemote)
|
|
tile.setValid(false)
|
|
world.setBlockTileEntity(pos.x, pos.y, pos.z, ntile)
|
|
ntile.from(tile)
|
|
}
|
|
}
|
|
else
|
|
{
|
|
world.setBlock(pos.x, pos.y, pos.z, MultipartProxy.block.blockID, 0, 0)
|
|
ntile = factory.generateTile(partTraits, world.isRemote)
|
|
world.setBlockTileEntity(pos.x, pos.y, pos.z, ntile)
|
|
}
|
|
ntile.addPart_impl(part)
|
|
return ntile
|
|
}
|
|
|
|
/**
|
|
* Check if tile satisfies all the interfaces required by parts. If not, return a new generated copy of tile
|
|
*/
|
|
private[multipart] def generateCompositeTile(tile:TileEntity, parts:Seq[TMultiPart], client:Boolean):TileMultipart =
|
|
{
|
|
var partTraits = parts.flatMap(traitsForPart(_, client)).distinct
|
|
if(tile != null && tile.isInstanceOf[TileMultipart])
|
|
{
|
|
var tileTraits = tileTraitMap(tile.getClass)
|
|
if(partTraits.forall(tileTraits(_)) && partTraits.size == tileTraits.size)//equal contents
|
|
return tile.asInstanceOf[TileMultipart]
|
|
|
|
}
|
|
return factory.generateTile(partTraits, client)
|
|
}
|
|
|
|
/**
|
|
* Check if there are any redundant interfaces on tile, if so, replace tile with new copy
|
|
*/
|
|
private[multipart] def partRemoved(tile:TileMultipart, part:TMultiPart):TileMultipart =
|
|
{
|
|
val client = tile.worldObj.isRemote
|
|
var partTraits = tile.partList.flatMap(traitsForPart(_, client))
|
|
var testSet = partTraits.toSet
|
|
if(!traitsForPart(part, client).forall(testSet(_)))
|
|
{
|
|
val ntile = factory.generateTile(testSet.toSeq, client)
|
|
tile.setValid(false)
|
|
tile.worldObj.setBlockTileEntity(tile.xCoord, tile.yCoord, tile.zCoord, ntile)
|
|
ntile.from(tile)
|
|
return ntile
|
|
}
|
|
return tile
|
|
}
|
|
|
|
/**
|
|
* register s_trait to be applied to tiles containing parts implementing s_interface
|
|
*/
|
|
def registerTrait(s_interface:String, s_trait:String):Unit = registerTrait(s_interface, s_trait, s_trait)
|
|
|
|
/**
|
|
* register traits to be applied to tiles containing parts implementing s_interface
|
|
* s_trait for server worlds (may be null)
|
|
* c_trait for client worlds (may be null)
|
|
*/
|
|
def registerTrait(s_interface:String, c_trait:String, s_trait:String)
|
|
{
|
|
if(c_trait != null)
|
|
{
|
|
if(interfaceTraitMap_c.contains(s_interface))
|
|
System.err.println("Trait already registered for "+s_interface)
|
|
else
|
|
{
|
|
interfaceTraitMap_c = interfaceTraitMap_c+(s_interface->c_trait)
|
|
factory.registerTrait(s_interface, c_trait, true)
|
|
}
|
|
}
|
|
if(s_trait != null)
|
|
{
|
|
if(interfaceTraitMap_s.contains(s_interface))
|
|
System.err.println("Trait already registered for "+s_interface)
|
|
else
|
|
{
|
|
interfaceTraitMap_s = interfaceTraitMap_s+(s_interface->s_trait)
|
|
factory.registerTrait(s_interface, s_trait, false)
|
|
}
|
|
}
|
|
}
|
|
|
|
def registerPassThroughInterface(s_interface:String):Unit = registerPassThroughInterface(s_interface, true, true)
|
|
|
|
/**
|
|
* A passthrough interface, is an interface to be implemented on the container tile instance, for which all calls are passed directly to the single implementing part.
|
|
* Registering a passthrough interface is equivalent to defining a mixin class as follows.
|
|
* 1. field 'impl' which contains the reference to the corresponding part
|
|
* 2. occlusionTest is overriden to prevent more than one part with s_interface existing in the block space
|
|
* 3. implementing s_interface and passing all calls directly to the part instance.
|
|
*
|
|
* This allows compatibility with APIs that expect interfaces on the tile entity.
|
|
*/
|
|
def registerPassThroughInterface(s_interface:String, client:Boolean, server:Boolean)
|
|
{
|
|
val tType = factory.generatePassThroughTrait(s_interface)
|
|
if(tType == null)
|
|
return
|
|
|
|
if(client)
|
|
{
|
|
if(interfaceTraitMap_c.contains(s_interface))
|
|
System.err.println("Trait already registered for "+s_interface)
|
|
else
|
|
interfaceTraitMap_c = interfaceTraitMap_c+(s_interface->tType)
|
|
}
|
|
if(server)
|
|
{
|
|
if(interfaceTraitMap_s.contains(s_interface))
|
|
System.err.println("Trait already registered for "+s_interface)
|
|
else
|
|
interfaceTraitMap_s = interfaceTraitMap_s+(s_interface->tType)
|
|
}
|
|
}
|
|
|
|
private[multipart] def registerTileClass(clazz:Class[_ <: TileEntity], traits:Set[String])
|
|
{
|
|
tileTraitMap=tileTraitMap+(clazz->traits)
|
|
MultipartProxy.onTileClassBuilt(clazz)
|
|
}
|
|
} |