211 lines
7.3 KiB
Python
211 lines
7.3 KiB
Python
from .connection import Connection
|
|
from .vec3 import Vec3
|
|
from .event import BlockEvent, ChatEvent
|
|
from .block import Block
|
|
import math
|
|
from .util import flatten
|
|
|
|
""" Minecraft PI low level api v0.1_1
|
|
|
|
Note: many methods have the parameter *arg. This solution makes it
|
|
simple to allow different types, and variable number of arguments.
|
|
The actual magic is a mix of flatten_parameters() and __iter__. Example:
|
|
A Cube class could implement __iter__ to work in Minecraft.setBlocks(c, id).
|
|
|
|
(Because of this, it's possible to "erase" arguments. CmdPlayer removes
|
|
entityId, by injecting [] that flattens to nothing)
|
|
|
|
@author: Aron Nieminen, Mojang AB"""
|
|
|
|
""" Updated to include functionality provided by RaspberryJuice:
|
|
- getBlocks()
|
|
- getDirection()
|
|
- getPitch()
|
|
- getRotation()
|
|
- getPlayerEntityId()
|
|
- pollChatPosts() """
|
|
|
|
def intFloor(*args):
|
|
return [int(math.floor(x)) for x in flatten(args)]
|
|
|
|
class CmdPositioner:
|
|
"""Methods for setting and getting positions"""
|
|
def __init__(self, connection, packagePrefix):
|
|
self.conn = connection
|
|
self.pkg = packagePrefix
|
|
|
|
def getPos(self, id):
|
|
"""Get entity position (entityId:int) => Vec3"""
|
|
s = self.conn.sendReceive(self.pkg + b".getPos", id)
|
|
return Vec3(*list(map(float, s.split(","))))
|
|
|
|
def setPos(self, id, *args):
|
|
"""Set entity position (entityId:int, x,y,z)"""
|
|
self.conn.send(self.pkg + b".setPos", id, args)
|
|
|
|
def getTilePos(self, id):
|
|
"""Get entity tile position (entityId:int) => Vec3"""
|
|
s = self.conn.sendReceive(self.pkg + b".getTile", id)
|
|
return Vec3(*list(map(int, s.split(","))))
|
|
|
|
def setTilePos(self, id, *args):
|
|
"""Set entity tile position (entityId:int) => Vec3"""
|
|
self.conn.send(self.pkg + b".setTile", id, intFloor(*args))
|
|
|
|
def getDirection(self, id):
|
|
"""Get entity direction (entityId:int) => Vec3"""
|
|
s = self.conn.sendReceive(self.pkg + b".getDirection", id)
|
|
return Vec3(*map(float, s.split(",")))
|
|
|
|
def getRotation(self, id):
|
|
"""get entity rotation (entityId:int) => float"""
|
|
return float(self.conn.sendReceive(self.pkg + b".getRotation", id))
|
|
|
|
def getPitch(self, id):
|
|
"""get entity pitch (entityId:int) => float"""
|
|
return float(self.conn.sendReceive(self.pkg + b".getPitch", id))
|
|
|
|
def setting(self, setting, status):
|
|
"""Set a player setting (setting, status). keys: autojump"""
|
|
self.conn.send(self.pkg + b".setting", setting, 1 if bool(status) else 0)
|
|
|
|
|
|
class CmdEntity(CmdPositioner):
|
|
"""Methods for entities"""
|
|
def __init__(self, connection):
|
|
CmdPositioner.__init__(self, connection, b"entity")
|
|
|
|
|
|
class CmdPlayer(CmdPositioner):
|
|
"""Methods for the host (Raspberry Pi) player"""
|
|
def __init__(self, connection):
|
|
CmdPositioner.__init__(self, connection, b"player")
|
|
self.conn = connection
|
|
|
|
def getPos(self):
|
|
return CmdPositioner.getPos(self, [])
|
|
def setPos(self, *args):
|
|
return CmdPositioner.setPos(self, [], args)
|
|
def getTilePos(self):
|
|
return CmdPositioner.getTilePos(self, [])
|
|
def setTilePos(self, *args):
|
|
return CmdPositioner.setTilePos(self, [], args)
|
|
def getDirection(self):
|
|
return CmdPositioner.getDirection(self, [])
|
|
def getRotation(self):
|
|
return CmdPositioner.getRotation(self, [])
|
|
def getPitch(self):
|
|
return CmdPositioner.getPitch(self, [])
|
|
|
|
class CmdCamera:
|
|
def __init__(self, connection):
|
|
self.conn = connection
|
|
|
|
def setNormal(self, *args):
|
|
"""Set camera mode to normal Minecraft view ([entityId])"""
|
|
self.conn.send(b"camera.mode.setNormal", args)
|
|
|
|
def setFixed(self):
|
|
"""Set camera mode to fixed view"""
|
|
self.conn.send(b"camera.mode.setFixed")
|
|
|
|
def setFollow(self, *args):
|
|
"""Set camera mode to follow an entity ([entityId])"""
|
|
self.conn.send(b"camera.mode.setFollow", args)
|
|
|
|
def setPos(self, *args):
|
|
"""Set camera entity position (x,y,z)"""
|
|
self.conn.send(b"camera.setPos", args)
|
|
|
|
|
|
class CmdEvents:
|
|
"""Events"""
|
|
def __init__(self, connection):
|
|
self.conn = connection
|
|
|
|
def clearAll(self):
|
|
"""Clear all old events"""
|
|
self.conn.send(b"events.clear")
|
|
|
|
def pollBlockHits(self):
|
|
"""Only triggered by sword => [BlockEvent]"""
|
|
s = self.conn.sendReceive(b"events.block.hits")
|
|
events = [e for e in s.split("|") if e]
|
|
return [BlockEvent.Hit(*list(map(int, e.split(",")))) for e in events]
|
|
|
|
def pollChatPosts(self):
|
|
"""Triggered by posts to chat => [ChatEvent]"""
|
|
s = self.conn.sendReceive(b"events.chat.posts")
|
|
events = [e for e in s.split("|") if e]
|
|
return [ChatEvent.Post(int(e[:e.find(",")]), e[e.find(",") + 1:]) for e in events]
|
|
|
|
class Minecraft:
|
|
"""The main class to interact with a running instance of Minecraft Pi."""
|
|
def __init__(self, connection):
|
|
self.conn = connection
|
|
|
|
self.camera = CmdCamera(connection)
|
|
self.entity = CmdEntity(connection)
|
|
self.player = CmdPlayer(connection)
|
|
self.events = CmdEvents(connection)
|
|
|
|
def getBlock(self, *args):
|
|
"""Get block (x,y,z) => id:int"""
|
|
return int(self.conn.sendReceive(b"world.getBlock", intFloor(args)))
|
|
|
|
def getBlockWithData(self, *args):
|
|
"""Get block with data (x,y,z) => Block"""
|
|
ans = self.conn.sendReceive(b"world.getBlockWithData", intFloor(args))
|
|
return Block(*list(map(int, ans.split(","))))
|
|
|
|
def getBlocks(self, *args):
|
|
"""Get a cuboid of blocks (x0,y0,z0,x1,y1,z1) => [id:int]"""
|
|
s = self.conn.sendReceive(b"world.getBlocks", intFloor(args))
|
|
return map(int, s.split(","))
|
|
|
|
def setBlock(self, *args):
|
|
"""Set block (x,y,z,id,[data])"""
|
|
self.conn.send(b"world.setBlock", intFloor(args))
|
|
|
|
def setBlocks(self, *args):
|
|
"""Set a cuboid of blocks (x0,y0,z0,x1,y1,z1,id,[data])"""
|
|
self.conn.send(b"world.setBlocks", intFloor(args))
|
|
|
|
def getHeight(self, *args):
|
|
"""Get the height of the world (x,z) => int"""
|
|
return int(self.conn.sendReceive(b"world.getHeight", intFloor(args)))
|
|
|
|
def getPlayerEntityIds(self):
|
|
"""Get the entity ids of the connected players => [id:int]"""
|
|
ids = self.conn.sendReceive(b"world.getPlayerIds")
|
|
return list(map(int, ids.split("|")))
|
|
|
|
def getPlayerEntityId(self, name):
|
|
"""Get the entity id of the named player => [id:int]"""
|
|
return int(self.conn.sendReceive(b"world.getPlayerId", name))
|
|
|
|
def saveCheckpoint(self):
|
|
"""Save a checkpoint that can be used for restoring the world"""
|
|
self.conn.send(b"world.checkpoint.save")
|
|
|
|
def restoreCheckpoint(self):
|
|
"""Restore the world state to the checkpoint"""
|
|
self.conn.send(b"world.checkpoint.restore")
|
|
|
|
def postToChat(self, msg):
|
|
"""Post a message to the game chat"""
|
|
self.conn.send(b"chat.post", msg)
|
|
|
|
def setting(self, setting, status):
|
|
"""Set a world setting (setting, status). keys: world_immutable, nametags_visible"""
|
|
self.conn.send(b"world.setting", setting, 1 if bool(status) else 0)
|
|
|
|
@staticmethod
|
|
def create(address = "localhost", port = 4711):
|
|
return Minecraft(Connection(address, port))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
mc = Minecraft.create()
|
|
mc.postToChat("Hello, Minecraft!")
|