diff --git a/assets/opencomputers/lang/de_DE.lang b/assets/opencomputers/lang/de_DE.lang index fb472408f..4facf3947 100644 --- a/assets/opencomputers/lang/de_DE.lang +++ b/assets/opencomputers/lang/de_DE.lang @@ -23,6 +23,7 @@ oc:block.Screen1.name=Hochwertiger Bildschirm oc:block.Screen2.name=Ausgezeichneter Bildschirm # Items +oc:item.AbstractBusCard.name=Abstrakter-Bus-Karte oc:item.Acid.name=Grog oc:item.ALU.name=Arithmetisch-logische Einheit (ALU) oc:item.Analyzer.name=Messgerät @@ -77,6 +78,7 @@ oc:container.Case=Computer oc:container.DiskDrive=Diskettenlaufwerk # Item / Block Tooltips +oc:tooltip.AbstractBusCard=Erlaubt es LIP-Pakete des Abstrakten Busses von §fStargateTech 2§7 zu senden und zu empfangen. oc:tooltip.Acid=Eine hochgiftige Möchtegernflüssigkeit, wird üblicherweise nur von gewissen Piraten konsumiert. Dank ihrer korrosiven Eigenschaften ideal zum Bedrucken von Leiterplatten geeignet. oc:tooltip.Adapter=Erlaubt es Blöcke anzusteuern, die keine Komponentenblöcke sind, wie etwa reguläre Minecraft-Blöcke oder Blöcke anderer Mods. oc:tooltip.ALU=Zählt Zahlen zum Zeitvertreib. Klingt komisch, is aber so. diff --git a/assets/opencomputers/lang/en_US.lang b/assets/opencomputers/lang/en_US.lang index 1e8933334..d9e40efee 100644 --- a/assets/opencomputers/lang/en_US.lang +++ b/assets/opencomputers/lang/en_US.lang @@ -23,6 +23,7 @@ oc:block.Screen1.name=Advanced Screen oc:block.Screen2.name=Superior Screen # Items +oc:item.AbstractBusCard.name=Abstract Bus Card oc:item.Acid.name=Grog oc:item.ALU.name=Arithmetic Logic Unit (ALU) oc:item.Analyzer.name=Analyzer @@ -77,6 +78,7 @@ oc:container.Case=Computer oc:container.DiskDrive=Disk Drive # Item / Block Tooltips +oc:tooltip.AbstractBusCard=Allows interacting with §fStargateTech 2§7's abstract bus by sending and receiving LIP packets. oc:tooltip.Acid=A highly toxic pseudo-liquid, usually only consumed by certain pirates. Thanks to its corrosive nature it is perfectly suited for etching circuit boards. oc:tooltip.Adapter=Used to control non-component blocks, such as vanilla blocks or blocks from other mods. oc:tooltip.ALU=Adds number so you don't have to. It might be better this way. diff --git a/assets/opencomputers/recipes/default.recipes b/assets/opencomputers/recipes/default.recipes index 2027fadc4..844f59760 100644 --- a/assets/opencomputers/recipes/default.recipes +++ b/assets/opencomputers/recipes/default.recipes @@ -51,6 +51,10 @@ hdd3 { ["oc:circuitElite", "oc:craftingHDDAdvanced", "oc:circuitElite"]] } +abstractBusCard { + input: [[busCable, {item=naquadah, subID=8}, ""] + ["", "oc:craftingCard", ""]] +} graphicsCard1 { input: [["oc:circuitBasic", "oc:craftingALU", "oc:craftingRAMBasic"] ["", "oc:craftingCard", ""]] diff --git a/assets/opencomputers/textures/items/card_abstract_bus.png b/assets/opencomputers/textures/items/card_abstract_bus.png new file mode 100644 index 000000000..6bbe35504 Binary files /dev/null and b/assets/opencomputers/textures/items/card_abstract_bus.png differ diff --git a/li/cil/oc/Items.scala b/li/cil/oc/Items.scala index 4a0c7efe6..4325f9290 100644 --- a/li/cil/oc/Items.scala +++ b/li/cil/oc/Items.scala @@ -7,57 +7,58 @@ import net.minecraft.item.{Item, ItemStack} import net.minecraftforge.oredict.OreDictionary object Items { - var multi: item.Delegator = null + var multi: item.Delegator = _ // ----------------------------------------------------------------------- // // Tools - var analyzer: item.Analyzer = null + var analyzer: item.Analyzer = _ // ----------------------------------------------------------------------- // // Memory - var ram1, ram2, ram3, ram4, ram5: item.Memory = null + var ram1, ram2, ram3, ram4, ram5: item.Memory = _ // ----------------------------------------------------------------------- // // Storage - var floppyDisk: item.FloppyDisk = null - var hdd1, hdd2, hdd3: item.HardDiskDrive = null + var floppyDisk: item.FloppyDisk = _ + var hdd1, hdd2, hdd3: item.HardDiskDrive = _ // ----------------------------------------------------------------------- // // Cards - var gpu1, gpu2, gpu3: item.GraphicsCard = null - var lan: item.NetworkCard = null - var rs: item.RedstoneCard = null - var wlan: item.WirelessNetworkCard = null + var abstractBus: item.AbstractBusCard = _ + var gpu1, gpu2, gpu3: item.GraphicsCard = _ + var lan: item.NetworkCard = _ + var rs: item.RedstoneCard = _ + var wlan: item.WirelessNetworkCard = _ // ----------------------------------------------------------------------- // // Upgrades - var upgradeCrafting: item.UpgradeCrafting = null - var upgradeGenerator: item.UpgradeGenerator = null - var upgradeNavigation: item.UpgradeNavigation = null - var upgradeSign: item.UpgradeSign = null - var upgradeSolarGenerator: item.UpgradeSolarGenerator = null + var upgradeCrafting: item.UpgradeCrafting = _ + var upgradeGenerator: item.UpgradeGenerator = _ + var upgradeNavigation: item.UpgradeNavigation = _ + var upgradeSign: item.UpgradeSign = _ + var upgradeSolarGenerator: item.UpgradeSolarGenerator = _ // ----------------------------------------------------------------------- // // Crafting - var ironNugget: item.IronNugget = null - var cuttingWire: item.CuttingWire = null - var acid: item.Acid = null - var disk: item.Disk = null + var ironNugget: item.IronNugget = _ + var cuttingWire: item.CuttingWire = _ + var acid: item.Acid = _ + var disk: item.Disk = _ - var buttonGroup: item.ButtonGroup = null - var arrowKeys: item.ArrowKeys = null - var numPad: item.NumPad = null + var buttonGroup: item.ButtonGroup = _ + var arrowKeys: item.ArrowKeys = _ + var numPad: item.NumPad = _ - var transistor: item.Transistor = null - var chip1, chip2, chip3: item.Microchip = null - var alu: item.ALU = null - var cpu: item.CPU = null - var cu: item.ControlUnit = null + var transistor: item.Transistor = _ + var chip1, chip2, chip3: item.Microchip = _ + var alu: item.ALU = _ + var cpu: item.CPU = _ + var cu: item.ControlUnit = _ - var rawCircuitBoard: item.RawCircuitBoard = null - var circuitBoard: item.CircuitBoard = null - var pcb: item.PrintedCircuitBoard = null - var card: item.CardBase = null + var rawCircuitBoard: item.RawCircuitBoard = _ + var circuitBoard: item.CircuitBoard = _ + var pcb: item.PrintedCircuitBoard = _ + var card: item.CardBase = _ def init() { multi = new item.Delegator(Settings.get.itemId) @@ -115,6 +116,8 @@ object Items { ram4 = new item.Memory(multi, 3) ram5 = new item.Memory(multi, 4) + abstractBus = new item.AbstractBusCard(multi) + // ----------------------------------------------------------------------- // registerExclusive("craftingPiston", new ItemStack(Block.pistonBase), new ItemStack(Block.pistonStickyBase)) diff --git a/li/cil/oc/OpenComputersCore.scala b/li/cil/oc/OpenComputersCore.scala new file mode 100644 index 000000000..0ade59b0a --- /dev/null +++ b/li/cil/oc/OpenComputersCore.scala @@ -0,0 +1,8 @@ +package li.cil.oc + +import cpw.mods.fml.common.Mod + +// This empty mod is used to avoid cyclic dependencies with mods we depend on +// for optional interfaces, for example, but that also depend on our API. +@Mod(modid = "OpenComputers|Core", name = "OpenComputers (Core)", version = "1.0.0", modLanguage = "scala") +object OpenComputersCore diff --git a/li/cil/oc/Recipes.scala b/li/cil/oc/Recipes.scala index 7cffe689f..bad78cceb 100644 --- a/li/cil/oc/Recipes.scala +++ b/li/cil/oc/Recipes.scala @@ -64,6 +64,7 @@ object Recipes { addRecipe(Items.hdd2.createItemStack(), recipes, "hdd2") addRecipe(Items.hdd3.createItemStack(), recipes, "hdd3") + addRecipe(Items.abstractBus.createItemStack(), recipes, "abstractBusCard") addRecipe(Items.gpu1.createItemStack(), recipes, "graphicsCard1") addRecipe(Items.gpu2.createItemStack(), recipes, "graphicsCard2") addRecipe(Items.gpu3.createItemStack(), recipes, "graphicsCard3") diff --git a/li/cil/oc/Settings.scala b/li/cil/oc/Settings.scala index 53a487610..145df6699 100644 --- a/li/cil/oc/Settings.scala +++ b/li/cil/oc/Settings.scala @@ -122,6 +122,7 @@ class Settings(config: Config) { val robotMoveCost = config.getDouble("power.cost.robotMove") max 0 val robotExhaustionCost = config.getDouble("power.cost.robotExhaustion") max 0 val wirelessCostPerRange = config.getDouble("power.cost.wirelessStrength") max 0 + val abstractBusPacketCost = config.getDouble("power.cost.abstractBusPacket") max 0 // ----------------------------------------------------------------------- // // filesystem diff --git a/li/cil/oc/api/package-info.java b/li/cil/oc/api/package-info.java index 80f4792b8..d3b693586 100644 --- a/li/cil/oc/api/package-info.java +++ b/li/cil/oc/api/package-info.java @@ -35,7 +35,7 @@ * */ @cpw.mods.fml.common.API( - owner = "OpenComputers", + owner = "OpenComputers|Core", provides = "OpenComputersAPI", apiVersion = "1.2.0") package li.cil.oc.api; \ No newline at end of file diff --git a/li/cil/oc/client/PacketHandler.scala b/li/cil/oc/client/PacketHandler.scala index 4f2ce836c..9e6637a47 100644 --- a/li/cil/oc/client/PacketHandler.scala +++ b/li/cil/oc/client/PacketHandler.scala @@ -23,6 +23,7 @@ class PacketHandler extends CommonPacketHandler { override def dispatch(p: PacketParser) = p.packetType match { + case PacketType.AbstractBusState => onAbstractBusState(p) case PacketType.Analyze => onAnalyze(p) case PacketType.ChargerState => onChargerState(p) case PacketType.ComputerState => onComputerState(p) @@ -47,6 +48,12 @@ class PacketHandler extends CommonPacketHandler { case _ => // Invalid packet. } + def onAbstractBusState(p: PacketParser) = + p.readTileEntity[AbstractBusAware]() match { + case Some(t) => t.isAbstractBusAvailable = p.readBoolean() + case _ => // Invalid packet. + } + def onAnalyze(p: PacketParser) { val player = p.player.asInstanceOf[EntityPlayer] val stats = p.readNBT().asInstanceOf[NBTTagCompound] diff --git a/li/cil/oc/common/PacketType.scala b/li/cil/oc/common/PacketType.scala index c8a8a14c3..e121e80d2 100644 --- a/li/cil/oc/common/PacketType.scala +++ b/li/cil/oc/common/PacketType.scala @@ -3,6 +3,7 @@ package li.cil.oc.common object PacketType extends Enumeration { val // Server -> Client + AbstractBusState, Analyze, ChargerState, ComputerState, diff --git a/li/cil/oc/common/Proxy.scala b/li/cil/oc/common/Proxy.scala index f6a57a9e6..c24f4741e 100644 --- a/li/cil/oc/common/Proxy.scala +++ b/li/cil/oc/common/Proxy.scala @@ -28,16 +28,17 @@ class Proxy { api.Driver.add(driver.block.CommandBlock) api.Driver.add(driver.block.NoteBlock) - api.Driver.add(driver.item.UpgradeCrafting) + api.Driver.add(driver.item.AbstractBusCard) api.Driver.add(driver.item.FileSystem) - api.Driver.add(driver.item.UpgradeGenerator) - api.Driver.add(driver.item.SolarGenerator) api.Driver.add(driver.item.GraphicsCard) - api.Driver.add(driver.item.UpgradeNavigation) api.Driver.add(driver.item.Memory) api.Driver.add(driver.item.NetworkCard) - api.Driver.add(driver.item.UpgradeSign) api.Driver.add(driver.item.RedstoneCard) + api.Driver.add(driver.item.UpgradeCrafting) + api.Driver.add(driver.item.UpgradeGenerator) + api.Driver.add(driver.item.UpgradeNavigation) + api.Driver.add(driver.item.UpgradeSign) + api.Driver.add(driver.item.UpgradeSolarGenerator) api.Driver.add(driver.item.WirelessNetworkCard) Recipes.init() diff --git a/li/cil/oc/common/item/AbstractBusCard.scala b/li/cil/oc/common/item/AbstractBusCard.scala new file mode 100644 index 000000000..6d3aba644 --- /dev/null +++ b/li/cil/oc/common/item/AbstractBusCard.scala @@ -0,0 +1,26 @@ +package li.cil.oc.common.item + +import cpw.mods.fml.common.Loader +import java.util +import li.cil.oc.Settings +import li.cil.oc.util.Tooltip +import net.minecraft.client.renderer.texture.IconRegister +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.item.ItemStack + +class AbstractBusCard(val parent: Delegator) extends Delegate { + val unlocalizedName = "AbstractBusCard" + + override val showInItemList = Loader.isModLoaded("StargateTech2") + + override def tooltipLines(stack: ItemStack, player: EntityPlayer, tooltip: util.List[String], advanced: Boolean) { + tooltip.addAll(Tooltip.get(unlocalizedName)) + super.tooltipLines(stack, player, tooltip, advanced) + } + + override def registerIcons(iconRegister: IconRegister) = { + super.registerIcons(iconRegister) + + icon = iconRegister.registerIcon(Settings.resourceDomain + ":card_abstract_bus") + } +} diff --git a/li/cil/oc/common/tileentity/AbstractBusAware.scala b/li/cil/oc/common/tileentity/AbstractBusAware.scala new file mode 100644 index 000000000..88b490d2a --- /dev/null +++ b/li/cil/oc/common/tileentity/AbstractBusAware.scala @@ -0,0 +1,105 @@ +package li.cil.oc.common.tileentity + +import cpw.mods.fml.common.{Loader, Optional} +import cpw.mods.fml.relauncher.{SideOnly, Side} +import li.cil.oc.api.network.Node +import li.cil.oc.server.{PacketSender => ServerPacketSender, component} +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.common.MinecraftForge +import stargatetech2.api.StargateTechAPI +import stargatetech2.api.bus.{BusEvent, IBusDevice} + +@Optional.Interface(iface = "stargatetech2.api.bus.IBusDevice", modid = "StargateTech2") +trait AbstractBusAware extends TileEntity with ComponentInventory with IBusDevice { + def getInterfaces(side: Int) = + if (isAbstractBusAvailable) { + if (isServer) { + components collect { + case Some(abstractBus: component.AbstractBus) => abstractBus.busInterface + } + } + else fakeInterface + } + else null + + protected var _isAbstractBusAvailable = false + + private lazy val fakeInterface = Array(StargateTechAPI.api.getFactory.getIBusInterface(this, null)) + + def getWorld = world + + def getXCoord = x + + def getYCoord = y + + def getZCoord = z + + def isAbstractBusAvailable = _isAbstractBusAvailable + + def isAbstractBusAvailable_=(value: Boolean) = { + if (value != isAbstractBusAvailable) { + _isAbstractBusAvailable = value + if (isAbstractBusAvailable) addAbstractBus() + else removeAbstractBus() + world.notifyBlocksOfNeighborChange(x, y, z, block.blockID) + if (isServer) ServerPacketSender.sendAbstractBusState(this) + else world.markBlockForRenderUpdate(x, y, z) + } + this + } + + @SideOnly(Side.CLIENT) + override def readFromNBTForClient(nbt: NBTTagCompound) { + super.readFromNBTForClient(nbt) + isAbstractBusAvailable = nbt.getBoolean("isAbstractBusAvailable") + } + + override def writeToNBTForClient(nbt: NBTTagCompound) { + super.writeToNBTForClient(nbt) + nbt.setBoolean("isAbstractBusAvailable", isAbstractBusAvailable) + } + + override protected def onItemAdded(slot: Int, stack: ItemStack) { + super.onItemAdded(slot, stack) + if (isServer) { + isAbstractBusAvailable = hasAbstractBusCard + } + } + + override protected def onItemRemoved(slot: Int, stack: ItemStack) { + super.onItemRemoved(slot, stack) + if (isServer) { + isAbstractBusAvailable = hasAbstractBusCard + } + } + + abstract override def onConnect(node: Node) { + super.onConnect(node) + isAbstractBusAvailable = hasAbstractBusCard + } + + abstract override def onDisconnect(node: Node) { + super.onDisconnect(node) + removeAbstractBus() + } + + protected def addAbstractBus() { + // Mod loaded check to avoid class not found errors. + if (isServer && Loader.isModLoaded("StargateTech2")) { + MinecraftForge.EVENT_BUS.post(new BusEvent.AddToNetwork(world, x, y, z)) + } + } + + protected def removeAbstractBus() { + // Mod loaded check to avoid class not found errors. + if (isServer && Loader.isModLoaded("StargateTech2")) { + MinecraftForge.EVENT_BUS.post(new BusEvent.RemoveFromNetwork(world, x, y, z)) + } + } + + protected def hasAbstractBusCard = components exists { + case Some(_: component.AbstractBus) => true + case _ => false + } +} diff --git a/li/cil/oc/common/tileentity/Computer.scala b/li/cil/oc/common/tileentity/Computer.scala index 927213997..de4a8c4d7 100644 --- a/li/cil/oc/common/tileentity/Computer.scala +++ b/li/cil/oc/common/tileentity/Computer.scala @@ -11,7 +11,7 @@ import net.minecraftforge.common.ForgeDirection import scala.Some import scala.collection.mutable -abstract class Computer(isRemote: Boolean) extends Environment with ComponentInventory with Rotatable with BundledRedstoneAware with Analyzable with Context { +abstract class Computer(isRemote: Boolean) extends Environment with ComponentInventory with Rotatable with BundledRedstoneAware with AbstractBusAware with Analyzable with Context { protected val _computer = if (isRemote) null else new component.Computer(this) def computer = _computer diff --git a/li/cil/oc/common/tileentity/Environment.scala b/li/cil/oc/common/tileentity/Environment.scala index bd8ea125d..a86ba722a 100644 --- a/li/cil/oc/common/tileentity/Environment.scala +++ b/li/cil/oc/common/tileentity/Environment.scala @@ -6,22 +6,10 @@ import li.cil.oc.api.{Network, network} import li.cil.oc.util.ExtendedNBT._ import li.cil.oc.util.Persistable import net.minecraft.nbt.NBTTagCompound -import net.minecraft.network.INetworkManager -import net.minecraft.network.packet.Packet132TileEntityData import net.minecraftforge.common.ForgeDirection import scala.math.ScalaNumber -abstract class Environment extends net.minecraft.tileentity.TileEntity with TileEntity with network.Environment with Persistable { - def world = getWorldObj - - def x = xCoord - - def y = yCoord - - def z = zCoord - - def block = getBlockType - +abstract class Environment extends TileEntity with network.Environment with Persistable { protected var addedToNetwork = false // ----------------------------------------------------------------------- // @@ -76,18 +64,6 @@ abstract class Environment extends net.minecraft.tileentity.TileEntity with Tile // ----------------------------------------------------------------------- // - override def getDescriptionPacket = { - val nbt = new NBTTagCompound() - writeToNBTForClient(nbt) - if (nbt.hasNoTags) null else new Packet132TileEntityData(x, y, z, -1, nbt) - } - - override def onDataPacket(manager: INetworkManager, packet: Packet132TileEntityData) { - readFromNBTForClient(packet.data) - } - - // ----------------------------------------------------------------------- // - def onMessage(message: network.Message) {} def onConnect(node: network.Node) {} diff --git a/li/cil/oc/common/tileentity/Router.scala b/li/cil/oc/common/tileentity/Router.scala index 2ba7602d5..5f7b023b9 100644 --- a/li/cil/oc/common/tileentity/Router.scala +++ b/li/cil/oc/common/tileentity/Router.scala @@ -11,7 +11,7 @@ import net.minecraftforge.common.ForgeDirection import scala.collection.mutable @Optional.Interface(iface = "dan200.computer.api.IPeripheral", modid = "ComputerCraft") -class Router extends net.minecraft.tileentity.TileEntity with api.network.SidedEnvironment with IPeripheral with PassiveNode { +class Router extends TileEntity with api.network.SidedEnvironment with IPeripheral with PassiveNode { private val plugs = ForgeDirection.VALID_DIRECTIONS.map(side => new Plug(side)) // ----------------------------------------------------------------------- // diff --git a/li/cil/oc/common/tileentity/TileEntity.scala b/li/cil/oc/common/tileentity/TileEntity.scala index d894267d1..ba2538e6a 100644 --- a/li/cil/oc/common/tileentity/TileEntity.scala +++ b/li/cil/oc/common/tileentity/TileEntity.scala @@ -1,25 +1,38 @@ package li.cil.oc.common.tileentity import cpw.mods.fml.relauncher.{Side, SideOnly} -import net.minecraft.block.Block import net.minecraft.nbt.NBTTagCompound -import net.minecraft.world.World +import net.minecraft.network.INetworkManager +import net.minecraft.network.packet.Packet132TileEntityData +import net.minecraft.tileentity.{TileEntity => MCTileEntity} -trait TileEntity { - def world: World +trait TileEntity extends MCTileEntity { + def world = getWorldObj - def x: Int + def x = xCoord - def y: Int + def y = yCoord - def z: Int + def z = zCoord - def block: Block + def block = getBlockType lazy val isClient = world.isRemote lazy val isServer = !isClient + // ----------------------------------------------------------------------- // + + override def getDescriptionPacket = { + val nbt = new NBTTagCompound() + writeToNBTForClient(nbt) + if (nbt.hasNoTags) null else new Packet132TileEntityData(x, y, z, -1, nbt) + } + + override def onDataPacket(manager: INetworkManager, packet: Packet132TileEntityData) { + readFromNBTForClient(packet.data) + } + @SideOnly(Side.CLIENT) def readFromNBTForClient(nbt: NBTTagCompound) {} diff --git a/li/cil/oc/server/PacketSender.scala b/li/cil/oc/server/PacketSender.scala index 66be4fe86..6a70700f3 100644 --- a/li/cil/oc/server/PacketSender.scala +++ b/li/cil/oc/server/PacketSender.scala @@ -8,9 +8,17 @@ import li.cil.oc.util.PackedColor import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound import net.minecraftforge.common.ForgeDirection -import scala.Some object PacketSender { + def sendAbstractBusState(t: AbstractBusAware) { + val pb = new PacketBuilder(PacketType.AbstractBusState) + + pb.writeTileEntity(t) + pb.writeBoolean(t.isAbstractBusAvailable) + + pb.sendToNearbyPlayers(t) + } + def sendAnalyze(stats: NBTTagCompound, address: String, player: Player) { val pb = new PacketBuilder(PacketType.Analyze) @@ -20,28 +28,22 @@ object PacketSender { pb.sendToPlayer(player) } - def sendChargerState(t: Charger, player: Option[Player] = None) { + def sendChargerState(t: Charger) { val pb = new PacketBuilder(PacketType.ChargerState) pb.writeTileEntity(t) pb.writeDouble(t.chargeSpeed) - player match { - case Some(p) => pb.sendToPlayer(p) - case _ => pb.sendToNearbyPlayers(t) - } + pb.sendToNearbyPlayers(t) } - def sendComputerState(t: Computer, player: Option[Player] = None) { + def sendComputerState(t: Computer) { val pb = new PacketBuilder(PacketType.ComputerState) pb.writeTileEntity(t) pb.writeBoolean(t.isRunning) - player match { - case Some(p) => pb.sendToPlayer(p) - case _ => pb.sendToNearbyPlayers(t) - } + pb.sendToNearbyPlayers(t) } def sendComputerUserList(t: Computer, list: Array[String]) { @@ -54,20 +56,17 @@ object PacketSender { pb.sendToNearbyPlayers(t) } - def sendPowerState(t: PowerInformation, player: Option[Player] = None) { + def sendPowerState(t: PowerInformation) { val pb = new PacketBuilder(PacketType.PowerState) pb.writeTileEntity(t) pb.writeDouble(t.globalBuffer) pb.writeDouble(t.globalBufferSize) - player match { - case Some(p) => pb.sendToPlayer(p) - case _ => pb.sendToNearbyPlayers(t) - } + pb.sendToNearbyPlayers(t) } - def sendRedstoneState(t: RedstoneAware, player: Option[Player] = None) { + def sendRedstoneState(t: RedstoneAware) { val pb = new PacketBuilder(PacketType.RedstoneState) pb.writeTileEntity(t) @@ -76,10 +75,7 @@ object PacketSender { pb.writeByte(t.output(d)) } - player match { - case Some(p) => pb.sendToPlayer(p) - case _ => pb.sendToNearbyPlayers(t) - } + pb.sendToNearbyPlayers(t) } def sendRobotMove(t: Robot, ox: Int, oy: Int, oz: Int, direction: ForgeDirection) { @@ -150,17 +146,14 @@ object PacketSender { pb.sendToNearbyPlayers(t) } - def sendRotatableState(t: Rotatable, player: Option[Player] = None) { + def sendRotatableState(t: Rotatable) { val pb = new PacketBuilder(PacketType.RotatableState) pb.writeTileEntity(t) pb.writeDirection(t.pitch) pb.writeDirection(t.yaw) - player match { - case Some(p) => pb.sendToPlayer(p) - case _ => pb.sendToNearbyPlayers(t) - } + pb.sendToNearbyPlayers(t) } def sendScreenColorChange(t: Buffer, foreground: Int, background: Int) { diff --git a/li/cil/oc/server/component/AbstractBus.scala b/li/cil/oc/server/component/AbstractBus.scala new file mode 100644 index 000000000..d60874605 --- /dev/null +++ b/li/cil/oc/server/component/AbstractBus.scala @@ -0,0 +1,119 @@ +package li.cil.oc.server.component + +import li.cil.oc.api.network.{Arguments, Context, LuaCallback, Visibility} +import li.cil.oc.common.tileentity +import li.cil.oc.{Settings, api} +import net.minecraft.nbt.NBTTagCompound +import scala.collection.convert.WrapAsScala._ +import stargatetech2.api.StargateTechAPI +import stargatetech2.api.bus.{BusPacketLIP, BusPacket, IBusDriver, IBusInterface} + +class AbstractBus(val owner: tileentity.Computer) extends ManagedComponent with IBusDriver { + val node = api.Network.newNode(this, Visibility.Neighbors). + withComponent("abstract_bus"). + withConnector(). + create() + + val busInterface: IBusInterface = StargateTechAPI.api.getFactory.getIBusInterface(owner, this) + + protected var isEnabled = true + + protected var address = 0 + + protected var sendQueue: Option[QueuedPacket] = None + + // ----------------------------------------------------------------------- // + + def canHandlePacket(sender: Short, protocolID: Int, hasLIP: Boolean) = hasLIP + + def handlePacket(packet: BusPacket) { + val lip = packet.getPlainText + val data = Map(lip.getEntryList.map(key => (key, lip.get(key))): _*) + val metadata = Map("mod" -> lip.getMetadata.modID, "device" -> lip.getMetadata.deviceName, "player" -> lip.getMetadata.playerName) + owner.signal("bus_message", Int.box(packet.getProtocolID), Int.box(packet.getSender), Int.box(packet.getTarget), data, metadata) + } + + def getNextPacketToSend = if (sendQueue.isDefined) { + val info = sendQueue.get + sendQueue = None + val packet = new BusPacketLIP(info.sender, info.target) + for ((key, value) <- info.data) { + packet.set(key, value) + } + packet.setMetadata(new BusPacketLIP.LIPMetadata("OpenComputers", node.address, null)) + packet.finish() + packet + } + else null + + def isInterfaceEnabled = isEnabled + + def getInterfaceAddress = address.toShort + + // ----------------------------------------------------------------------- // + + @LuaCallback("getEnabled") + def getEnabled(context: Context, args: Arguments): Array[AnyRef] = result(isEnabled) + + @LuaCallback("setEnabled") + def setEnabled(context: Context, args: Arguments): Array[AnyRef] = { + isEnabled = args.checkBoolean(0) + result(isEnabled) + } + + @LuaCallback("getAddress") + def getAddress(context: Context, args: Arguments): Array[AnyRef] = result(address) + + @LuaCallback("setAddress") + def setAddress(context: Context, args: Arguments): Array[AnyRef] = { + address = args.checkInteger(0) & 0xFFFF + result(address) + } + + @LuaCallback("send") + def send(context: Context, args: Arguments): Array[AnyRef] = { + val target = args.checkInteger(0) & 0xFFFF + val data = args.checkTable(1) + if (node.tryChangeBuffer(-Settings.get.abstractBusPacketCost)) { + sendQueue = Some(new QueuedPacket(address.toShort, target.toShort, Map(data.toSeq.map(entry => (entry._1.toString, entry._2.toString)): _*))) + busInterface.sendAllPackets() + result(true) + } + else result(false, "not enough energy") + } + + @LuaCallback(value = "maxPacketSize", direct = true) + def maxPacketSize(context: Context, args: Arguments): Array[AnyRef] = result(Settings.get.maxNetworkPacketSize) + + // ----------------------------------------------------------------------- // + + override def load(nbt: NBTTagCompound) { + super.load(nbt) + busInterface.readFromNBT(nbt, "bus") + // Don't default to false. + if (nbt.hasKey("enabled")) { + isEnabled = nbt.getBoolean("enabled") + } + address = nbt.getInteger("address") & 0xFFFF + } + + override def save(nbt: NBTTagCompound) { + super.save(nbt) + busInterface.writeToNBT(nbt, "bus") + nbt.setBoolean("enabled", isEnabled) + nbt.setInteger("address", address) + } + + protected class QueuedPacket(val sender: Short, val target: Short, val data: Map[String, String]) { + // Extra braces because we don't want/have to keep size as a field. + { + val size = data.foldLeft(0)((acc, arg) => { + acc + arg._1.length + arg._2.length + }) + if (size > Settings.get.maxNetworkPacketSize) { + throw new IllegalArgumentException("packet too big (max " + Settings.get.maxNetworkPacketSize + ")") + } + } + } + +} diff --git a/li/cil/oc/server/component/Computer.scala b/li/cil/oc/server/component/Computer.scala index 9a9ee05b0..2651385c0 100644 --- a/li/cil/oc/server/component/Computer.scala +++ b/li/cil/oc/server/component/Computer.scala @@ -1018,8 +1018,8 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con lua.pushBoolean(true) lua.pushNil() lua.pushString(e.getMessage) - if (true) { - lua.pushString(e.getStackTraceString) + if (Settings.get.logLuaCallbackErrors) { + lua.pushString(e.getStackTraceString.replace("\r\n", "\n")) 4 } else 3 diff --git a/li/cil/oc/server/driver/item/AbstractBusCard.scala b/li/cil/oc/server/driver/item/AbstractBusCard.scala new file mode 100644 index 000000000..bfc9cda42 --- /dev/null +++ b/li/cil/oc/server/driver/item/AbstractBusCard.scala @@ -0,0 +1,20 @@ +package li.cil.oc.server.driver.item + +import cpw.mods.fml.common.Loader +import li.cil.oc.Items +import li.cil.oc.api.driver.Slot +import li.cil.oc.common.tileentity +import li.cil.oc.server.component +import net.minecraft.item.ItemStack +import net.minecraft.tileentity.{TileEntity => MCTileEntity} + +object AbstractBusCard extends Item { + def worksWith(stack: ItemStack) = isOneOf(stack, Items.abstractBus) + + override def createEnvironment(stack: ItemStack, container: MCTileEntity) = container match { + case computer: tileentity.Computer if Loader.isModLoaded("StargateTech2") => new component.AbstractBus(computer) + case _ => null + } + + def slot(stack: ItemStack) = Slot.Card +} diff --git a/li/cil/oc/server/driver/item/SolarGenerator.scala b/li/cil/oc/server/driver/item/UpgradeSolarGenerator.scala similarity index 91% rename from li/cil/oc/server/driver/item/SolarGenerator.scala rename to li/cil/oc/server/driver/item/UpgradeSolarGenerator.scala index 0998fe1f7..b8e89b469 100644 --- a/li/cil/oc/server/driver/item/SolarGenerator.scala +++ b/li/cil/oc/server/driver/item/UpgradeSolarGenerator.scala @@ -6,7 +6,7 @@ import li.cil.oc.server.component import net.minecraft.item.ItemStack import net.minecraft.tileentity.{TileEntity => MCTileEntity} -object SolarGenerator extends Item { +object UpgradeSolarGenerator extends Item { override def worksWith(stack: ItemStack) = isOneOf(stack, Items.upgradeSolarGenerator) override def createEnvironment(stack: ItemStack, container: MCTileEntity) = new component.UpgradeSolarGenerator(container) diff --git a/mcmod.info b/mcmod.info index dc6b83b19..1dfa3f861 100644 --- a/mcmod.info +++ b/mcmod.info @@ -4,7 +4,7 @@ "modid": "OpenComputers", "name": "OpenComputers", "description": "This mod adds modular computers and robots that can be programmed in Lua.", - "version": "1.1.1", + "version": "1.1.2", "mcversion": "1.6.4", "url": "https://github.com/MightyPirates/OpenComputers/wiki", "authors": ["Florian 'Sangar' Nücke", "Johannes 'Lord Joda' Lohrer"], @@ -18,6 +18,7 @@ "MineFactoryReloaded", "ProjRed|Transmission", "RedLogic", + "StargateTech2", "ThermalExpansion" ], "useDependencyInformation": "true" diff --git a/reference.conf b/reference.conf index 2398a10af..50c05ca5d 100644 --- a/reference.conf +++ b/reference.conf @@ -519,6 +519,9 @@ opencomputers { # the higher the cost of wireless messages. # See also: `maxWirelessRange`. wirelessStrength: 0.05 + + # The cost of a single packet sent via StargateTech 2's abstract bus. + abstractBusPacket: 1 } } diff --git a/stargatetech2/api/IFactory.java b/stargatetech2/api/IFactory.java new file mode 100644 index 000000000..0a9b43492 --- /dev/null +++ b/stargatetech2/api/IFactory.java @@ -0,0 +1,15 @@ +package stargatetech2.api; + +import stargatetech2.api.bus.IBusDevice; +import stargatetech2.api.bus.IBusDriver; +import stargatetech2.api.bus.IBusInterface; + +/** + * A factory for private classes that implement + * interfaces from the public API. + * + * @author LordFokas + */ +public interface IFactory { + public IBusInterface getIBusInterface(IBusDevice device, IBusDriver driver); +} diff --git a/stargatetech2/api/IStargateTechAPI.java b/stargatetech2/api/IStargateTechAPI.java new file mode 100644 index 000000000..ab00336e3 --- /dev/null +++ b/stargatetech2/api/IStargateTechAPI.java @@ -0,0 +1,27 @@ +package stargatetech2.api; + +import net.minecraft.creativetab.CreativeTabs; +import net.minecraftforge.fluids.Fluid; +import stargatetech2.api.stargate.IStargateNetwork; + +public interface IStargateTechAPI { + /** + * @return The Fluid instance corresponding to Ionized Particles. + */ + public Fluid getIonizedParticlesFluidInstance(); + + /** + * @return The creative inventory tab used by StargateTech 2. + */ + public CreativeTabs getStargateTab(); + + /** + * @return The IStargateNetwork singleton instance. + */ + public IStargateNetwork getStargateNetwork(); + + /** + * @return The current IFactory instance. + */ + public IFactory getFactory(); +} \ No newline at end of file diff --git a/stargatetech2/api/ITabletAccess.java b/stargatetech2/api/ITabletAccess.java new file mode 100644 index 000000000..d9ffb5edb --- /dev/null +++ b/stargatetech2/api/ITabletAccess.java @@ -0,0 +1,27 @@ +package stargatetech2.api; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.world.World; + +/** + * Implemented by blocks that run special actions when clicked with a TabletPC in hand. + * The default action in StargateTech2 is opening a special GUI. + * + * @author LordFokas + */ +public interface ITabletAccess { + + /** + * Make the block run a special action when activated with a TabletPC. + * This method is only called on the client side. + * Implementations requiring server side should use packets. + * + * @param player The player activating the block. + * @param world The world the block is in. + * @param x The block's X coordinate. + * @param y The block's Y coordinate. + * @param z The block's Z coordinate. + * @return True if a special action was executed, false otherwise. + */ + public boolean onTabletAccess(EntityPlayer player, World world, int x, int y, int z); +} diff --git a/stargatetech2/api/StargateTechAPI.java b/stargatetech2/api/StargateTechAPI.java new file mode 100644 index 000000000..c3096a6e5 --- /dev/null +++ b/stargatetech2/api/StargateTechAPI.java @@ -0,0 +1,16 @@ +package stargatetech2.api; + +public abstract class StargateTechAPI implements IStargateTechAPI { + protected static IStargateTechAPI apiInstance; + + /** + * StargateTech's API is abstract, and it's implementation is not visible in the API package. + * All available methods in IStargateTechAPI are implemented elsewhere. + * This method allows you to retrieve an instance of that implementation. + * + * @return a concrete implementation of IStargateTechAPI + */ + public static IStargateTechAPI api(){ + return apiInstance; + } +} \ No newline at end of file diff --git a/stargatetech2/api/bus/BusEvent.java b/stargatetech2/api/bus/BusEvent.java new file mode 100644 index 000000000..80038e88c --- /dev/null +++ b/stargatetech2/api/bus/BusEvent.java @@ -0,0 +1,40 @@ +package stargatetech2.api.bus; + +import net.minecraft.world.World; +import net.minecraftforge.event.Event; + +public class BusEvent extends Event{ + public final World world; + public final int x, y, z; + + protected BusEvent(World world, int x, int y, int z){ + this.world = world; + this.x = x; + this.y = y; + this.z = z; + } + + /** + * Fire this event on Forge's BUS_EVENT to add the IBusDevice + * in this location to a bus network, if any is available. + * + * @author LordFokas + */ + public static final class AddToNetwork extends BusEvent{ + public AddToNetwork(World world, int x, int y, int z) { + super(world, x, y, z); + } + } + + /** + * Fire this event on Forge's BUS_EVENT to remove the IBusDevice + * in this location from any connected bus networks. + * + * @author LordFokas + */ + public static final class RemoveFromNetwork extends BusEvent{ + public RemoveFromNetwork(World world, int x, int y, int z) { + super(world, x, y, z); + } + } +} \ No newline at end of file diff --git a/stargatetech2/api/bus/BusPacket.java b/stargatetech2/api/bus/BusPacket.java new file mode 100644 index 000000000..10116cd4c --- /dev/null +++ b/stargatetech2/api/bus/BusPacket.java @@ -0,0 +1,68 @@ +package stargatetech2.api.bus; + +public abstract class BusPacket { + private final short sender; + private final short target; + private final boolean hasLIP; + + /** + * @param sender The address of the Device that is sending this packet. + * @param target The address of the Device(s) that should receive this packet. + * @param hasLIP Whether or not this packet supports being converted to a plain text (LIP) format. + */ + protected BusPacket(short sender, short target, boolean hasLIP){ + this.sender = sender; + this.target = target; + this.hasLIP = hasLIP; + } + + /** + * @return The address of the device that sent this packet. + */ + public final short getSender(){ + return sender; + } + + /** + * @return The address of the device(s) that should receive this packet. + */ + public final short getTarget(){ + return target; + } + + /** + * @return The ID of the protocol this packet corresponds to. + */ + public final int getProtocolID(){ + return BusProtocols.getProtocolID(this.getClass()); + } + + /** + * @return A plain text (LIP) version of this packet, if it has one. + */ + public final BusPacketLIP getPlainText(){ + if(this instanceof BusPacketLIP){ + return (BusPacketLIP) this; + }else if(hasLIP){ + BusPacketLIP lip = new BusPacketLIP(sender, target); + fillPlainText(lip); + lip.finish(); + return lip; + } + return null; + } + + /** + * Used by subclasses to convert themselves to plain text format. + * + * @param lip The Lazy Intercom Protocol (LIP) packet we're filling with our data. + */ + protected abstract void fillPlainText(BusPacketLIP lip); + + /** + * @return Whether or not this packet supports conversion to the LIP format. + */ + public final boolean hasPlainText(){ + return hasLIP; + } +} diff --git a/stargatetech2/api/bus/BusPacketLIP.java b/stargatetech2/api/bus/BusPacketLIP.java new file mode 100644 index 000000000..063d19b89 --- /dev/null +++ b/stargatetech2/api/bus/BusPacketLIP.java @@ -0,0 +1,113 @@ +package stargatetech2.api.bus; + +import java.util.ArrayList; +import java.util.Hashtable; + +/** + * LIP - Lazy Intercom Protocol
+ *
+ * Baptized by sciguyryan (often found in #ThermalExpansion and #StargateTech on esper.net's IRC), + * this is the universal plain text format used in the Abstract Bus.
+ *
+ * Any packet can choose to be convertible to this format, allowing for any class anywhere to + * read data from a packet which has an unknown and / or private class by asking it to convert to + * a text format. This removes all the problems with type casting and such. + * + * @author LordFokas + */ +public final class BusPacketLIP extends BusPacket { + public static final int PROTOCOL_ID = BusProtocols.addProtocol(BusPacketLIP.class); + private boolean isEditable = true; + private LIPMetadata metadata = null; + private Hashtable data = new Hashtable(); + + /** + * Defines optional metadata that helps sorting this packet out / figuring out what to do with this. + * The fact this is a plain text protocol makes the possibilities so vague it'd be impossible to + * guess what kind of data this packet carries. + * + * @author LordFokas + */ + public static final class LIPMetadata{ + public final String modID; + public final String deviceName; + public final String playerName; + + /** + * Example: new LIPMetadata("StargateTech2", "shieldEmitter", "LordFokas"); + * + * @param modID The ID of the mod that generated this packet. PLEASE do fill this one. + * @param deviceName The name (or a unique identifier) of the type of machine that generated this. + * @param playerName The name of the player that made this packet be generated, if any. + */ + public LIPMetadata(String modID, String deviceName, String playerName){ + this.modID = modID; + this.deviceName = deviceName; + this.playerName = playerName; + } + } + + public BusPacketLIP(short sender, short target) { + super(sender, target, true); + } + + /** + * @param metadata The LIPMetadata object to set to this packet. + */ + public void setMetadata(LIPMetadata metadata){ + if(isEditable && this.metadata == null){ + this.metadata = metadata; + } + } + + /** + * @return The LIPMetadata object on this object. May be null. + */ + public LIPMetadata getMetadata(){ + return metadata; + } + + @Override // We don't need this. At all. + protected void fillPlainText(BusPacketLIP lip){} + + /** + * Finish creating this packet. + * As soon as you call this, it can no longer be modified. + */ + public void finish(){ + isEditable = false; + } + + /** + * @return A list of all the keys for the data on this packet. + */ + public ArrayList getEntryList(){ + ArrayList entries = new ArrayList(); + entries.addAll(data.keySet()); + return entries; + } + + /** + * Add a new entry to this packet. + * If the key already exists the entry is ignored. + * + * @param key The key under which to send the data. + * @param val The data to send. + */ + public void set(String key, String val){ + if(isEditable && !data.containsKey(key)){ + data.put(key.toLowerCase(), val); + } + } + + /** + * Get the value stored under that key, if any. + * Case Insensitive. + * + * @param key The key under which the data is stored. + * @return The data stored under that key, if any, null otherwise. + */ + public String get(String key){ + return data.get(key.toLowerCase()); + } +} \ No newline at end of file diff --git a/stargatetech2/api/bus/BusProtocols.java b/stargatetech2/api/bus/BusProtocols.java new file mode 100644 index 000000000..8cfceb4e6 --- /dev/null +++ b/stargatetech2/api/bus/BusProtocols.java @@ -0,0 +1,32 @@ +package stargatetech2.api.bus; + +import java.util.ArrayList; + +public final class BusProtocols { + private static final ArrayList> protocols = new ArrayList(); + + /** + * Add a protocol to the list, if it doesn't exist yet. + * + * @param packetClass the class of the packet corresponding to the protocol we're adding. + * @return the id of the protocol we just added. + */ + public static final int addProtocol(Class packetClass){ + if(!protocols.contains(packetClass)){ + protocols.add(packetClass); + } + return protocols.indexOf(packetClass); + } + + /** + * Gives you the id of the protocol correspondig to a given packet class. + * + * @param packetClass the class of the packet for which we want to know the protocol ID. + * @return the ID of the protocol corresponding to the packet class. + */ + public static final int getProtocolID(Class packetClass){ + return protocols.indexOf(packetClass); + } + + private BusProtocols(){} +} diff --git a/stargatetech2/api/bus/IBusDevice.java b/stargatetech2/api/bus/IBusDevice.java new file mode 100644 index 000000000..becf8b4d7 --- /dev/null +++ b/stargatetech2/api/bus/IBusDevice.java @@ -0,0 +1,41 @@ +package stargatetech2.api.bus; + +import net.minecraft.world.World; + +/** + * To be implemented by Tile Entities that wish + * to access the Abstract Bus. + * + * @author LordFokas + */ +public interface IBusDevice { + /** + * Returns the IBusInterfaces that exist on that + * side of the Tile Entity. It may be multiple + * values or null. + * + * @param side The side of the block that is being queried. + * @return This side's IBusInterface, if any. + */ + public IBusInterface[] getInterfaces(int side); + + /** + * @return This device's worldObj. + */ + public World getWorld(); + + /** + * @return This device's X Coordinate. + */ + public int getXCoord(); + + /** + * @return This device's Y Coordinate. + */ + public int getYCoord(); + + /** + * @return This device's Z Coordinate. + */ + public int getZCoord(); +} diff --git a/stargatetech2/api/bus/IBusDriver.java b/stargatetech2/api/bus/IBusDriver.java new file mode 100644 index 000000000..d4c25d6ae --- /dev/null +++ b/stargatetech2/api/bus/IBusDriver.java @@ -0,0 +1,53 @@ +package stargatetech2.api.bus; + +/** + * This provides a level of abstraction over the IBusInterface. + * Implement to your own liking. + * + * @author LordFokas + */ +public interface IBusDriver { + /** + * Used to check if this device should receive a specific + * packet type from this sender. If true is returned, + * handlePacket() is called afterwards. + * + * @param sender The Bus address of the packet's sender. + * @param protocolID The unique ID of this packet type. + * @param hasLIP whether the packet can be converted to the LIP format or not. + * @return Whether the device will accept this packet or not. + */ + public boolean canHandlePacket(short sender, int protocolID, boolean hasLIP); + + /** + * Called by the network to have this device handle a packet. + * + * @param packet The packet to be handled. + */ + public void handlePacket(BusPacket packet); + + /** + * Used to make the IBusDriver give all packets in its send + * queue, one by one, to the IBusInterface so that it can + * send them accross the network. + * + * @return The next BusPacket in the queue, if any, null otherise. + */ + public BusPacket getNextPacketToSend(); + + /** + * Called by the hardware representation (IBusInterface) + * to check if it's active or not. + * Inactive interfaces cannot send or receive packets. + * + * @return Whether this interface is active or not. + */ + public boolean isInterfaceEnabled(); + + /** + * Called by this Driver's Interface to check it's own address. + * + * @return The address of this IBusDriver's IBusInterface. + */ + public short getInterfaceAddress(); +} \ No newline at end of file diff --git a/stargatetech2/api/bus/IBusInterface.java b/stargatetech2/api/bus/IBusInterface.java new file mode 100644 index 000000000..39a1dfe87 --- /dev/null +++ b/stargatetech2/api/bus/IBusInterface.java @@ -0,0 +1,35 @@ +package stargatetech2.api.bus; + +import net.minecraft.nbt.NBTTagCompound; + +/** + * DO NOT IMPLEMENT THIS INTERFACE! To get an instance use + * StargateTechAPI.api().getFactory().getIBusInterface(); + * + * @author LordFokas + */ +public interface IBusInterface { + /** + * Makes the IBusInterface call its IBusDriver's + * getNextPacketToSend() method repeatedly until it returns + * null. Every packet returned by that method will be sent + * across the network. + */ + public void sendAllPackets(); + + /** + * Serialize this object. + * + * @param nbt The tag compound where this object's data is. + * @param tag The name of the tag under which this object's data is stored. + */ + public void writeToNBT(NBTTagCompound nbt, String tag); + + /** + * Unserialize this object. + * + * @param nbt The tag compound where this object's data is. + * @param tag The name of the tag under which this object's data is stored. + */ + public void readFromNBT(NBTTagCompound nbt, String tag); +} \ No newline at end of file diff --git a/stargatetech2/api/shields/IShieldable.java b/stargatetech2/api/shields/IShieldable.java new file mode 100644 index 000000000..b185defe8 --- /dev/null +++ b/stargatetech2/api/shields/IShieldable.java @@ -0,0 +1,35 @@ +package stargatetech2.api.shields; + +import net.minecraft.world.World; + +/** + * Used by shield emitters to make shieldable blocks in their way raise and lower their shields. + * + * @author LordFokas + */ +public interface IShieldable { + /** + * Called by shield emitters to make blocks raise their shields. + * The block on {px, py, pz} contains an ITileShieldEmitter from which + * you can get the current ShieldPermissions object. + * + * @param world The world this IShieldable is on. + * @param x This IShieldable's X Coordinate. + * @param y This IShieldable's Y Coordinate. + * @param z This IShieldable's Z Coordinate. + * @param px The X Coordinate of the shield emitter raising a shield on this block. + * @param py The Y Coordinate of the shield emitter raising a shield on this block. + * @param pz The Z Coordinate of the shield emitter raising a shield on this block. + */ + public void onShield(World world, int x, int y, int z, int px, int py, int pz); + + /** + * Called by shield emitters to make blocks lower their shields. + * + * @param world The world this IShieldable is on. + * @param x This IShieldable's X Coordinate. + * @param y This IShieldable's Y Coordinate. + * @param z This IShieldable's Z Coordinate. + */ + public void onUnshield(World world, int x, int y, int z); +} \ No newline at end of file diff --git a/stargatetech2/api/shields/ITileShieldEmitter.java b/stargatetech2/api/shields/ITileShieldEmitter.java new file mode 100644 index 000000000..96dd027b0 --- /dev/null +++ b/stargatetech2/api/shields/ITileShieldEmitter.java @@ -0,0 +1,65 @@ +package stargatetech2.api.shields; + +/** + * Implemented by the shield emitter TileEntities. + * + * @author LordFokas + */ +public interface ITileShieldEmitter { + /** + * Given the way shield emitters work together, you cannot + * directly access their ShieldPermissions object. + * This means you cannot use this method to change + * permissions on a shield. + * + * @return A deep clone of theShieldPermissions object that + * defines this emitter's shield behavior. + */ + public ShieldPermissions getPermissions(); + + /** + * @return True if the shield is activated, false otherwise. + */ + public boolean isShieldOn(); + + /** + * Update the permissions on this emitter. + * It will propagate to the whole shield. + * + * @param isAllow true if allowing this flag, false if disallowing. + * @param flag The flag we're (dis)allowing. + * @see stargatetech2.api.ShieldPermissions + */ + public void updatePermissions(boolean isAllow, int flag); + + /** + * Update the exceptions on this emitter. + * It will propagate to the whole shield. + * + * @param isAdding true if we're adding a player to the exceptions, false if removing. + * @param player The name of the player we're adding / removing. + * @see stargatetech2.api.ShieldPermissions + */ + public void updateExceptions(boolean isAdding, String player); + + /** + * Sets the owner of this Shield Emitter. + * An owner has previleges no other players have. + * + * @param owner The owner's player name. + */ + public void setOwner(String owner); + + /** + * @return The player name of this machine's owner. + */ + public String getOwner(); + + /** + * Checks if a player can access this device. + * + * @param player The player's name. + * @return Whether or not this player can access this device. + */ + public boolean canAccess(String player); +} \ No newline at end of file diff --git a/stargatetech2/api/shields/ShieldPermissions.java b/stargatetech2/api/shields/ShieldPermissions.java new file mode 100644 index 000000000..c0cf3920c --- /dev/null +++ b/stargatetech2/api/shields/ShieldPermissions.java @@ -0,0 +1,163 @@ +package stargatetech2.api.shields; + +import java.util.ArrayList; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.item.EntityMinecart; +import net.minecraft.entity.monster.EntityMob; +import net.minecraft.entity.passive.EntityAnimal; +import net.minecraft.entity.passive.EntityVillager; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.nbt.NBTTagCompound; + + +public class ShieldPermissions { + // Permission Flags + public static final int PERM_PLAYER = 0x01; + public static final int PERM_VILLAGER = 0x02; + public static final int PERM_ANIMAL = 0x04; + public static final int PERM_MONSTER = 0x08; + public static final int PERM_MINECART = 0x10; + + private int permValue = 0; + private ArrayList playerExceptions = new ArrayList(); + + /** + * @return A default ShieldPermissions object. + */ + public static ShieldPermissions getDefault(){ + ShieldPermissions perm = new ShieldPermissions(); + perm.allow(PERM_PLAYER); + return perm; + } + + /** + * @param perm Make shields allow this PERM_* flag. + * Combinations (binary or) are allowed. + */ + public void allow(int perm){ + permValue |= perm; + } + + /** + * @param perm Make shields disallow this PERM_* flag. + * Combinations (binary or) are allowed. + */ + public void disallow(int perm){ + permValue &= ~perm; + } + + /** + * Add an exception to the current player permission setting. + * @param player The name of the player to add to the exceptions. + */ + public void setPlayerException(String player){ + if(!playerExceptions.contains(player)) + playerExceptions.add(player); + } + + /** + * Remove an exception from the current player permission setting. + * @param player The name of the player to remove from the exceptions. + */ + public void removePlayerException(String player){ + playerExceptions.remove(player); + } + + /** + * @return A list of strings containing the names of all the players + * who currently are exceptions to the player permission setting. + */ + public ArrayList getExceptionList(){ + return playerExceptions; + } + + /** + * Check if this entity is allowed to go through the shields. + * + * @param entity The entity to be checked. + * @param doDismount If set to true, when an allowed entity is being ridden + * by a disallowed one, it's rider will be dismounted so this entity can pass. + * @return Whether this entity can go through or not. + */ + public boolean isEntityAllowed(Entity entity, boolean doDismount){ + boolean allow = false; + if(entity instanceof EntityPlayer){ + if(hasBit(PERM_PLAYER)){ + allow = true; + } + if(playerExceptions.contains(entity.getEntityName())){ + allow = !allow; + } + }else if(entity instanceof EntityVillager){ + allow = hasBit(PERM_VILLAGER); + }else if(entity instanceof EntityAnimal){ + allow = hasBit(PERM_ANIMAL); + }else if(entity instanceof EntityMob){ + allow = hasBit(PERM_MONSTER); + }else if(entity instanceof EntityMinecart){ + allow = hasBit(PERM_MINECART); + } + if(allow && entity.riddenByEntity != null && doDismount && entity.worldObj.isRemote == false){ + if(!isEntityAllowed(entity.riddenByEntity, true)){ + Entity rider = entity.riddenByEntity; + if(rider instanceof EntityPlayer){ + rider.mountEntity(null); + }else{ + rider.ridingEntity = null; + rider.prevPosY += 1; + rider.posY += 1; + entity.riddenByEntity = null; + } + } + } + return allow; + } + + /** + * @param bit A binary flag to check against these permissions. + * While usually this is a PERM_* flag, any combination of bits is allowed. + * @return Whether this flag exists in these permissions or not. + */ + public boolean hasBit(int bit){ + return (permValue & bit) != 0; + } + + /** + * @return A deep clone of this object. + */ + public ShieldPermissions deepClone(){ + ShieldPermissions clone = new ShieldPermissions(); + clone.permValue = this.permValue; + for(String player : playerExceptions){ + clone.playerExceptions.add(player); + } + return clone; + } + + // This really doesn't need an explanation... + public static ShieldPermissions readFromNBT(NBTTagCompound nbt){ + ShieldPermissions permissions = getDefault(); + if(nbt != null){ + int exceptions = nbt.getInteger("exceptions"); + permissions.permValue = nbt.getInteger("permValue"); + permissions.playerExceptions = new ArrayList(exceptions); + for(int i = 0; i < exceptions; i++){ + permissions.setPlayerException(nbt.getString("pex" + i)); + } + } + return permissions; + } + + // ... does it? + public NBTTagCompound writeToNBT(){ + NBTTagCompound nbt = new NBTTagCompound(); + int exceptions = playerExceptions.size(); + nbt.setInteger("permValue", permValue); + nbt.setInteger("exceptions", exceptions); + for(int i = 0; i < exceptions; i++){ + nbt.setString("pex" + i, playerExceptions.get(i)); + } + return nbt; + } +} \ No newline at end of file diff --git a/stargatetech2/api/stargate/Address.java b/stargatetech2/api/stargate/Address.java new file mode 100644 index 000000000..5f7caa404 --- /dev/null +++ b/stargatetech2/api/stargate/Address.java @@ -0,0 +1,82 @@ +package stargatetech2.api.stargate; + +public class Address { + private Symbol[] symbols; + + public static Address create(Symbol[] symbols){ + try{ + boolean used[] = new boolean[40]; + if(symbols.length < 7) throw new Exception("Address too short: " + symbols.length); + if(symbols.length > 9) throw new Exception("Address too long: " + symbols.length); + for(int i = 0; i < used.length; i++){ + used[i] = (i == 0); + } + for(Symbol symbol : symbols){ + if(symbol == null || symbol == Symbol.VOID){ + throw new Exception("Invalid Symbol."); + } + if(used[symbol.ordinal()]){ + throw new Exception("Repeated Symbol."); + } + used[symbol.ordinal()] = true; + } + return new Address(symbols); + }catch(Exception e){ + return null; + } + } + + private Address(Symbol[] symbols){ + this.symbols = symbols; + } + + public int length(){ + return symbols.length; + } + + public Symbol getSymbol(int symbol){ + if(symbol >= 0 && symbol < length()){ + return symbols[symbol]; + }else{ + return Symbol.VOID; + } + } + + @Override + public String toString(){ + StringBuilder sb = new StringBuilder(); + sb.append(getSymbol(0).toString()); + sb.append(getSymbol(1).toString().toLowerCase()); + sb.append(getSymbol(2).toString().toLowerCase()); + sb.append(" "); + sb.append(getSymbol(3).toString()); + sb.append(getSymbol(4).toString().toLowerCase()); + sb.append(getSymbol(5).toString().toLowerCase()); + sb.append(" "); + sb.append(getSymbol(6).toString()); + sb.append(getSymbol(7).toString().toLowerCase()); + sb.append(getSymbol(8).toString().toLowerCase()); + return sb.toString(); + } + + @Override + public boolean equals(Object o){ + if(o instanceof Address){ + Address a = (Address) o; + if(a.length() == length()){ + for(int i = 0; i < length(); i++){ + if(symbols[i] != a.symbols[i]){ + return false; + } + } + return true; + } + } + return false; + } + + @Override + public int hashCode(){ + return length(); + } +} \ No newline at end of file diff --git a/stargatetech2/api/stargate/IStargateNetwork.java b/stargatetech2/api/stargate/IStargateNetwork.java new file mode 100644 index 000000000..7bd3ce4c5 --- /dev/null +++ b/stargatetech2/api/stargate/IStargateNetwork.java @@ -0,0 +1,36 @@ +package stargatetech2.api.stargate; + +import net.minecraft.world.World; + +public interface IStargateNetwork { + /** + * @return Whether the Stargate Network is loaded (working) or not. + */ + public boolean isLoaded(); + + /** + * @param address The string representation of an address. (e.g. "Proclarush Taonas At") + * @return an address object if the string is a valid address, null otherwise. + */ + public Address parseAddress(String address); + + /** + * Checks if a given address exists in the network or not. + * (i.e., if this address maps to a physical Stargate) + * + * @param address the address we want to check. + * @return whether the address exists or not. + */ + public boolean addressExists(Address address); + + /** + * Returns the address of the Stargate in a specific location if it exists or null otherwise. + * + * @param world The world the target Stargate is in. + * @param x The target Stargate's X coordinate. + * @param y The target Stargate's Y coordinate. + * @param z The target Stargate's Z coordinate. + * @return The Stargate's address, or null if the location doesn't contain a Stargate. + */ + public Address getAddressOf(World world, int x, int y, int z); +} \ No newline at end of file diff --git a/stargatetech2/api/stargate/ITileStargate.java b/stargatetech2/api/stargate/ITileStargate.java new file mode 100644 index 000000000..b50d2d8ab --- /dev/null +++ b/stargatetech2/api/stargate/ITileStargate.java @@ -0,0 +1,16 @@ +package stargatetech2.api.stargate; + +/** + * Represents a Stargate ring. + * Stargate "base" blocks contain the ring, so they implement this as well. + * + * All you can get from the Stargate ring is the address. + * + * @author LordFokas + */ +public interface ITileStargate { + /** + * @return This Stargate's address. null values are possible on the client side. + */ + public Address getAddress(); +} \ No newline at end of file diff --git a/stargatetech2/api/stargate/ITileStargateBase.java b/stargatetech2/api/stargate/ITileStargateBase.java new file mode 100644 index 000000000..43e8fdae6 --- /dev/null +++ b/stargatetech2/api/stargate/ITileStargateBase.java @@ -0,0 +1,25 @@ +package stargatetech2.api.stargate; + +/** + * Represents a Stargate base block (the block that supports the stargate). + * + * It contains all the important logic in the Stargate, + * like dialing, Iris control and power usage. + * + * Because the ring is inside the block that supports it, it is possible to + * call the same methods you can call on a ring. + * + * @see ITileStargate + * + * @author LordFokas + */ +public interface ITileStargateBase extends ITileStargate{ + + /** + * Used to try making the Stargate dial an address. + * + * @param address The address this Stargate should dial. + * @return whether the dialing sequence started (true) or failed (false). + */ + public boolean dial(Address address); +} \ No newline at end of file diff --git a/stargatetech2/api/stargate/Symbol.java b/stargatetech2/api/stargate/Symbol.java new file mode 100644 index 000000000..dcad644a7 --- /dev/null +++ b/stargatetech2/api/stargate/Symbol.java @@ -0,0 +1,63 @@ +package stargatetech2.api.stargate; + +public enum Symbol { + VOID(""), + AT ("At"), // 1 + AL ("Al"), // 2 + CLA ("Cla"), // 3 + UR ("Ur"), // 4 + ON ("On"), // 5 + DEH ("Deh"), // 6 + EC ("Ec"), // 7 + MIG ("Mig"), // 8 + AM ("Am"), // 9 + RUM ("Rum"), // 10 + AR ("Ar"), // 11 + VA ("Va"), // 12 + COR ("Cor"), // 13 + PRA ("Pra"), // 14 + OM ("Om"), // 15 + ET ("Et"), // 16 + AS ("As"), // 17 + US ("Us"), // 18 + GON ("Gon"), // 19 + ORM ("Orm"), // 20 + EM ("Em"), // 21 + AC ("Ac"), // 22 + OTH ("Oth"), // 23 + LOS ("Los"), // 24 + LAN ("Lan"), // 25 + EST ("Est"), // 26 + CRO ("Cro"), // 27 + SIL ("Sil"), // 28 + TA ("Ta"), // 29 + BREI("Brei"), // 30 + RUSH("Rush"), // 31 + ERP ("Erp"), // 32 + SET ("Set"), // 33 + ULF ("Ulf"), // 34 + PRO ("Pro"), // 35 + SAL ("Sal"), // 36 + TIS ("Tis"), // 37 + MAC ("Mac"), // 38 + IRT ("Irt"); // 39 + + private String name; + + private Symbol(String name){ + this.name = name; + } + + public static Symbol get(int s){ + if(s >= 0 && s <= 39){ + return values()[s]; + }else{ + return VOID; + } + } + + @Override + public String toString(){ + return name; + } +} \ No newline at end of file