Add half-done command parsers for /fill, /give, /playsound, /clone, /blockdata
Register command parsers using a decorator
This commit is contained in:
parent
e2f41bac1e
commit
d1e2a7680e
@ -10,40 +10,41 @@ from mceditlib.selection import BoundingBox
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
"""
|
||||
/fill Fills a region with a specific block. Op — Blocks — — —
|
||||
/give Gives an item to a player. Op — — — Players —
|
||||
/playsound Plays a sound. Op — — — Players —
|
||||
/blockdata Modifies the data tag of a block. Op — Blocks — — —
|
||||
/clone Copies blocks from one place to another. Op — Blocks — — —
|
||||
/execute Executes another command. Op — — — — —
|
||||
/setblock Changes a block to another block. Op — Blocks — — —
|
||||
/summon Summons an entity. Op — — Entities — —
|
||||
/testforblock Tests whether a block is in a location. Op — Blocks — — —
|
||||
|
||||
/achievement Gives or removes an achievement from a player. Op — — — Players —
|
||||
/blockdata Modifies the data tag of a block. Op — Blocks — — —
|
||||
/clear Clears items from player inventory. Op — — — Players —
|
||||
/clone Copies blocks from one place to another. Op — Blocks — — —
|
||||
/defaultgamemode Sets the default game mode. Op — — — — World
|
||||
/difficulty Sets the difficulty level. Op — — — Players —
|
||||
/effect Add or remove status effects. Op — — Entities Players —
|
||||
/enchant Enchants a player item. Op — — — Players —
|
||||
/entitydata Modifies the data tag of an entity. Op — — Entities — —
|
||||
/execute Executes another command. Op — — — — —
|
||||
/fill Fills a region with a specific block. Op — Blocks — — —
|
||||
/gamemode Sets a player's game mode. Op — — — Players —
|
||||
/gamerule Sets or queries a game rule value. Op — — — — World
|
||||
/give Gives an item to a player. Op — — — Players —
|
||||
/help Provides help for commands. — — — — — —
|
||||
/kill Kills entities (players, mobs, items, etc.). Op — — Entities Players —
|
||||
/list Lists players on the server. Op MP — — Players —
|
||||
/me Displays a message about yourself. — — — — Players —
|
||||
/particle Creates particles. Op — — — Players —
|
||||
/playsound Plays a sound. Op — — — Players —
|
||||
/replaceitem Replaces items in inventories. Op — Blocks Entities Players —
|
||||
/say Displays a message to multiple players. Op — — — — —
|
||||
/scoreboard Manages objectives, players, and teams. Op — — Entities Players —
|
||||
/seed Displays the world seed. Op — — — — World
|
||||
/setblock Changes a block to another block. Op — Blocks — — —
|
||||
/setworldspawn Sets the world spawn. Op — — — — World
|
||||
/spawnpoint Sets the spawn point for a player. Op — — — Players —
|
||||
/spreadplayers Teleports entities to random locations. Op — — Entities Players —
|
||||
/stats Update objectives from command results. Op — Blocks Entities Players —
|
||||
/summon Summons an entity. Op — — Entities — —
|
||||
/tell Displays a private message to other players. — — — — Players —
|
||||
/tellraw Displays a JSON message to players. Op — — — Players —
|
||||
/testfor Counts entities matching specified conditions. Op — — Entities Players —
|
||||
/testforblock Tests whether a block is in a location. Op — Blocks — — —
|
||||
/testforblocks Tests whether the blocks in two regions match. Op — Blocks — — —
|
||||
/time Changes or queries the world's game time. Op — — — — World
|
||||
/title Manages screen titles. Op — — — Players —
|
||||
@ -78,7 +79,11 @@ def ParseCommand(commandText):
|
||||
if cmdClass is None:
|
||||
return UnknownCommand(name, args)
|
||||
else:
|
||||
return cmdClass(args)
|
||||
try:
|
||||
return cmdClass(args)
|
||||
except Exception as e:
|
||||
log.warn("Parse error while parsing command %r", commandText)
|
||||
raise
|
||||
|
||||
|
||||
def parseCoord(text):
|
||||
@ -170,6 +175,13 @@ def argsplit(args, numargs, required=0):
|
||||
args = args + [""] * (numargs - len(args))
|
||||
return args
|
||||
|
||||
|
||||
def argpop(args):
|
||||
"""
|
||||
Pop one arg off the front of args and return (arg, rest)
|
||||
"""
|
||||
return argsplit(args, 2)
|
||||
|
||||
class TargetSelector(object):
|
||||
playerName = None
|
||||
targetVariable = None
|
||||
@ -195,7 +207,11 @@ class TargetSelector(object):
|
||||
|
||||
selectorText = ",".join(implicitArgs + selectorArgs)
|
||||
|
||||
return "@%s[%s]" % (self.targetVariable, selectorText)
|
||||
ret = "@" + self.targetVariable
|
||||
if selectorText:
|
||||
ret += "[%s]" % (selectorText,)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def __init__(self, selectorText):
|
||||
@ -205,27 +221,27 @@ class TargetSelector(object):
|
||||
|
||||
targetVariable = selectorText[1]
|
||||
targetArgText = selectorText[2:]
|
||||
|
||||
if targetArgText[0] != '[' and targetArgText[-1] != ']':
|
||||
raise ParseError("Selector arguments must be enclosed in [].")
|
||||
|
||||
targetArgText = targetArgText[1:-1]
|
||||
targetArgs = targetArgText.split(",")
|
||||
|
||||
parsedArgs = []
|
||||
implicitKeys = list('xyzr')
|
||||
if len(targetArgText):
|
||||
if targetArgText[0] != '[' and targetArgText[-1] != ']':
|
||||
raise ParseError("Selector arguments must be enclosed in [].")
|
||||
|
||||
for arg in targetArgs:
|
||||
if '=' not in arg:
|
||||
if len(implicitKeys):
|
||||
key = implicitKeys.pop(0)
|
||||
value = arg
|
||||
targetArgText = targetArgText[1:-1]
|
||||
targetArgs = targetArgText.split(",")
|
||||
|
||||
implicitKeys = list('xyzr')
|
||||
|
||||
for arg in targetArgs:
|
||||
if '=' not in arg:
|
||||
if len(implicitKeys):
|
||||
key = implicitKeys.pop(0)
|
||||
value = arg
|
||||
else:
|
||||
raise ParseError("Selector argument must be in the form key=value.")
|
||||
else:
|
||||
raise ParseError("Selector argument must be in the form key=value.")
|
||||
else:
|
||||
key, value = arg.split('=', 1)
|
||||
key, value = arg.split('=', 1)
|
||||
|
||||
parsedArgs.append((key, value))
|
||||
parsedArgs.append((key, value))
|
||||
|
||||
self.targetVariable = targetVariable
|
||||
self.targetArgs = parsedArgs
|
||||
@ -260,30 +276,47 @@ def resolvePosition(point, x, relX, y, relY, z, relZ):
|
||||
|
||||
return x, y, z
|
||||
|
||||
class PositionalCommand(object):
|
||||
x = y = z = 0
|
||||
relX = relY = relZ = False
|
||||
|
||||
def resolvePosition(self, point):
|
||||
return resolvePosition(point, self.x, self.relX, self.y, self.relY, self.z, self.relZ)
|
||||
|
||||
class CloneCommand(object):
|
||||
name = "clone"
|
||||
maskMode = "replace"
|
||||
cloneMode = "normal"
|
||||
tileName = None
|
||||
|
||||
_commandClasses = {}
|
||||
|
||||
def register_command(cls):
|
||||
_commandClasses[cls.name] = cls
|
||||
return cls
|
||||
|
||||
@register_command
|
||||
class GiveCommand(object):
|
||||
name = "give"
|
||||
amount = None
|
||||
data = None
|
||||
dataTag = None
|
||||
|
||||
def __str__(self):
|
||||
raise NotImplementedError("Implement me!!")
|
||||
|
||||
def __init__(self, args):
|
||||
target, item, rest = argsplit(args, 3, required=2)
|
||||
self.targetSelector = TargetSelector(target)
|
||||
self.item = item
|
||||
|
||||
self.amount, rest = argpop(rest)
|
||||
if rest:
|
||||
self.data, rest = argpop(rest)
|
||||
if rest:
|
||||
self.dataTag = rest
|
||||
|
||||
class BoundingBoxCommand(object):
|
||||
|
||||
def resolveS1(self, point):
|
||||
return resolvePosition(point,
|
||||
self.sx1, self.relSX1,
|
||||
self.sy1, self.relSY1,
|
||||
self.sz1, self.relSZ1)
|
||||
return resolvePosition(point,
|
||||
self.x1, self.relX1,
|
||||
self.y1, self.relY1,
|
||||
self.z1, self.relZ1)
|
||||
|
||||
def resolveS2(self, point):
|
||||
return resolvePosition(point,
|
||||
self.sx2, self.relSX2,
|
||||
self.sy2, self.relSY2,
|
||||
self.sz2, self.relSZ2)
|
||||
return resolvePosition(point,
|
||||
self.x2, self.relX2,
|
||||
self.y2, self.relY2,
|
||||
self.z2, self.relZ2)
|
||||
|
||||
def resolveDestination(self, point):
|
||||
return resolvePosition(point,
|
||||
@ -291,19 +324,54 @@ class CloneCommand(object):
|
||||
self.dy, self.relDY,
|
||||
self.dz, self.relDZ)
|
||||
|
||||
def resolveSourceBounds(self, point):
|
||||
def resolveBoundingBox(self, point):
|
||||
sourceP1 = self.resolveS1(point)
|
||||
sourceP2 = self.resolveS2(point)
|
||||
return BoundingBox(sourceP1, (1, 1, 1)).union(BoundingBox(sourceP2, (1, 1, 1)))
|
||||
|
||||
def parseCoordPair(self, x1, y1, z1, x2, y2, z2):
|
||||
self.x1, self.relX1 = parseCoord(x1)
|
||||
self.y1, self.relY1 = parseCoord(y1)
|
||||
self.z1, self.relZ1 = parseCoord(z1)
|
||||
self.x2, self.relX2 = parseCoord(x2)
|
||||
self.y2, self.relY2 = parseCoord(y2)
|
||||
self.z2, self.relZ2 = parseCoord(z2)
|
||||
|
||||
x1 = y1 = z1 = None
|
||||
relX1 = relY1 = relZ1 = False
|
||||
|
||||
x2 = y2 = z2 = None
|
||||
relX2 = relY2 = relZ2 = False
|
||||
|
||||
|
||||
@register_command
|
||||
class FillCommand(BoundingBoxCommand):
|
||||
name = "fill"
|
||||
|
||||
def __str__(self):
|
||||
args = formatCoordTuple(self.sx1, self.relSX1,
|
||||
self.sy1, self.relSY1,
|
||||
self.sz1, self.relSZ1)
|
||||
raise NotImplementedError
|
||||
|
||||
def __init__(self, args):
|
||||
x1, y1, z1, x2, y2, z2, tileName, rest = argsplit(args, 8, required=7)
|
||||
self.parseCoordPair(x1, y1, z1, x2, y2, z2)
|
||||
self.tileName = tileName
|
||||
|
||||
|
||||
@register_command
|
||||
class CloneCommand(BoundingBoxCommand):
|
||||
name = "clone"
|
||||
maskMode = "replace"
|
||||
cloneMode = "normal"
|
||||
tileName = None
|
||||
|
||||
def __str__(self):
|
||||
args = formatCoordTuple(self.x1, self.relX1,
|
||||
self.y1, self.relY1,
|
||||
self.z1, self.relZ1)
|
||||
args += " "
|
||||
args += formatCoordTuple(self.sx2, self.relSX2,
|
||||
self.sy2, self.relSY2,
|
||||
self.sz2, self.relSZ2)
|
||||
args += formatCoordTuple(self.x2, self.relX2,
|
||||
self.y2, self.relY2,
|
||||
self.z2, self.relZ2)
|
||||
args += " "
|
||||
args += formatCoordTuple(self.dx, self.relDX,
|
||||
self.dy, self.relDY,
|
||||
@ -319,14 +387,10 @@ class CloneCommand(object):
|
||||
return "/%s %s" % (self.name, args)
|
||||
|
||||
def __init__(self, args):
|
||||
sx1, sy1, sz1, sx2, sy2, sz2, dx, dy, dz, rest = argsplit(args, 10, required=9)
|
||||
x1, y1, z1, x2, y2, z2, dx, dy, dz, rest = argsplit(args, 10, required=9)
|
||||
|
||||
self.parseCoordPair(x1, y1, z1, x2, y2, z2)
|
||||
|
||||
self.sx1, self.relSX1 = parseCoord(sx1)
|
||||
self.sy1, self.relSY1 = parseCoord(sy1)
|
||||
self.sz1, self.relSZ1 = parseCoord(sz1)
|
||||
self.sx2, self.relSX2 = parseCoord(sx2)
|
||||
self.sy2, self.relSY2 = parseCoord(sy2)
|
||||
self.sz2, self.relSZ2 = parseCoord(sz2)
|
||||
self.dx, self.relDX = parseCoord(dx)
|
||||
self.dy, self.relDY = parseCoord(dy)
|
||||
self.dz, self.relDZ = parseCoord(dz)
|
||||
@ -351,8 +415,53 @@ class CloneCommand(object):
|
||||
raise ParseError("Filtered clone command must have tileName as the last argument.")
|
||||
|
||||
|
||||
class PositionalCommand(object):
|
||||
x = y = z = 0
|
||||
relX = relY = relZ = False
|
||||
|
||||
def resolvePosition(self, point):
|
||||
return resolvePosition(point, self.x, self.relX, self.y, self.relY, self.z, self.relZ)
|
||||
|
||||
|
||||
@register_command
|
||||
class PlaySoundCommand(PositionalCommand):
|
||||
name = "playsound"
|
||||
|
||||
def __str__(self):
|
||||
raise NotImplementedError("Implement me!!")
|
||||
|
||||
def __init__(self, args):
|
||||
self.sound, target, rest = argsplit(args, 3, required=2)
|
||||
self.targetSelector = TargetSelector(target)
|
||||
|
||||
if rest:
|
||||
x, y, z, rest = argsplit(rest, 4)
|
||||
self.x, self.relX = parseCoord(x)
|
||||
self.y, self.relY = parseCoord(y)
|
||||
self.z, self.relZ = parseCoord(z)
|
||||
if rest:
|
||||
self.volume, self.pitch, self.minimumVolume = argsplit(rest, 3)
|
||||
|
||||
|
||||
@register_command
|
||||
class BlockdataCommand(PositionalCommand):
|
||||
name = "blockdata"
|
||||
|
||||
def __str__(self):
|
||||
args = formatCoords(self)
|
||||
args += " " + self.dataTag
|
||||
|
||||
return "/%s %s" % (self.name, args)
|
||||
|
||||
def __init__(self, args):
|
||||
x, y, z, dataTag = argsplit(args, 4, required=4)
|
||||
self.x, self.relX = parseCoord(x)
|
||||
self.y, self.relY = parseCoord(y)
|
||||
self.z, self.relZ = parseCoord(z)
|
||||
self.dataTag = dataTag
|
||||
|
||||
|
||||
@register_command
|
||||
class ExecuteCommand(PositionalCommand):
|
||||
name = "execute"
|
||||
subcommand = None
|
||||
@ -398,6 +507,7 @@ class ExecuteCommand(PositionalCommand):
|
||||
self.subcommand = ParseCommand(commandText)
|
||||
|
||||
|
||||
@register_command
|
||||
class SetBlockCommand(PositionalCommand):
|
||||
name = "setblock"
|
||||
dataValue = -1
|
||||
@ -445,6 +555,7 @@ class SetBlockCommand(PositionalCommand):
|
||||
self.dataTag = dataTag
|
||||
|
||||
|
||||
@register_command
|
||||
class TestForBlockCommand(PositionalCommand):
|
||||
name = "testforblock"
|
||||
dataTag = ""
|
||||
@ -481,6 +592,7 @@ class TestForBlockCommand(PositionalCommand):
|
||||
self.dataTag = dataTag
|
||||
|
||||
|
||||
@register_command
|
||||
class SummonCommand(PositionalCommand):
|
||||
name = "summon"
|
||||
entityName = NotImplemented
|
||||
@ -525,20 +637,6 @@ class SummonCommand(PositionalCommand):
|
||||
self.z = z
|
||||
|
||||
|
||||
_commandClasses = {}
|
||||
|
||||
|
||||
def addCommandClass(cls):
|
||||
_commandClasses[cls.name] = cls
|
||||
|
||||
_cc = [SummonCommand, TestForBlockCommand, SetBlockCommand, ExecuteCommand, CloneCommand]
|
||||
|
||||
for cc in _cc:
|
||||
addCommandClass(cc)
|
||||
|
||||
del _cc
|
||||
|
||||
|
||||
def main():
|
||||
testCommands = [
|
||||
r'/summon Zombie 2.5 63 -626.5 {Health:18,HealF:18,IsVillager:0,Attributes:[{Name:"generic.followRange",Base:6},{Name:"generic.movementSpeed",Base:0.2},{Name:"generic.knockbackResistance",Base:1.0}],Equipment:[{id:283},{},{},{},{id:332, Age:5980}],PersistenceRequired:1}',
|
||||
|
Reference in New Issue
Block a user