Merge branch 'master-MC1.9.4' of github.com:MightyPirates/OpenComputers into master-MC1.10

This commit is contained in:
Florian Nücke 2016-09-18 20:59:55 +02:00
commit 5cab00af2d
12 changed files with 384 additions and 84 deletions

View File

@ -0,0 +1,31 @@
package li.cil.oc.api.event;
import net.minecraft.tileentity.TileEntitySign;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;
/**
* A bit more specific sign change event that holds information about new text of the sign. Used in the sign upgrade.
*/
public abstract class SignChangeEvent extends Event {
public final TileEntitySign sign;
public final String[] lines;
private SignChangeEvent(TileEntitySign sign, String[] lines) {
this.sign = sign;
this.lines = lines;
}
@Cancelable
public static class Pre extends SignChangeEvent {
public Pre(TileEntitySign sign, String[] lines) {
super(sign, lines);
}
}
public static class Post extends SignChangeEvent {
public Post(TileEntitySign sign, String[] lines) {
super(sign, lines);
}
}
}

View File

@ -1511,9 +1511,11 @@ opencomputers {
# Enable debug card functionality. This may also be of use for custom
# maps, so it is enabled by default. If you run a server where people
# may cheat in items but should not have op/admin-like rights, you may
# want to set this to false. This will *not* remove the card, it will
# just make all functions it provides error out.
enableDebugCard: true
# want to set this to false or `deny`. Set this to `whitelist` if you
# want to enable whitelisting of debug card users (managed by command
# /oc_debugWhitelist). This will *not* remove the card, it will just
# make all functions it provides error out.
debugCardAccess: allow
# Whether to always register the LuaJ architecture - even if the native
# library is available. In that case it is possible to switch between

View File

@ -66,9 +66,15 @@ function bit32.replace(n, v, field, width)
end
function bit32.lrotate(x, disp)
if disp == 0 then return x
elseif disp < 0 then return bit32.rrotate(x, -disp)
else return trim((x << disp) | (x >> (32 - disp))) end
if disp == 0 then
return x
elseif disp < 0 then
return bit32.rrotate(x, -disp)
else
disp = disp & 31
x = trim(x)
return trim((x << disp) | (x >> (32 - disp)))
end
end
function bit32.lshift(x, disp)
@ -76,9 +82,15 @@ function bit32.lshift(x, disp)
end
function bit32.rrotate(x, disp)
if disp == 0 then return x
elseif disp < 0 then return bit32.lrotate(x, -disp)
else return trim((x >> disp) | (x << (32 - disp))) end
if disp == 0 then
return x
elseif disp < 0 then
return bit32.lrotate(x, -disp)
else
disp = disp & 31
x = trim(x)
return trim((x >> disp) | (x << (32 - disp)))
end
end
function bit32.rshift(x, disp)

View File

@ -3,19 +3,26 @@ package li.cil.oc
import java.io._
import java.net.Inet4Address
import java.net.InetAddress
import java.nio.charset.StandardCharsets
import java.security.SecureRandom
import java.util.UUID
import com.google.common.net.InetAddresses
import com.mojang.authlib.GameProfile
import com.typesafe.config._
import li.cil.oc.Settings.DebugCardAccess
import li.cil.oc.common.Tier
import li.cil.oc.integration.Mods
import li.cil.oc.server.component.DebugCard
import li.cil.oc.server.component.DebugCard.AccessContext
import org.apache.commons.codec.binary.Hex
import net.minecraftforge.fml.common.Loader
import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion
import net.minecraftforge.fml.common.versioning.VersionRange
import org.apache.commons.lang3.StringEscapeUtils
import scala.collection.convert.WrapAsScala._
import scala.collection.mutable
import scala.io.Codec
import scala.io.Source
import scala.util.matching.Regex
@ -415,7 +422,21 @@ class Settings(val config: Config) {
val nativeInTmpDir = config.getBoolean("debug.nativeInTmpDir")
val periodicallyForceLightUpdate = config.getBoolean("debug.periodicallyForceLightUpdate")
val insertIdsInConverters = config.getBoolean("debug.insertIdsInConverters")
val enableDebugCard = config.getBoolean("debug.enableDebugCard")
val debugCardAccess = config.getValue("debug.debugCardAccess").unwrapped() match {
case "true" | "allow" | java.lang.Boolean.TRUE => DebugCardAccess.Allowed
case "false" | "deny" | java.lang.Boolean.FALSE => DebugCardAccess.Forbidden
case "whitelist" =>
val wlFile = new File(Loader.instance.getConfigDir + File.separator + "opencomputers" + File.separator +
"debug_card_whitelist.txt")
DebugCardAccess.Whitelist(wlFile)
case _ => // Fallback to most secure configuration
OpenComputers.log.warn("Unknown debug card access type, falling back to `deny`. Allowed values: `allow`, `deny`, `whitelist`.")
DebugCardAccess.Forbidden
}
val registerLuaJArchitecture = config.getBoolean("debug.registerLuaJArchitecture")
val disableLocaleChanging = config.getBoolean("debug.disableLocaleChanging")
}
@ -549,4 +570,95 @@ object Settings {
def apply(inetAddress: InetAddress, host: String) = validator(inetAddress, host)
}
sealed trait DebugCardAccess {
def checkAccess(ctx: Option[DebugCard.AccessContext]): Option[String]
}
object DebugCardAccess {
case object Forbidden extends DebugCardAccess {
override def checkAccess(ctx: Option[AccessContext]): Option[String] =
Some("debug card is disabled")
}
case object Allowed extends DebugCardAccess {
override def checkAccess(ctx: Option[AccessContext]): Option[String] = None
}
case class Whitelist(noncesFile: File) extends DebugCardAccess {
private val values = mutable.Map.empty[String, String]
private val rng = SecureRandom.getInstance("SHA1PRNG")
load()
def save(): Unit = {
val noncesDir = noncesFile.getParentFile
if (!noncesDir.exists() && !noncesDir.mkdirs())
throw new IOException(s"Cannot create nonces directory: ${noncesDir.getCanonicalPath}")
val writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(noncesFile), StandardCharsets.UTF_8), false)
try {
for ((p, n) <- values)
writer.println(s"$p $n")
} finally writer.close()
}
def load(): Unit = {
values.clear()
if (!noncesFile.exists())
return
val reader = new BufferedReader(new InputStreamReader(new FileInputStream(noncesFile), StandardCharsets.UTF_8))
Iterator.continually(reader.readLine())
.takeWhile(_ != null)
.map(_.split(" ", 2))
.flatMap {
case Array(p, n) => Seq(p -> n)
case _ => Nil
}.foreach(values += _)
}
private def generateNonce(): String = {
val buf = new Array[Byte](16)
rng.nextBytes(buf)
new String(Hex.encodeHex(buf, true))
}
def nonce(player: String) = values.get(player.toLowerCase)
def isWhitelisted(player: String) = values.contains(player.toLowerCase)
def whitelist: collection.Set[String] = values.keySet
def add(player: String): Unit = {
if (!values.contains(player.toLowerCase)) {
values.put(player.toLowerCase, generateNonce())
save()
}
}
def remove(player: String): Unit = {
if (values.remove(player.toLowerCase).isDefined)
save()
}
def invalidate(player: String): Unit = {
if (values.contains(player.toLowerCase)) {
values.put(player.toLowerCase, generateNonce())
save()
}
}
def checkAccess(ctxOpt: Option[DebugCard.AccessContext]): Option[String] = ctxOpt match {
case Some(ctx) => values.get(ctx.player) match {
case Some(x) =>
if (x == ctx.nonce) None
else Some("debug card is invalidated, please re-bind it to yourself")
case None => Some("you are not whitelisted to use debug card")
}
case None => Some("debug card is whitelisted, Shift+Click with it to bind card to yourself")
}
}
}
}

