showing user list of computers in their analyzer result; some (theoretical) improvements to computer ownership (I really should test this somewhen); reduced range of included players for sending some packets

This commit is contained in:
Florian Nücke 2013-12-01 17:16:58 +01:00
parent 5ea8d072c0
commit cbf17609bd
10 changed files with 100 additions and 29 deletions

View File

@ -56,6 +56,7 @@ oc:gui.Analyzer.RobotName=Name
oc:gui.Analyzer.RobotOwner=Owner
oc:gui.Analyzer.StoredEnergy=Stored energy
oc:gui.Analyzer.TotalEnergy=Total stored energy
oc:gui.Analyzer.Users=Users
oc:gui.Robot.Power=Power
oc:gui.Robot.TurnOff=Turn off
oc:gui.Robot.TurnOn=Turn on

View File

@ -26,6 +26,7 @@ class PacketHandler extends CommonPacketHandler {
case PacketType.Analyze => onAnalyze(p)
case PacketType.ChargerState => onChargerState(p)
case PacketType.ComputerState => onComputerState(p)
case PacketType.ComputerUserList => onComputerUserList(p)
case PacketType.PowerState => onPowerState(p)
case PacketType.RedstoneState => onRedstoneState(p)
case PacketType.RobotAnimateSwing => onRobotAnimateSwing(p)
@ -73,6 +74,14 @@ class PacketHandler extends CommonPacketHandler {
case _ => // Invalid packet.
}
def onComputerUserList(p: PacketParser) =
p.readTileEntity[Computer]() match {
case Some(t) =>
val count = p.readInt()
t.users = (0 until count).map(_ => p.readUTF())
case _ => // Invalid packet.
}
def onPowerState(p: PacketParser) =
p.readTileEntity[PowerInformation]() match {
case Some(t) =>

View File

@ -6,6 +6,7 @@ object PacketType extends Enumeration {
Analyze,
ChargerState,
ComputerState,
ComputerUserList,
PowerState,
RedstoneState,
RobotAnimateSwing,

View File

@ -80,8 +80,7 @@ abstract class Case(val parent: SimpleDelegator) extends Computer with SimpleDel
// TODO do we have to manually sync the client since we can only check this on the server side?
override def removedByEntity(world: World, x: Int, y: Int, z: Int, player: EntityPlayer) =
world.getBlockTileEntity(x, y, z) match {
case c: tileentity.Case if !world.isRemote =>
c.computer.canInteract(player.getCommandSenderName)
case c: tileentity.Case => c.canInteract(player.getCommandSenderName)
case _ => super.removedByEntity(world, x, y, z, player)
}
}

View File

@ -54,7 +54,7 @@ class RobotAfterimage(val parent: SpecialDelegator) extends SpecialDelegate {
override def rightClick(world: World, x: Int, y: Int, z: Int, player: EntityPlayer, side: ForgeDirection, hitX: Float, hitY: Float, hitZ: Float) = {
findMovingRobot(world, x, y, z) match {
case Some(robot) => robot.getBlockType.onBlockActivated(world, robot.x, robot.y, robot.z, player, side.ordinal, hitX, hitY, hitZ)
case Some(robot) => Blocks.robotProxy.rightClick(world, robot.x, robot.y, robot.z, player, side, hitX, hitY, hitZ)
case _ => world.setBlockToAir(x, y, z)
}
}

View File

@ -2,6 +2,7 @@ package li.cil.oc.common.block
import java.util
import li.cil.oc.common.{GuiType, tileentity}
import li.cil.oc.server.PacketSender
import li.cil.oc.server.component.robot
import li.cil.oc.util.Tooltip
import li.cil.oc.{Settings, OpenComputers}
@ -84,6 +85,14 @@ class RobotProxy(val parent: SpecialDelegator) extends Computer with SpecialDele
side: ForgeDirection, hitX: Float, hitY: Float, hitZ: Float) = {
if (!player.isSneaking) {
if (!world.isRemote) {
// We only send slot changes to nearby players, so if there was no slot
// change since this player got into range he might have the wrong one,
// so we send him the current one just in case.
world.getBlockTileEntity(x, y, z) match {
case proxy: tileentity.RobotProxy =>
PacketSender.sendRobotSelectedSlotChange(proxy.robot)
case _ =>
}
player.openGui(OpenComputers, GuiType.Robot.id, world, x, y, z)
}
true

View File

@ -6,9 +6,10 @@ import li.cil.oc.api.network._
import li.cil.oc.server.{PacketSender => ServerPacketSender, driver, component}
import li.cil.oc.util.ExtendedNBT._
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.nbt.{NBTTagString, NBTTagCompound}
import net.minecraftforge.common.ForgeDirection
import scala.Some
import scala.collection.mutable
abstract class Computer(isRemote: Boolean) extends Environment with ComponentInventory with Rotatable with BundledRedstone with Analyzable {
protected val _computer = if (isRemote) null else new component.Computer(this)
@ -23,10 +24,14 @@ abstract class Computer(isRemote: Boolean) extends Environment with ComponentInv
private var hasChanged = false
@SideOnly(Side.CLIENT)
private val _users = mutable.Set.empty[String]
// ----------------------------------------------------------------------- //
def isRunning = _isRunning
@SideOnly(Side.CLIENT)
def isRunning_=(value: Boolean) = {
_isRunning = value
world.markBlockForRenderUpdate(x, y, z)
@ -40,6 +45,21 @@ abstract class Computer(isRemote: Boolean) extends Environment with ComponentInv
case _ => false
}
def users: Iterable[String] =
if (isServer) computer.users
else _users
@SideOnly(Side.CLIENT)
def users_=(list: Iterable[String]) {
_users.clear()
_users ++= list
}
def canInteract(player: String) =
if (isServer) computer.canInteract(player)
else !Settings.get.canComputersBeOwned ||
_users.isEmpty || _users.contains(player)
// ----------------------------------------------------------------------- //
override def updateEntity() {
@ -95,11 +115,14 @@ abstract class Computer(isRemote: Boolean) extends Environment with ComponentInv
override def readFromNBTForClient(nbt: NBTTagCompound) {
super.readFromNBTForClient(nbt)
isRunning = nbt.getBoolean("isRunning")
_users.clear()
_users ++= nbt.getTagList("users").iterator[NBTTagString].map(_.data)
}
override def writeToNBTForClient(nbt: NBTTagCompound) {
super.writeToNBTForClient(nbt)
nbt.setBoolean("isRunning", isRunning)
nbt.setNewTagList("users", computer.users.map(user => new NBTTagString(null, user)))
}
// ----------------------------------------------------------------------- //
@ -126,9 +149,14 @@ abstract class Computer(isRemote: Boolean) extends Environment with ComponentInv
def onAnalyze(stats: NBTTagCompound, player: EntityPlayer, side: Int, hitX: Float, hitY: Float, hitZ: Float): Node = {
if (computer != null) computer.lastError match {
case Some(value) => stats.setString(Settings.namespace + "gui.Analyzer.LastError", value)
case Some(value) =>
stats.setString(Settings.namespace + "gui.Analyzer.LastError", value)
case _ =>
}
val list = users
if (list.size > 0) {
stats.setString(Settings.namespace + "gui.Analyzer.Users", list.mkString(", "))
}
computer.node
}
}

View File

@ -44,6 +44,16 @@ object PacketSender {
}
}
def sendComputerUserList(t: Computer, list: Array[String]) {
val pb = new PacketBuilder(PacketType.ComputerUserList)
pb.writeTileEntity(t)
pb.writeInt(list.length)
list.foreach(pb.writeUTF)
pb.sendToNearbyPlayers(t)
}
def sendPowerState(t: PowerInformation, player: Option[Player] = None) {
val pb = new PacketBuilder(PacketType.PowerState)
@ -91,7 +101,7 @@ object PacketSender {
pb.writeTileEntity(t.proxy)
pb.writeInt(t.animationTicksTotal)
pb.sendToNearbyPlayers(t)
pb.sendToNearbyPlayers(t, 64)
}
def sendRobotAnimateTurn(t: Robot) {
@ -101,7 +111,7 @@ object PacketSender {
pb.writeByte(t.turnAxis)
pb.writeInt(t.animationTicksTotal)
pb.sendToNearbyPlayers(t)
pb.sendToNearbyPlayers(t, 64)
}
def sendRobotEquippedItemChange(t: Robot, stack: ItemStack) {
@ -119,7 +129,7 @@ object PacketSender {
pb.writeTileEntity(t.proxy)
pb.writeInt(t.selectedSlot)
pb.sendToNearbyPlayers(t)
pb.sendToNearbyPlayers(t, 16)
}
def sendRotatableState(t: Rotatable, player: Option[Player] = None) {
@ -187,7 +197,7 @@ object PacketSender {
pb.writeTileEntity(t)
pb.writeBoolean(hasPower)
pb.sendToNearbyPlayers(t)
pb.sendToNearbyPlayers(t, 64)
}
def sendScreenResolutionChange(t: Buffer, w: Int, h: Int) {

View File

@ -7,6 +7,7 @@ import li.cil.oc.api
import li.cil.oc.api.network._
import li.cil.oc.common.tileentity
import li.cil.oc.server
import li.cil.oc.server.PacketSender
import li.cil.oc.util.ExtendedLuaState.extendLuaState
import li.cil.oc.util.ExtendedNBT._
import li.cil.oc.util.{ThreadPoolFactory, GameTimeFormatter, LuaStateFactory}
@ -45,7 +46,7 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
private val addedComponents = mutable.Set.empty[Component]
private val users = mutable.Set.empty[String]
private val _users = mutable.Set.empty[String]
private val signals = new mutable.Queue[Computer.Signal]
@ -67,6 +68,8 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
private var remainingPause = 0 // Ticks left to wait before resuming.
private var usersChanged = false // Send updated users list to clients?
private var message: Option[String] = None // For error messages.
// ----------------------------------------------------------------------- //
@ -83,6 +86,8 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
def lastError = message
def users = _users.synchronized(_users.toArray)
def isRobot = false
// ----------------------------------------------------------------------- //
@ -90,7 +95,7 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
def address = node.address
def canInteract(player: String) = !Settings.get.canComputersBeOwned ||
users.isEmpty || users.contains(player) ||
_users.synchronized(_users.isEmpty || _users.contains(player)) ||
MinecraftServer.getServer.isSinglePlayer ||
MinecraftServer.getServer.getConfigurationManager.isPlayerOpped(player)
@ -232,6 +237,15 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
}
})
// Avoid spamming user list across the network.
if (worldTime % 20 == 0 && usersChanged) {
val list = _users.synchronized {
usersChanged = false
users
}
PacketSender.sendComputerUserList(owner, list)
}
// Check if we should switch states. These are all the states in which we're
// guaranteed that the executor thread isn't running anymore.
state.synchronized(state.top match {
@ -419,14 +433,14 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
override def load(nbt: NBTTagCompound) = this.synchronized {
assert(state.top == Computer.State.Stopped)
assert(users.isEmpty)
assert(_users.isEmpty)
assert(signals.isEmpty)
state.clear()
super.load(nbt)
state.pushAll(nbt.getTagList("state").iterator[NBTTagInt].reverse.map(s => Computer.State(s.data)))
nbt.getTagList("users").foreach[NBTTagString](u => users += u.data)
nbt.getTagList("users").foreach[NBTTagString](u => _users += u.data)
if (state.size > 0 && state.top != Computer.State.Stopped && init()) {
// Unlimit memory use while unpersisting.
@ -506,7 +520,7 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
processAddedComponents()
nbt.setNewTagList("state", state.map(_.id))
nbt.setNewTagList("users", users)
nbt.setNewTagList("users", _users)
if (state.top != Computer.State.Stopped) {
// Unlimit memory while persisting.
@ -826,25 +840,28 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
// User management.
lua.pushScalaFunction(lua => {
users.foreach(lua.pushString)
users.size
_users.foreach(lua.pushString)
_users.size
})
lua.setField(-2, "users")
lua.pushScalaFunction(lua => try {
if (users.size >= Settings.get.maxUsers)
if (_users.size >= Settings.get.maxUsers)
throw new Exception("too many users")
val name = lua.checkString(1)
if (users.contains(name))
if (_users.contains(name))
throw new Exception("user exists")
if (name.length > Settings.get.maxUsernameLength)
throw new Exception("username too long")
if (!MinecraftServer.getServer.getConfigurationManager.getAllUsernames.contains(name))
throw new Exception("player must be online")
users += name
_users.synchronized {
_users += name
usersChanged = true
}
lua.pushBoolean(true)
1
} catch {
@ -857,7 +874,13 @@ class Computer(val owner: tileentity.Computer) extends ManagedComponent with Con
lua.pushScalaFunction(lua => {
val name = lua.checkString(1)
lua.pushBoolean(users.remove(name))
_users.synchronized {
val success = _users.remove(name)
if (success) {
usersChanged = true
}
lua.pushBoolean(success)
}
1
})
lua.setField(-2, "removeUser")

View File

@ -22,15 +22,6 @@ abstract class ManagedComponent extends ManagedEnvironment {
if (node != null) nbt.setNewCompoundTag("node", node.save)
}
/**
* Handy function for returning a list of results.
* <p/>
* This is primarily meant to be used for returning result arrays from Lua
* callbacks, to avoid having to write `XYZ.box(...)` all the time.
*
* @param args the values to return.
* @return and array of objects.
*/
final protected def result(args: Any*): Array[AnyRef] = {
def unwrap(arg: Any): AnyRef = arg match {
case x: ScalaNumber => x.underlying