View File

@ -474,7 +474,9 @@ class Drone(val world: World) extends Entity(world) with MachineHost with intern
override def processInitialInteract(player: EntityPlayer, stack: ItemStack, hand: EnumHand): Boolean = {
if (player.isSneaking) {
if (Wrench.isWrench(player.getHeldItemMainhand)) {
kill()
if(!world.isRemote) {
kill()
}
}
else if (!world.isRemote && !machine.isRunning) {
preparePowerUp()

View File

@ -2,26 +2,44 @@ package li.cil.oc.common.item
import java.util
import li.cil.oc.Settings
import li.cil.oc.Settings.DebugCardAccess
import li.cil.oc.common.item.data.DebugCardData
import li.cil.oc.server.component.{DebugCard => CDebugCard}
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.util.ActionResult
import net.minecraft.util.EnumActionResult
import net.minecraft.util.EnumHand
import net.minecraft.util.text.TextComponentString
import net.minecraft.world.World
class DebugCard(val parent: Delegator) extends traits.Delegate {
override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[String]): Unit = {
super.tooltipExtended(stack, tooltip)
val data = new DebugCardData(stack)
data.player.foreach(name => tooltip.add(s"§8$name§r"))
data.access.foreach(access => tooltip.add(s"§8${access.player}§r"))
}
override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer): ActionResult[ItemStack] = {
if (player.isSneaking) {
val data = new DebugCardData(stack)
if (data.player.contains(player.getName)) data.player = None
else data.player = Option(player.getName)
val name = player.getName
if (data.access.exists(_.player == name)) data.access = None
else data.access =
Some(CDebugCard.AccessContext(name, Settings.get.debugCardAccess match {
case wl: DebugCardAccess.Whitelist => wl.nonce(name) match {
case Some(n) => n
case None =>
player.addChatComponentMessage(new TextComponentString("§cYou are not whitelisted to use debug card"))
player.swingArm(EnumHand.MAIN_HAND)
return new ActionResult[ItemStack](EnumActionResult.FAIL, stack)
}
case _ => ""
}))
data.save(stack)
player.swingArm(EnumHand.MAIN_HAND)
}

View File

@ -1,5 +1,6 @@
package li.cil.oc.common.item.data
import li.cil.oc.server.component.DebugCard.AccessContext
import li.cil.oc.Constants
import li.cil.oc.Settings
import net.minecraft.item.ItemStack
@ -11,22 +12,18 @@ class DebugCardData extends ItemData(Constants.ItemName.DebugCard) {
load(stack)
}
var player: Option[String] = None
var access: Option[AccessContext] = None
private final val DataTag = Settings.namespace + "data"
private final val PlayerTag = Settings.namespace + "player"
override def load(nbt: NBTTagCompound) {
val tag = dataTag(nbt)
if (tag.hasKey(PlayerTag)) {
player = Option(tag.getString(PlayerTag))
}
override def load(nbt: NBTTagCompound): Unit = {
access = AccessContext.load(dataTag(nbt))
}
override def save(nbt: NBTTagCompound) {
override def save(nbt: NBTTagCompound): Unit = {
val tag = dataTag(nbt)
tag.removeTag(PlayerTag)
player.foreach(tag.setString(PlayerTag, _))
AccessContext.remove(tag)
access.foreach(_.save(tag))
}
private def dataTag(nbt: NBTTagCompound) = {

View File

@ -9,5 +9,6 @@ object CommandHandler {
e.registerServerCommand(NonDisassemblyAgreementCommand)
e.registerServerCommand(WirelessRenderingCommand)
e.registerServerCommand(SpawnComputerCommand)
e.registerServerCommand(DebugWhitelistCommand)
}
}

View File

@ -0,0 +1,54 @@
package li.cil.oc.server.command
import li.cil.oc.Settings
import li.cil.oc.Settings.DebugCardAccess
import li.cil.oc.common.command.SimpleCommand
import net.minecraft.command.{ICommandSender, WrongUsageException}
import net.minecraft.server.MinecraftServer
import net.minecraft.util.text.TextComponentString
object DebugWhitelistCommand extends SimpleCommand("oc_debugWhitelist") {
// Required OP levels:
// to revoke your cards - 0
// to do other whitelist manipulation - 2
override def getRequiredPermissionLevel = 0
private def isOp(sender: ICommandSender) = getOpLevel(sender) >= 2
override def getCommandUsage(sender: ICommandSender): String =
if (isOp(sender)) name + " [revoke|add|remove] <player> OR " + name + " [revoke|list]"
else name + " revoke"
override def execute(server: MinecraftServer, sender: ICommandSender, args: Array[String]): Unit = {
val wl = Settings.get.debugCardAccess match {
case w: DebugCardAccess.Whitelist => w
case _ => throw new WrongUsageException("§cDebug card whitelisting is not enabled.")
}
def revokeUser(player: String): Unit = {
if (wl.isWhitelisted(player)) {
wl.invalidate(player)
sender.addChatMessage(new TextComponentString("§aAll your debug cards were invalidated."))
} else sender.addChatMessage(new TextComponentString("§cYou are not whitelisted to use debug card."))
}
args match {
case Array("revoke") => revokeUser(sender.getName)
case Array("revoke", player) if isOp(sender) => revokeUser(player)
case Array("list") if isOp(sender) =>
val players = wl.whitelist
if (players.nonEmpty)
sender.addChatMessage(new TextComponentString("§aCurrently whitelisted players: §e" + players.mkString(", ")))
else
sender.addChatMessage(new TextComponentString("§cThere is no currently whitelisted players."))
case Array("add", player) if isOp(sender) =>
wl.add(player)
sender.addChatMessage(new TextComponentString("§aPlayer was added to whitelist."))
case Array("remove", player) if isOp(sender) =>
wl.remove(player)
sender.addChatMessage(new TextComponentString("§aPlayer was removed from whitelist"))
case _ =>
sender.addChatMessage(new TextComponentString("§e" + getCommandUsage(sender)))
}
}
}

View File

@ -0,0 +1,31 @@
package li.cil.oc.server
import net.minecraft.command.ICommandSender
import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.server.MinecraftServer
import net.minecraft.util.text.ITextComponent
import net.minecraft.util.text.TextComponentString
import net.minecraftforge.fml.server.FMLServerHandler
import scala.language.implicitConversions
package object command {
implicit def string2text(s: String): ITextComponent = new TextComponentString(s)
def getOpLevel(sender: ICommandSender): Int = {
// Shitty minecraft server logic & shitty minecraft server code.
val srv = FMLServerHandler.instance().getServer
if (srv.isSinglePlayer && srv.worldServers.head.getWorldInfo.areCommandsAllowed &&
srv.getServerOwner.equalsIgnoreCase(sender.getName) /* || srv.commandsAllowedForAll */ )
return 4
sender match {
case _: MinecraftServer => 4
case p: EntityPlayerMP =>
val e = srv.getPlayerList.getOppedPlayers.getEntry(p.getGameProfile)
if (e == null) 0 else e.getPermissionLevel
case _ => 0
}
}
}

View File

@ -14,7 +14,7 @@ import li.cil.oc.api.network.SidedEnvironment
import li.cil.oc.api.network.Visibility
import li.cil.oc.api.prefab
import li.cil.oc.api.prefab.AbstractValue
import li.cil.oc.server.component.DebugCard.CommandSender
import li.cil.oc.server.component.DebugCard.{AccessContext, CommandSender}
import li.cil.oc.util.BlockPosition
import li.cil.oc.util.ExtendedArguments._
import li.cil.oc.util.ExtendedNBT._
@ -63,7 +63,9 @@ class DebugCard(host: EnvironmentHost) extends prefab.ManagedEnvironment {
private var remoteNodePosition: Option[(Int, Int, Int)] = None
// Player this card is bound to (if any) to use for permissions.
var player: Option[String] = None
implicit var access: Option[AccessContext] = None
def player = access.map(_.player)
private lazy val CommandSender = {
def defaultFakePlayer = FakePlayerFactory.get(host.world.asInstanceOf[WorldServer], Settings.get.fakePlayerProfile)
@ -75,67 +77,67 @@ class DebugCard(host: EnvironmentHost) extends prefab.ManagedEnvironment {
// ----------------------------------------------------------------------- //
import li.cil.oc.server.component.DebugCard.checkEnabled
import li.cil.oc.server.component.DebugCard.checkAccess
@Callback(doc = """function(value:number):number -- Changes the component network's energy buffer by the specified delta.""")
def changeBuffer(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
result(node.changeBuffer(args.checkDouble(0)))
}
@Callback(doc = """function():number -- Get the container's X position in the world.""")
def getX(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
result(host.xPosition)
}
@Callback(doc = """function():number -- Get the container's Y position in the world.""")
def getY(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
result(host.yPosition)
}
@Callback(doc = """function():number -- Get the container's Z position in the world.""")
def getZ(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
result(host.zPosition)
}
@Callback(doc = """function([id:number]):userdata -- Get the world object for the specified dimension ID, or the container's.""")
def getWorld(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
if (args.count() > 0) result(new DebugCard.WorldValue(DimensionManager.getWorld(args.checkInteger(0))))
else result(new DebugCard.WorldValue(host.world))
}
@Callback(doc = """function():table -- Get a list of all world IDs, loaded and unloaded.""")
def getWorlds(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
result(DimensionManager.getStaticDimensionIDs)
}
@Callback(doc = """function(name:string):userdata -- Get the entity of a player.""")
def getPlayer(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
result(new DebugCard.PlayerValue(args.checkString(0)))
}
@Callback(doc = """function():table -- Get a list of currently logged-in players.""")
def getPlayers(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
result(FMLCommonHandler.instance.getMinecraftServerInstance.getAllUsernames)
}
@Callback(doc = """function(name:string):boolean -- Get whether a mod or API is loaded.""")
def isModLoaded(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
val name = args.checkString(0)
result(Loader.isModLoaded(name) || ModAPIManager.INSTANCE.hasAPI(name))
}
@Callback(doc = """function(command:string):number -- Runs an arbitrary command using a fake player.""")
def runCommand(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
val commands =
if (args.isTable(0)) collectionAsScalaIterable(args.checkTable(0).values())
else Iterable(args.checkString(0))
@ -152,7 +154,7 @@ class DebugCard(host: EnvironmentHost) extends prefab.ManagedEnvironment {
@Callback(doc = """function(x:number, y:number, z:number):boolean -- Connect the debug card to the block at the specified coordinates.""")
def connectToBlock(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
val x = args.checkInteger(0)
val y = args.checkInteger(1)
val z = args.checkInteger(2)
@ -180,7 +182,7 @@ class DebugCard(host: EnvironmentHost) extends prefab.ManagedEnvironment {
@Callback(doc = """function():userdata -- Test method for user-data and general value conversion.""")
def test(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
val v1 = mutable.Map("a" -> true, "b" -> "test")
val v2 = Map(10 -> "zxc", false -> v1)
@ -218,42 +220,63 @@ class DebugCard(host: EnvironmentHost) extends prefab.ManagedEnvironment {
override def load(nbt: NBTTagCompound): Unit = {
super.load(nbt)
access = AccessContext.load(nbt)
if (nbt.hasKey(Settings.namespace + "remoteX")) {
val x = nbt.getInteger(Settings.namespace + "remoteX")
val y = nbt.getInteger(Settings.namespace + "remoteY")
val z = nbt.getInteger(Settings.namespace + "remoteZ")
remoteNodePosition = Some((x, y, z))
}
if (nbt.hasKey(Settings.namespace + "player")) {
player = Option(nbt.getString(Settings.namespace + "player"))
}
}
override def save(nbt: NBTTagCompound): Unit = {
super.save(nbt)
access.foreach(_.save(nbt))
remoteNodePosition.foreach {
case (x, y, z) =>
nbt.setInteger(Settings.namespace + "remoteX", x)
nbt.setInteger(Settings.namespace + "remoteY", y)
nbt.setInteger(Settings.namespace + "remoteZ", z)
}
player.foreach(nbt.setString(Settings.namespace + "player", _))
}
}
object DebugCard {
def checkAccess()(implicit ctx: Option[AccessContext]) =
for (msg <- Settings.get.debugCardAccess.checkAccess(ctx))
throw new Exception(msg)
import li.cil.oc.util.ResultWrapper.result
object AccessContext {
def remove(nbt: NBTTagCompound): Unit = {
nbt.removeTag(Settings.namespace + "player")
nbt.removeTag(Settings.namespace + "accessNonce")
}
def checkEnabled() = if (!Settings.get.enableDebugCard) throw new Exception("debug card functionality is disabled")
def load(nbt: NBTTagCompound): Option[AccessContext] = {
if (nbt.hasKey(Settings.namespace + "player"))
Some(AccessContext(
nbt.getString(Settings.namespace + "player"),
nbt.getString(Settings.namespace + "accessNonce")
))
else
None
}
}
class PlayerValue(var name: String) extends prefab.AbstractValue {
def this() = this("") // For loading.
case class AccessContext(player: String, nonce: String) {
def save(nbt: NBTTagCompound): Unit = {
nbt.setString(Settings.namespace + "player", player)
nbt.setString(Settings.namespace + "accessNonce", nonce)
}
}
class PlayerValue(var name: String)(implicit var ctx: Option[AccessContext]) extends prefab.AbstractValue {
def this() = this("")(None) // For loading.
// ----------------------------------------------------------------------- //
def withPlayer(f: (EntityPlayerMP) => Array[AnyRef]) = {
checkEnabled()
checkAccess()
FMLCommonHandler.instance.getMinecraftServerInstance.getPlayerList.getPlayerByUsername(name) match {
case player: EntityPlayerMP => f(player)
case _ => result(Unit, "player is offline")
@ -309,93 +332,95 @@ object DebugCard {
override def load(nbt: NBTTagCompound) {
super.load(nbt)
ctx = AccessContext.load(nbt)
name = nbt.getString(NameTag)
}
override def save(nbt: NBTTagCompound) {
super.save(nbt)
ctx.foreach(_.save(nbt))
nbt.setString(NameTag, name)
}
}
class WorldValue(var world: World) extends prefab.AbstractValue {
def this() = this(null) // For loading.
class WorldValue(var world: World)(implicit var ctx: Option[AccessContext]) extends prefab.AbstractValue {
def this() = this(null)(None) // For loading.
// ----------------------------------------------------------------------- //
@Callback(doc = """function():number -- Gets the numeric id of the current dimension.""")
def getDimensionId(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
result(world.provider.getDimension)
}
@Callback(doc = """function():string -- Gets the name of the current dimension.""")
def getDimensionName(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
result(world.provider.getDimensionType.getName)
}
@Callback(doc = """function():number -- Gets the seed of the world.""")
def getSeed(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
result(world.getSeed)
}
@Callback(doc = """function():boolean -- Returns whether it is currently raining.""")
def isRaining(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
result(world.isRaining)
}
@Callback(doc = """function(value:boolean) -- Sets whether it is currently raining.""")
def setRaining(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
world.getWorldInfo.setRaining(args.checkBoolean(0))
null
}
@Callback(doc = """function():boolean -- Returns whether it is currently thundering.""")
def isThundering(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
result(world.isThundering)
}
@Callback(doc = """function(value:boolean) -- Sets whether it is currently thundering.""")
def setThundering(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
world.getWorldInfo.setThundering(args.checkBoolean(0))
null
}
@Callback(doc = """function():number -- Get the current world time.""")
def getTime(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
result(world.getWorldTime)
}
@Callback(doc = """function(value:number) -- Set the current world time.""")
def setTime(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
world.setWorldTime(args.checkDouble(0).toLong)
null
}
@Callback(doc = """function():number, number, number -- Get the current spawn point coordinates.""")
def getSpawnPoint(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
result(world.getWorldInfo.getSpawnX, world.getWorldInfo.getSpawnY, world.getWorldInfo.getSpawnZ)
}
@Callback(doc = """function(x:number, y:number, z:number) -- Set the spawn point coordinates.""")
def setSpawnPoint(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
world.getWorldInfo.setSpawn(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2)))
null
}
@Callback(doc = """function(x:number, y:number, z:number, sound:string, range:number) -- Play a sound at the specified coordinates.""")
def playSoundAt(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
val (x, y, z) = (args.checkInteger(0), args.checkInteger(1), args.checkInteger(2))
val sound = args.checkString(3)
val range = args.checkInteger(4)
@ -407,25 +432,25 @@ object DebugCard {
@Callback(doc = """function(x:number, y:number, z:number):number -- Get the ID of the block at the specified coordinates.""")
def getBlockId(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
result(Block.getIdFromBlock(world.getBlockState(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2))).getBlock))
}
@Callback(doc = """function(x:number, y:number, z:number):number -- Get the metadata of the block at the specified coordinates.""")
def getMetadata(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
result(world.getBlockState(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2))))
}
@Callback(doc = """function(x:number, y:number, z:number):number -- Check whether the block at the specified coordinates is loaded.""")
def isLoaded(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
result(world.isBlockLoaded(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2))))
}
@Callback(doc = """function(x:number, y:number, z:number):number -- Check whether the block at the specified coordinates has a tile entity.""")
def hasTileEntity(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
val blockPos = new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2))
val state = world.getBlockState(blockPos)
result(state.getBlock.hasTileEntity(state))
@ -433,7 +458,7 @@ object DebugCard {
@Callback(doc = """function(x:number, y:number, z:number):table -- Get the NBT of the block at the specified coordinates.""")
def getTileNBT(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
val blockPos = new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2))
world.getTileEntity(blockPos) match {
case tileEntity: TileEntity => result(toNbt((nbt) => tileEntity.writeToNBT(nbt): Unit).toTypedMap)
@ -443,7 +468,7 @@ object DebugCard {
@Callback(doc = """function(x:number, y:number, z:number, nbt:table):boolean -- Set the NBT of the block at the specified coordinates.""")
def setTileNBT(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
val blockPos = new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2))
world.getTileEntity(blockPos) match {
case tileEntity: TileEntity =>
@ -461,25 +486,25 @@ object DebugCard {
@Callback(doc = """function(x:number, y:number, z:number):number -- Get the light opacity of the block at the specified coordinates.""")
def getLightOpacity(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
result(world.getBlockLightOpacity(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2))))
}
@Callback(doc = """function(x:number, y:number, z:number):number -- Get the light value (emission) of the block at the specified coordinates.""")
def getLightValue(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
result(world.getLight(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2)), false))
}
@Callback(doc = """function(x:number, y:number, z:number):number -- Get whether the block at the specified coordinates is directly under the sky.""")
def canSeeSky(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
result(world.canBlockSeeSky(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2))))
}
@Callback(doc = """function(x:number, y:number, z:number, id:number or string, meta:number):number -- Set the block at the specified coordinates.""")
def setBlock(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
val block = if (args.isInteger(3)) Block.getBlockById(args.checkInteger(3)) else Block.getBlockFromName(args.checkString(3))
val metadata = args.checkInteger(4)
result(world.setBlockState(new BlockPos(args.checkInteger(0), args.checkInteger(1), args.checkInteger(2)), block.getStateFromMeta(metadata)))
@ -487,7 +512,7 @@ object DebugCard {
@Callback(doc = """function(x1:number, y1:number, z1:number, x2:number, y2:number, z2:number, id:number or string, meta:number):number -- Set all blocks in the area defined by the two corner points (x1, y1, z1) and (x2, y2, z2).""")
def setBlocks(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
val (xMin, yMin, zMin) = (args.checkInteger(0), args.checkInteger(1), args.checkInteger(2))
val (xMax, yMax, zMax) = (args.checkInteger(3), args.checkInteger(4), args.checkInteger(5))
val block = if (args.isInteger(6)) Block.getBlockById(args.checkInteger(6)) else Block.getBlockFromName(args.checkString(6))
@ -506,7 +531,7 @@ object DebugCard {
@Callback(doc = """function(id:string, count:number, damage:number, nbt:string, x:number, y:number, z:number, side:number):boolean - Insert an item stack into the inventory at the specified location. NBT tag is expected in JSON format.""")
def insertItem(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
val item = Item.REGISTRY.getObject(new ResourceLocation(args.checkString(0)))
if (item == null) {
throw new IllegalArgumentException("invalid item id")
@ -528,7 +553,7 @@ object DebugCard {
@Callback(doc = """function(x:number, y:number, z:number, slot:number[, count:number]):number - Reduce the size of an item stack in the inventory at the specified location.""")
def removeItem(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
val position = BlockPosition(args.checkDouble(0), args.checkDouble(1), args.checkDouble(2), world)
InventoryUtils.inventoryAt(position) match {
case Some(inventory) =>
@ -543,7 +568,7 @@ object DebugCard {
@Callback(doc = """function(id:string, amount:number, x:number, y:number, z:number, side:number):boolean - Insert some fluid into the tank at the specified location.""")
def insertFluid(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
val fluid = FluidRegistry.getFluid(args.checkString(0))
if (fluid == null) {
throw new IllegalArgumentException("invalid fluid id")
@ -559,7 +584,7 @@ object DebugCard {
@Callback(doc = """function(amount:number, x:number, y:number, z:number, side:number):boolean - Remove some fluid from a tank at the specified location.""")
def removeFluid(context: Context, args: Arguments): Array[AnyRef] = {
checkEnabled()
checkAccess()
val amount = args.checkInteger(0)
val position = BlockPosition(args.checkDouble(1), args.checkDouble(2), args.checkDouble(3), world)
val side = args.checkSideAny(4)
@ -575,11 +600,13 @@ object DebugCard {
override def load(nbt: NBTTagCompound) {
super.load(nbt)
ctx = AccessContext.load(nbt)
world = DimensionManager.getWorld(nbt.getInteger(DimensionTag))
}
override def save(nbt: NBTTagCompound) {
super.save(nbt)
ctx.foreach(_.save(nbt))
nbt.setInteger(DimensionTag, world.provider.getDimension)
}
}

View File

@ -3,11 +3,12 @@ package li.cil.oc.server.component
import java.util
import li.cil.oc.Constants
import li.cil.oc.api.driver.DeviceInfo.DeviceAttribute
import li.cil.oc.api.driver.DeviceInfo.DeviceClass
import li.cil.oc.Settings
import li.cil.oc.api
import li.cil.oc.api.driver.DeviceInfo
import li.cil.oc.api.driver.DeviceInfo.DeviceAttribute
import li.cil.oc.api.driver.DeviceInfo.DeviceClass
import li.cil.oc.api.event.SignChangeEvent
import li.cil.oc.api.internal
import li.cil.oc.api.network.EnvironmentHost
import li.cil.oc.api.network.Message
@ -54,13 +55,19 @@ abstract class UpgradeSign extends prefab.ManagedEnvironment with DeviceInfo {
case robot: internal.Robot => robot.player
case _ => FakePlayerFactory.get(host.world.asInstanceOf[WorldServer], Settings.get.fakePlayerProfile)
}
if (!canChangeSign(player, sign)) {
val lines = text.lines.padTo(4, "").map(line => if (line.length > 15) line.substring(0, 15) else line).toArray
if (!canChangeSign(player, sign, lines)) {
return result(Unit, "not allowed")
}
text.lines.padTo(4, "").map(line => if (line.length > 15) line.substring(0, 15) else line).map(new TextComponentString(_)).copyToArray(sign.signText)
lines.map(line => new TextComponentString(line)).copyToArray(sign.signText)
host.world.notifyBlockUpdate(sign.getPos)
result(sign.signText.map(_.getUnformattedText).mkString("\n"))
MinecraftForge.EVENT_BUS.post(new SignChangeEvent.Post(sign, lines))
result(sign.signText.mkString("\n"))
case _ => result(Unit, "no sign")
}
}
@ -76,13 +83,19 @@ abstract class UpgradeSign extends prefab.ManagedEnvironment with DeviceInfo {
}
}
private def canChangeSign(player: EntityPlayer, tileEntity: TileEntitySign): Boolean = {
private def canChangeSign(player: EntityPlayer, tileEntity: TileEntitySign, lines: Array[String]): Boolean = {
if (!host.world.isBlockModifiable(player, tileEntity.getPos)) {
return false
}
val event = new BlockEvent.BreakEvent(host.world, tileEntity.getPos, tileEntity.getWorld.getBlockState(tileEntity.getPos), player)
MinecraftForge.EVENT_BUS.post(event)
!(event.isCanceled || event.getResult == Event.Result.DENY)
if (event.isCanceled || event.getResult == Event.Result.DENY) {
return false
}
val signEvent = new SignChangeEvent.Pre(tileEntity, lines)
MinecraftForge.EVENT_BUS.post(signEvent)
!(signEvent.isCanceled || signEvent.getResult == Event.Result.DENY)
}
override def onMessage(message: Message): Unit = {