From fc0a9005e6e059815083795bdcad2aa57fc0b8f5 Mon Sep 17 00:00:00 2001 From: Tim <85190511+sm-timmy@users.noreply.github.com> Date: Mon, 26 Feb 2024 22:00:07 +0300 Subject: [PATCH 01/19] Update util.py --- mcpi_e/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcpi_e/util.py b/mcpi_e/util.py index 38d9a96..ff64b6e 100644 --- a/mcpi_e/util.py +++ b/mcpi_e/util.py @@ -1,4 +1,4 @@ -import collections +import collections.abc as collections def flatten(l): for e in l: From 2e7ae6758c2b85a66ab6d803d07d043bb122c234 Mon Sep 17 00:00:00 2001 From: Tim <85190511+sm-timmy@users.noreply.github.com> Date: Wed, 28 Feb 2024 20:51:05 +0300 Subject: [PATCH 02/19] Fixed setBlock with color minecraft.py Fixed setBlock with color --- mcpi_e/minecraft.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mcpi_e/minecraft.py b/mcpi_e/minecraft.py index fb7b9d6..3ccdd19 100644 --- a/mcpi_e/minecraft.py +++ b/mcpi_e/minecraft.py @@ -316,7 +316,11 @@ def getBlocks(self, *args): def setBlock(self, *args): """Set block (x,y,z,id,[data])""" - self.conn.send(b"world.setBlock", intFloor(args)) + if len(args) > 4: + self.conn.send(b"world.setBlocks", + intFloor(args[0], args[1], args[2], args[0], args[1], args[2], args[3], args[4])) + else: + 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])""" From 2a82e289c47df57f6fe47288799c1466cbb1e86c Mon Sep 17 00:00:00 2001 From: caa Date: Mon, 4 Mar 2024 10:29:57 +0300 Subject: [PATCH 03/19] rcon support added --- mcpi_e/minecraft.py | 33 ++++++++++++++++++++++----------- mcpi_e/rconnection.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 11 deletions(-) create mode 100644 mcpi_e/rconnection.py diff --git a/mcpi_e/minecraft.py b/mcpi_e/minecraft.py index 3ccdd19..7d48bfa 100644 --- a/mcpi_e/minecraft.py +++ b/mcpi_e/minecraft.py @@ -1,3 +1,4 @@ +from mcpi_e.rconnection import Rconnection from .connection import Connection from .vec3 import Vec3 from .event import BlockEvent, ChatEvent, ProjectileEvent @@ -289,14 +290,15 @@ def pollProjectileHits(self): class Minecraft: """The main class to interact with a running instance of Minecraft Pi.""" - def __init__(self, connection,playerId): - self.conn = connection - - self.camera = CmdCamera(connection) - self.entity = CmdEntity(connection) - self.cmdplayer = CmdPlayer(connection,playerId) - self.player=CmdPlayerEntity(connection,playerId) - self.events = CmdEvents(connection) + def __init__(self, raspberryConnection, rconConnection, playerId): + self.conn = raspberryConnection + self.rconn = rconConnection + + self.camera = CmdCamera(raspberryConnection) + self.entity = CmdEntity(raspberryConnection) + self.cmdplayer = CmdPlayer(raspberryConnection,playerId) + self.player=CmdPlayerEntity(raspberryConnection,playerId) + self.events = CmdEvents(raspberryConnection) self.playerId= playerId self.settings=settings @@ -394,16 +396,25 @@ def removeEntities(self, typeId=-1): return int(self.conn.sendReceive(b"world.removeEntities", typeId)) + ### + RCON ADDITIONAL COMMANDS ### + def summonCreature(self, x, y, z, creature): + return self.rconn.sendReceive(f"/summon {creature} ~{x} ~{y} ~{z}") + + + ### - RCON ADDITIONAL COMMANDS ### + + @staticmethod - def create(address = "localhost", port = 4711,playerName=""): + def create(address = "localhost", rasberryPort = 4711, rconPort = 8711, rconPassword=47118711 ,playerName=""): log("Running Python version:"+sys.version) - conn=Connection(address, port) + conn=Connection(address, rasberryPort) + rconn=Rconnection(address, rconPort, rconPassword) playerId=[] if playerName!="": playerId= int(conn.sendReceive(b"world.getPlayerId", playerName)) log("get {} playerid={}".format(playerName, playerId)) - return Minecraft(conn,playerId) + return Minecraft(conn,rconn,playerId) diff --git a/mcpi_e/rconnection.py b/mcpi_e/rconnection.py new file mode 100644 index 0000000..48ca069 --- /dev/null +++ b/mcpi_e/rconnection.py @@ -0,0 +1,33 @@ +import socket +import select +import sys +import time +from .util import flatten_parameters_to_bytestring +from .logger import * +import mcpi_e.settings as settings +from mcrcon import MCRcon + +""" @author: Aron Nieminen, Mojang AB""" + + +class RequestError(Exception): + pass + + +class Rconnection: + """Connection to a Minecraft Pi game""" + RequestFailed = "Fail" + + def __init__(self, address, port, password): + self.rconn = MCRcon(host=address, port=port, password=password) + self.lastSent = "" + + def sendReceive(self, command): + """ + Sends data by RCON. Note that a trailing newline '\n' is added here + + The protocol uses CP437 encoding - https://en.wikipedia.org/wiki/Code_page_437 + which is mildly distressing as it can't encode all of Unicode. + """ + result = self.rconn.command(command) + return result From 87c3769bd0641550eeb89844e1801bd910cbbeda Mon Sep 17 00:00:00 2001 From: caa Date: Mon, 4 Mar 2024 10:39:56 +0300 Subject: [PATCH 04/19] rcon support added --- mcpi_e/rconnection.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mcpi_e/rconnection.py b/mcpi_e/rconnection.py index 48ca069..74e0742 100644 --- a/mcpi_e/rconnection.py +++ b/mcpi_e/rconnection.py @@ -29,5 +29,7 @@ def sendReceive(self, command): The protocol uses CP437 encoding - https://en.wikipedia.org/wiki/Code_page_437 which is mildly distressing as it can't encode all of Unicode. """ + print(f"-> sendReceive RCON: {command}") result = self.rconn.command(command) + print(f"<- sendReceive RCON: {result}") return result From 4b439f9a954941a5ccfd0e5e88ded93007fd37a0 Mon Sep 17 00:00:00 2001 From: caa Date: Mon, 4 Mar 2024 10:41:54 +0300 Subject: [PATCH 05/19] rcon connect added --- mcpi_e/rconnection.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mcpi_e/rconnection.py b/mcpi_e/rconnection.py index 74e0742..65eccbe 100644 --- a/mcpi_e/rconnection.py +++ b/mcpi_e/rconnection.py @@ -20,6 +20,7 @@ class Rconnection: def __init__(self, address, port, password): self.rconn = MCRcon(host=address, port=port, password=password) + self.rconn.connect() self.lastSent = "" def sendReceive(self, command): From 50651323c58e0f354368bcbf48203ad8680325f3 Mon Sep 17 00:00:00 2001 From: caa Date: Mon, 4 Mar 2024 11:28:36 +0300 Subject: [PATCH 06/19] summonCraeture data added --- mcpi_e/minecraft.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mcpi_e/minecraft.py b/mcpi_e/minecraft.py index 7d48bfa..92da3b8 100644 --- a/mcpi_e/minecraft.py +++ b/mcpi_e/minecraft.py @@ -397,8 +397,8 @@ def removeEntities(self, typeId=-1): ### + RCON ADDITIONAL COMMANDS ### - def summonCreature(self, x, y, z, creature): - return self.rconn.sendReceive(f"/summon {creature} ~{x} ~{y} ~{z}") + def summonCreature(self, x, y, z, creature, data = ""): + return self.rconn.sendReceive(f"/summon {creature} ~{x} ~{y} ~{z} {data}") ### - RCON ADDITIONAL COMMANDS ### From 890c0a39352327a27aeaab8b957bb663ad76b5a5 Mon Sep 17 00:00:00 2001 From: caa Date: Mon, 4 Mar 2024 12:08:23 +0300 Subject: [PATCH 07/19] rcon all commands added --- mcpi_e/minecraft.py | 49 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/mcpi_e/minecraft.py b/mcpi_e/minecraft.py index 92da3b8..c111d61 100644 --- a/mcpi_e/minecraft.py +++ b/mcpi_e/minecraft.py @@ -400,6 +400,55 @@ def removeEntities(self, typeId=-1): def summonCreature(self, x, y, z, creature, data = ""): return self.rconn.sendReceive(f"/summon {creature} ~{x} ~{y} ~{z} {data}") + def setTime(self, daytime): + return self.rconn.sendReceive(f"/time set {daytime}") + + def stopTime(self, isStopped): + if isStopped: + return self.rconn.sendReceive(f"/time doDaylightCycle false") + else: + return self.rconn.sendReceive(f"/time doDaylightCycle true") + + def setWeather(self, weather, duration=60*5): + return self.rconn.sendReceive(f"/weather {weather} {duration}") + + + #survival/creative/adventure(0/1/2) + def setGamemode(self, whom, gamemode): + return self.rconn.sendReceive(f"/gamemode {gamemode} {whom}") + + #peaceful/easy/normal/hard (0,1,2,3) + def setDifficulty(self, difficulty): + return self.rconn.sendReceive(f"/difficulty {difficulty}") + + def giveItem(self, whom, item, count=1): + return self.rconn.sendReceive(f"/give {whom} {item} {count}") + + def giveItemToMe(self, item, count=1): + return self.rconn.sendReceive(f"/give {self.playerId} {item} {count}") + + def teleport(self, x,y,z): + return self.rconn.sendReceive(f"/tp {self.playerId} {x} {y} {z}") + + def teleportToMe(self, whom): + return self.rconn.sendReceive(f"/tp {whom} {self.playerId}") + + def setWorldspawn(self, x, y, z): + return self.rconn.sendReceive(f"/setworldspawn {x} {y} {z}") + + def clearInventory(self, person): + return self.rconn.sendReceive(f"/clear {person}") + + def cloneBlocks(self, x1,y1,z1,x2,y2,z2,x,y,z): + return self.rconn.sendReceive(f"/clone {x1} {y1} {z1} {x2} {y2} {z2} {x} {y} {z}") + + def setEffect(self, whom, effect, duration=20, power=10): + return self.rconn.sendReceive(f"/effect {whom} {effect} {duration} {power}") + + def setEntityData(self, whom, data): + return self.rconn.sendReceive(f"/entitydata {whom} {data}") + + ### - RCON ADDITIONAL COMMANDS ### From 071d88864e36a358f9c4cfed1e402d07ae6c42ce Mon Sep 17 00:00:00 2001 From: caa Date: Mon, 4 Mar 2024 12:12:41 +0300 Subject: [PATCH 08/19] rcon all commands added --- mcpi_e/minecraft.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/mcpi_e/minecraft.py b/mcpi_e/minecraft.py index c111d61..c1c0b2b 100644 --- a/mcpi_e/minecraft.py +++ b/mcpi_e/minecraft.py @@ -290,7 +290,7 @@ def pollProjectileHits(self): class Minecraft: """The main class to interact with a running instance of Minecraft Pi.""" - def __init__(self, raspberryConnection, rconConnection, playerId): + def __init__(self, raspberryConnection, rconConnection, playerId, playerName): self.conn = raspberryConnection self.rconn = rconConnection @@ -299,7 +299,7 @@ def __init__(self, raspberryConnection, rconConnection, playerId): self.cmdplayer = CmdPlayer(raspberryConnection,playerId) self.player=CmdPlayerEntity(raspberryConnection,playerId) self.events = CmdEvents(raspberryConnection) - self.playerId= playerId + self.playerName= playerName self.settings=settings def getBlock(self, *args): @@ -425,13 +425,13 @@ def giveItem(self, whom, item, count=1): return self.rconn.sendReceive(f"/give {whom} {item} {count}") def giveItemToMe(self, item, count=1): - return self.rconn.sendReceive(f"/give {self.playerId} {item} {count}") + return self.rconn.sendReceive(f"/give {self.playerName} {item} {count}") def teleport(self, x,y,z): - return self.rconn.sendReceive(f"/tp {self.playerId} {x} {y} {z}") + return self.rconn.sendReceive(f"/tp {self.player} {x} {y} {z}") def teleportToMe(self, whom): - return self.rconn.sendReceive(f"/tp {whom} {self.playerId}") + return self.rconn.sendReceive(f"/tp {whom} {self.playerName}") def setWorldspawn(self, x, y, z): return self.rconn.sendReceive(f"/setworldspawn {x} {y} {z}") @@ -447,9 +447,6 @@ def setEffect(self, whom, effect, duration=20, power=10): def setEntityData(self, whom, data): return self.rconn.sendReceive(f"/entitydata {whom} {data}") - - - ### - RCON ADDITIONAL COMMANDS ### @@ -463,7 +460,7 @@ def create(address = "localhost", rasberryPort = 4711, rconPort = 8711, rconPass playerId= int(conn.sendReceive(b"world.getPlayerId", playerName)) log("get {} playerid={}".format(playerName, playerId)) - return Minecraft(conn,rconn,playerId) + return Minecraft(conn,rconn,playerId, playerName) From 29691e30598c62e0b63bdaf7fb544be04b1c0a87 Mon Sep 17 00:00:00 2001 From: caa Date: Mon, 4 Mar 2024 12:13:42 +0300 Subject: [PATCH 09/19] rcon teleport() fixed --- mcpi_e/minecraft.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcpi_e/minecraft.py b/mcpi_e/minecraft.py index c1c0b2b..b38994f 100644 --- a/mcpi_e/minecraft.py +++ b/mcpi_e/minecraft.py @@ -428,7 +428,7 @@ def giveItemToMe(self, item, count=1): return self.rconn.sendReceive(f"/give {self.playerName} {item} {count}") def teleport(self, x,y,z): - return self.rconn.sendReceive(f"/tp {self.player} {x} {y} {z}") + return self.rconn.sendReceive(f"/tp {self.playerName} {x} {y} {z}") def teleportToMe(self, whom): return self.rconn.sendReceive(f"/tp {whom} {self.playerName}") From 17bb81c7234ec1cf7d15777cf7ad1fe1a19e4d63 Mon Sep 17 00:00:00 2001 From: caa Date: Mon, 4 Mar 2024 13:53:43 +0300 Subject: [PATCH 10/19] DRAWING ADDITIONAL COMMANDS all added --- mcpi_e/entity.py | 7 +- mcpi_e/minecraft.py | 384 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 325 insertions(+), 66 deletions(-) diff --git a/mcpi_e/entity.py b/mcpi_e/entity.py index b78a485..c4e716c 100644 --- a/mcpi_e/entity.py +++ b/mcpi_e/entity.py @@ -1,7 +1,7 @@ class Entity: '''Minecraft PI entity description. Can be sent to Minecraft.spawnEntity''' - def __init__(self, id, name = None): + def __init__(self, id, name=None): self.id = id self.name = name @@ -19,7 +19,8 @@ def __iter__(self): return iter((self.id,)) def __repr__(self): - return 'Entity(%d)'%(self.id) + return 'Entity(%d)' % (self.id) + EXPERIENCE_ORB = Entity(2, "EXPERIENCE_ORB") AREA_EFFECT_CLOUD = Entity(3, "AREA_EFFECT_CLOUD") @@ -99,4 +100,4 @@ def __repr__(self): LLAMA_SPIT = Entity(104, "LLAMA_SPIT") PARROT = Entity(105, "PARROT") VILLAGER = Entity(120, "VILLAGER") -ENDER_CRYSTAL = Entity(200, "ENDER_CRYSTAL") \ No newline at end of file +ENDER_CRYSTAL = Entity(200, "ENDER_CRYSTAL") diff --git a/mcpi_e/minecraft.py b/mcpi_e/minecraft.py index b38994f..efb0e07 100644 --- a/mcpi_e/minecraft.py +++ b/mcpi_e/minecraft.py @@ -10,7 +10,6 @@ from .logger import * import mcpi_e.settings as settings - """ Minecraft PI low level api v0.1_1 Note: many methods have the parameter *arg. This solution makes it @@ -37,11 +36,14 @@ - removeEntityType() """ + 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 @@ -93,11 +95,13 @@ 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") - + def getName(self, id): """Get the list name of the player with entity id => [name:str] @@ -109,7 +113,8 @@ def getEntities(self, id, distance=10, typeId=-1): """If distanceFromPlayerInBlocks:int is not specified then default 10 blocks will be used""" s = self.conn.sendReceive(b"entity.getEntities", id, distance, typeId) entities = [e for e in s.split("|") if e] - return [ [int(n.split(",")[0]), int(n.split(",")[1]), n.split(",")[2], float(n.split(",")[3]), float(n.split(",")[4]), float(n.split(",")[5])] for n in entities] + return [[int(n.split(",")[0]), int(n.split(",")[1]), n.split(",")[2], float(n.split(",")[3]), + float(n.split(",")[4]), float(n.split(",")[5])] for n in entities] def removeEntities(self, id, distance=10, typeId=-1): """Remove entities all entities near entity (playerEntityId:int, distanceFromPlayerInBlocks:int, typeId:int, ) => (removedEntitiesCount:int)""" @@ -127,7 +132,7 @@ def pollChatPosts(self, *args): s = self.conn.sendReceive(b"entity.events.chat.posts", intFloor(args)) events = [e for e in s.split("|") if e] return [ChatEvent.Post(int(e[:e.find(",")]), e[e.find(",") + 1:]) for e in events] - + def pollProjectileHits(self, *args): """Only triggered by projectiles => [BlockEvent]""" s = self.conn.sendReceive(b"entity.events.projectile.hits", intFloor(args)) @@ -136,10 +141,10 @@ def pollProjectileHits(self, *args): for e in events: info = e.split(",") results.append(ProjectileEvent.Hit( - int(info[0]), - int(info[1]), - int(info[2]), - int(info[3]), + int(info[0]), + int(info[1]), + int(info[2]), + int(info[3]), info[4], info[5])) return results @@ -148,31 +153,42 @@ def clearEvents(self, *args): """Clear the entities events""" self.conn.send(b"entity.events.clear", intFloor(args)) + class CmdPlayer(CmdPositioner): """Methods for the host (Raspberry Pi) player""" - def __init__(self, connection,playerId): - CmdPositioner.__init__(self, connection, b"player") + + def __init__(self, connection, playerId): + CmdPositioner.__init__(self, connection, b"player") self.conn = connection - self.playerId=playerId + self.playerId = playerId def getPos(self): return CmdPositioner.getPos(self, self.playerId) + def setPos(self, *args): return CmdPositioner.setPos(self, self.playerId, args) + def getTilePos(self): return CmdPositioner.getTilePos(self, self.playerId) + def setTilePos(self, *args): return CmdPositioner.setTilePos(self, self.playerId, args) + def setDirection(self, *args): return CmdPositioner.setDirection(self, self.playerId, args) + def getDirection(self): return CmdPositioner.getDirection(self, self.playerId) + def setRotation(self, yaw): - return CmdPositioner.setRotation(self,self.playerId, yaw) + return CmdPositioner.setRotation(self, self.playerId, yaw) + def getRotation(self): return CmdPositioner.getRotation(self, self.playerId) + def setPitch(self, pitch): return CmdPositioner.setPitch(self, self.playerId, pitch) + def getPitch(self): return CmdPositioner.getPitch(self, self.playerId) @@ -181,7 +197,8 @@ def getEntities(self, distance=10, typeId=-1): """If distanceFromPlayerInBlocks:int is not specified then default 10 blocks will be used""" s = self.conn.sendReceive(b"player.getEntities", distance, typeId) entities = [e for e in s.split("|") if e] - return [ [int(n.split(",")[0]), int(n.split(",")[1]), n.split(",")[2], float(n.split(",")[3]), float(n.split(",")[4]), float(n.split(",")[5])] for n in entities] + return [[int(n.split(",")[0]), int(n.split(",")[1]), n.split(",")[2], float(n.split(",")[3]), + float(n.split(",")[4]), float(n.split(",")[5])] for n in entities] def removeEntities(self, distance=10, typeId=-1): """Remove entities all entities near entity (distanceFromPlayerInBlocks:int, typeId:int, ) => (removedEntitiesCount:int)""" @@ -199,7 +216,7 @@ def pollChatPosts(self): s = self.conn.sendReceive(b"player.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] - + def pollProjectileHits(self): """Only triggered by projectiles => [BlockEvent]""" s = self.conn.sendReceive(b"player.events.projectile.hits") @@ -208,10 +225,10 @@ def pollProjectileHits(self): for e in events: info = e.split(",") results.append(ProjectileEvent.Hit( - int(info[0]), - int(info[1]), - int(info[2]), - int(info[3]), + int(info[0]), + int(info[1]), + int(info[2]), + int(info[3]), info[4], info[5])) return results @@ -220,15 +237,18 @@ def clearEvents(self): """Clear the players events""" self.conn.send(b"player.events.clear") + class CmdPlayerEntity(CmdPlayer): """ use entity to build a player """ - def __init__(self, connection,playerId): - CmdPositioner.__init__(self, connection, b"entity") + + def __init__(self, connection, playerId): + CmdPositioner.__init__(self, connection, b"entity") self.conn = connection - self.playerId=playerId - + self.playerId = playerId + def getPos(self): - return CmdPositioner.getPos(self, self.playerId) + return CmdPositioner.getPos(self, self.playerId) + class CmdCamera: def __init__(self, connection): @@ -253,6 +273,7 @@ def setPos(self, *args): class CmdEvents: """Events""" + def __init__(self, connection): self.conn = connection @@ -271,7 +292,7 @@ def pollChatPosts(self): 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] - + def pollProjectileHits(self): """Only triggered by projectiles => [BlockEvent]""" s = self.conn.sendReceive(b"events.projectile.hits") @@ -280,27 +301,29 @@ def pollProjectileHits(self): for e in events: info = e.split(",") results.append(ProjectileEvent.Hit( - int(info[0]), - int(info[1]), - int(info[2]), - int(info[3]), + int(info[0]), + int(info[1]), + int(info[2]), + int(info[3]), info[4], info[5])) return results + class Minecraft: """The main class to interact with a running instance of Minecraft Pi.""" + def __init__(self, raspberryConnection, rconConnection, playerId, playerName): self.conn = raspberryConnection self.rconn = rconConnection self.camera = CmdCamera(raspberryConnection) self.entity = CmdEntity(raspberryConnection) - self.cmdplayer = CmdPlayer(raspberryConnection,playerId) - self.player=CmdPlayerEntity(raspberryConnection,playerId) + self.cmdplayer = CmdPlayer(raspberryConnection, playerId) + self.player = CmdPlayerEntity(raspberryConnection, playerId) self.events = CmdEvents(raspberryConnection) - self.playerName= playerName - self.settings=settings + self.playerName = playerName + self.settings = settings def getBlock(self, *args): """Get block (x,y,z) => id:int""" @@ -318,9 +341,9 @@ def getBlocks(self, *args): def setBlock(self, *args): """Set block (x,y,z,id,[data])""" - if len(args) > 4: + if len(args) > 4: self.conn.send(b"world.setBlocks", - intFloor(args[0], args[1], args[2], args[0], args[1], args[2], args[3], args[4])) + intFloor(args[0], args[1], args[2], args[0], args[1], args[2], args[3], args[4])) else: self.conn.send(b"world.setBlock", intFloor(args)) @@ -339,8 +362,8 @@ def setSign(self, *args): for arg in flatten(args): flatargs.append(arg) for flatarg in flatargs[5:]: - lines.append(flatarg.replace(",",";").replace(")","]").replace("(","[")) - self.conn.send(b"world.setSign",intFloor(flatargs[0:5]) + lines) + lines.append(flatarg.replace(",", ";").replace(")", "]").replace("(", "[")) + self.conn.send(b"world.setSign", intFloor(flatargs[0:5]) + lines) def spawnEntity(self, *args): """Spawn entity (x,y,z,id)""" @@ -376,16 +399,17 @@ def setting(self, setting, status): self.conn.send(b"world.setting", setting, 1 if bool(status) else 0) def getEntityTypes(self): - """Return a list of Entity objects representing all the entity types in Minecraft""" + """Return a list of Entity objects representing all the entity types in Minecraft""" s = self.conn.sendReceive(b"world.getEntityTypes") types = [t for t in s.split("|") if t] return [Entity(int(e[:e.find(",")]), e[e.find(",") + 1:]) for e in types] - + def getEntities(self, typeId=-1): """Return a list of all currently loaded entities (EntityType:int) => [[entityId:int,entityTypeId:int,entityTypeName:str,posX:float,posY:float,posZ:float]]""" s = self.conn.sendReceive(b"world.getEntities", typeId) entities = [e for e in s.split("|") if e] - return [[int(n.split(",")[0]), int(n.split(",")[1]), n.split(",")[2], float(n.split(",")[3]), float(n.split(",")[4]), float(n.split(",")[5])] for n in entities] + return [[int(n.split(",")[0]), int(n.split(",")[1]), n.split(",")[2], float(n.split(",")[3]), + float(n.split(",")[4]), float(n.split(",")[5])] for n in entities] def removeEntity(self, id): """Remove entity by id (entityId:int) => (removedEntitiesCount:int)""" @@ -395,9 +419,245 @@ def removeEntities(self, typeId=-1): """Remove entities all currently loaded Entities by type (typeId:int) => (removedEntitiesCount:int)""" return int(self.conn.sendReceive(b"world.removeEntities", typeId)) + ### + DRAWING ADDITIONAL COMMANDS ### + # draw point + def drawPoint3d(self, x, y, z, blockType, blockData=0): + self.setBlock(x, y, z, blockType, blockData) + # print "x = " + str(x) + ", y = " + str(y) + ", z = " + str(z) + + # draws a face, when passed a collection of vertices which make up a polyhedron + def drawFigure(self, vertices, filled, blockType, blockData=0): + # get the edges of the face + edgesVertices = [] + # persist first vertex + firstVertex = vertices[0] + # get last vertex + lastVertex = vertices[0] + # loop through vertices and get edges + for vertex in vertices[1:]: + # got 2 vertices, get the points for the edge + edgesVertices = edgesVertices + self.getLine(lastVertex.x, lastVertex.y, lastVertex.z, vertex.x, vertex.y, + vertex.z) + # persist the last vertex found + lastVertex = vertex + # get edge between the last and first vertices + edgesVertices = edgesVertices + self.getLine(lastVertex.x, lastVertex.y, lastVertex.z, firstVertex.x, + firstVertex.y, firstVertex.z) + + if (filled): + # draw solid face + # sort edges vertices + def keyX(point): + return point.x + + def keyY(point): + return point.y + + def keyZ(point): + return point.z + + edgesVertices.sort(key=keyZ) + edgesVertices.sort(key=keyY) + edgesVertices.sort(key=keyX) + + # draw lines between the points on the edges + # this algorithm isnt very efficient, but it does always fill the gap + lastVertex = edgesVertices[0] + for vertex in edgesVertices[1:]: + # got 2 vertices, draw lines between them + self.drawLine(lastVertex.x, lastVertex.y, lastVertex.z, vertex.x, vertex.y, vertex.z, blockType, + blockData) + # print "x = " + str(lastVertex.x) + ", y = " + str(lastVertex.y) + ", z = " + str(lastVertex.z) + " x2 = " + str(vertex.x) + ", y2 = " + str(vertex.y) + ", z2 = " + str(vertex.z) + # persist the last vertex found + lastVertex = vertex + + else: + # draw wireframe + self.drawVertices(edgesVertices, blockType, blockData) + + # draw's all the points in a collection of vertices with a block + def drawVertices(self, vertices, blockType, blockData=0): + for vertex in vertices: + self.drawPoint3d(vertex.x, vertex.y, vertex.z, blockType, blockData) + + # draw line + def drawLine(self, x1, y1, z1, x2, y2, z2, blockType, blockData=0): + self.drawVertices(self.getLine(x1, y1, z1, x2, y2, z2), blockType, blockData) + + # draw sphere + def drawSphere(self, x1, y1, z1, radius, blockType, blockData=0): + # create sphere + for x in range(radius * -1, radius): + for y in range(radius * -1, radius): + for z in range(radius * -1, radius): + if x ** 2 + y ** 2 + z ** 2 < radius ** 2: + self.drawPoint3d(x1 + x, y1 + y, z1 + z, blockType, blockData) + + # draw a verticle circle + def drawCircle(self, x0, y0, z, radius, blockType, blockData=0): + f = 1 - radius + ddf_x = 1 + ddf_y = -2 * radius + x = 0 + y = radius + self.drawPoint3d(x0, y0 + radius, z, blockType, blockData) + self.drawPoint3d(x0, y0 - radius, z, blockType, blockData) + self.drawPoint3d(x0 + radius, y0, z, blockType, blockData) + self.drawPoint3d(x0 - radius, y0, z, blockType, blockData) + + while x < y: + if f >= 0: + y -= 1 + ddf_y += 2 + f += ddf_y + x += 1 + ddf_x += 2 + f += ddf_x + self.drawPoint3d(x0 + x, y0 + y, z, blockType, blockData) + self.drawPoint3d(x0 - x, y0 + y, z, blockType, blockData) + self.drawPoint3d(x0 + x, y0 - y, z, blockType, blockData) + self.drawPoint3d(x0 - x, y0 - y, z, blockType, blockData) + self.drawPoint3d(x0 + y, y0 + x, z, blockType, blockData) + self.drawPoint3d(x0 - y, y0 + x, z, blockType, blockData) + self.drawPoint3d(x0 + y, y0 - x, z, blockType, blockData) + self.drawPoint3d(x0 - y, y0 - x, z, blockType, blockData) + + # draw a horizontal circle + def drawHorizontalCircle(self, x0, y, z0, radius, blockType, blockData=0): + f = 1 - radius + ddf_x = 1 + ddf_z = -2 * radius + x = 0 + z = radius + self.drawPoint3d(x0, y, z0 + radius, blockType, blockData) + self.drawPoint3d(x0, y, z0 - radius, blockType, blockData) + self.drawPoint3d(x0 + radius, y, z0, blockType, blockData) + self.drawPoint3d(x0 - radius, y, z0, blockType, blockData) + + while x < z: + if f >= 0: + z -= 1 + ddf_z += 2 + f += ddf_z + x += 1 + ddf_x += 2 + f += ddf_x + self.drawPoint3d(x0 + x, y, z0 + z, blockType, blockData) + self.drawPoint3d(x0 - x, y, z0 + z, blockType, blockData) + self.drawPoint3d(x0 + x, y, z0 - z, blockType, blockData) + self.drawPoint3d(x0 - x, y, z0 - z, blockType, blockData) + self.drawPoint3d(x0 + z, y, z0 + x, blockType, blockData) + self.drawPoint3d(x0 - z, y, z0 + x, blockType, blockData) + self.drawPoint3d(x0 + z, y, z0 - x, blockType, blockData) + self.drawPoint3d(x0 - z, y, z0 - x, blockType, blockData) + + # returns points on a line + # 3d implementation of bresenham line algorithm + def getLine(self, x1, y1, z1, x2, y2, z2): + + # return maximum of 2 values + def MAX(a, b): + if a > b: + return a + else: + return b + + # return step + def ZSGN(a): + if a < 0: + return -1 + elif a > 0: + return 1 + elif a == 0: + return 0 + + # list for vertices + vertices = [] + + # if the 2 points are the same, return single vertice + if (x1 == x2 and y1 == y2 and z1 == z2): + vertices.append(Vec3(x1, y1, z1)) + + # else get all points in edge + else: + + dx = x2 - x1 + dy = y2 - y1 + dz = z2 - z1 + + ax = abs(dx) << 1 + ay = abs(dy) << 1 + az = abs(dz) << 1 + + sx = ZSGN(dx) + sy = ZSGN(dy) + sz = ZSGN(dz) + + x = x1 + y = y1 + z = z1 + + # x dominant + if (ax >= MAX(ay, az)): + yd = ay - (ax >> 1) + zd = az - (ax >> 1) + loop = True + while (loop): + vertices.append(Vec3(x, y, z)) + if (x == x2): + loop = False + if (yd >= 0): + y += sy + yd -= ax + if (zd >= 0): + z += sz + zd -= ax + x += sx + yd += ay + zd += az + # y dominant + elif (ay >= MAX(ax, az)): + xd = ax - (ay >> 1) + zd = az - (ay >> 1) + loop = True + while (loop): + vertices.append(Vec3(x, y, z)) + if (y == y2): + loop = False + if (xd >= 0): + x += sx + xd -= ay + if (zd >= 0): + z += sz + zd -= ay + y += sy + xd += ax + zd += az + # z dominant + elif (az >= MAX(ax, ay)): + xd = ax - (az >> 1) + yd = ay - (az >> 1) + loop = True + while (loop): + vertices.append(Vec3(x, y, z)) + if (z == z2): + loop = False + if (xd >= 0): + x += sx + xd -= az + if (yd >= 0): + y += sy + yd -= az + z += sz + xd += ax + yd += ay + + return vertices + + ### - DRAWING ADDITIONAL COMMANDS ### ### + RCON ADDITIONAL COMMANDS ### - def summonCreature(self, x, y, z, creature, data = ""): + def summonCreature(self, x, y, z, creature, data=""): return self.rconn.sendReceive(f"/summon {creature} ~{x} ~{y} ~{z} {data}") def setTime(self, daytime): @@ -409,25 +669,24 @@ def stopTime(self, isStopped): else: return self.rconn.sendReceive(f"/time doDaylightCycle true") - def setWeather(self, weather, duration=60*5): + def setWeather(self, weather, duration=60 * 5): return self.rconn.sendReceive(f"/weather {weather} {duration}") - - #survival/creative/adventure(0/1/2) + # survival/creative/adventure(0/1/2) def setGamemode(self, whom, gamemode): return self.rconn.sendReceive(f"/gamemode {gamemode} {whom}") - #peaceful/easy/normal/hard (0,1,2,3) + # peaceful/easy/normal/hard (0,1,2,3) def setDifficulty(self, difficulty): return self.rconn.sendReceive(f"/difficulty {difficulty}") - def giveItem(self, whom, item, count=1): + def giveItem(self, whom, item, count=1): return self.rconn.sendReceive(f"/give {whom} {item} {count}") def giveItemToMe(self, item, count=1): return self.rconn.sendReceive(f"/give {self.playerName} {item} {count}") - def teleport(self, x,y,z): + def teleport(self, x, y, z): return self.rconn.sendReceive(f"/tp {self.playerName} {x} {y} {z}") def teleportToMe(self, whom): @@ -439,7 +698,7 @@ def setWorldspawn(self, x, y, z): def clearInventory(self, person): return self.rconn.sendReceive(f"/clear {person}") - def cloneBlocks(self, x1,y1,z1,x2,y2,z2,x,y,z): + def cloneBlocks(self, x1, y1, z1, x2, y2, z2, x, y, z): return self.rconn.sendReceive(f"/clone {x1} {y1} {z1} {x2} {y2} {z2} {x} {y} {z}") def setEffect(self, whom, effect, duration=20, power=10): @@ -447,25 +706,24 @@ def setEffect(self, whom, effect, duration=20, power=10): def setEntityData(self, whom, data): return self.rconn.sendReceive(f"/entitydata {whom} {data}") - ### - RCON ADDITIONAL COMMANDS ### + ### - RCON ADDITIONAL COMMANDS ### @staticmethod - def create(address = "localhost", rasberryPort = 4711, rconPort = 8711, rconPassword=47118711 ,playerName=""): - log("Running Python version:"+sys.version) - conn=Connection(address, rasberryPort) - rconn=Rconnection(address, rconPort, rconPassword) - playerId=[] - if playerName!="": - playerId= int(conn.sendReceive(b"world.getPlayerId", playerName)) - log("get {} playerid={}".format(playerName, playerId)) - - return Minecraft(conn,rconn,playerId, playerName) - - - -#settings + def create(address="localhost", rasberryPort=4711, rconPort=8711, rconPassword=47118711, playerName=""): + log("Running Python version:" + sys.version) + conn = Connection(address, rasberryPort) + rconn = Rconnection(address, rconPort, rconPassword) + playerId = [] + if playerName != "": + playerId = int(conn.sendReceive(b"world.getPlayerId", playerName)) + log("get {} playerid={}".format(playerName, playerId)) + + return Minecraft(conn, rconn, playerId, playerName) + + +# settings if __name__ == "__main__": - #initSettings() + # initSettings() mc = Minecraft.create() mc.postToChat("Hello, Minecraft!") From d766932540b14cf9b3c8bcb8d95e1e628d598f23 Mon Sep 17 00:00:00 2001 From: caa Date: Mon, 11 Mar 2024 14:46:42 +0300 Subject: [PATCH 11/19] stopTime fixed --- mcpi_e/minecraft.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mcpi_e/minecraft.py b/mcpi_e/minecraft.py index efb0e07..d75abf7 100644 --- a/mcpi_e/minecraft.py +++ b/mcpi_e/minecraft.py @@ -426,7 +426,7 @@ def drawPoint3d(self, x, y, z, blockType, blockData=0): # print "x = " + str(x) + ", y = " + str(y) + ", z = " + str(z) # draws a face, when passed a collection of vertices which make up a polyhedron - def drawFigure(self, vertices, filled, blockType, blockData=0): + def drawFace(self, vertices, filled, blockType, blockData=0): # get the edges of the face edgesVertices = [] # persist first vertex @@ -665,9 +665,9 @@ def setTime(self, daytime): def stopTime(self, isStopped): if isStopped: - return self.rconn.sendReceive(f"/time doDaylightCycle false") + return self.rconn.sendReceive(f"/gamerule doDaylightCycle false") else: - return self.rconn.sendReceive(f"/time doDaylightCycle true") + return self.rconn.sendReceive(f"/gamerule doDaylightCycle true") def setWeather(self, weather, duration=60 * 5): return self.rconn.sendReceive(f"/weather {weather} {duration}") From 7e8930ee99734716e814f56f0438e44e6b285002 Mon Sep 17 00:00:00 2001 From: caa Date: Mon, 11 Mar 2024 15:02:17 +0300 Subject: [PATCH 12/19] drawFace -> drawFigure --- mcpi_e/minecraft.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcpi_e/minecraft.py b/mcpi_e/minecraft.py index d75abf7..130c9d7 100644 --- a/mcpi_e/minecraft.py +++ b/mcpi_e/minecraft.py @@ -426,7 +426,7 @@ def drawPoint3d(self, x, y, z, blockType, blockData=0): # print "x = " + str(x) + ", y = " + str(y) + ", z = " + str(z) # draws a face, when passed a collection of vertices which make up a polyhedron - def drawFace(self, vertices, filled, blockType, blockData=0): + def drawFigure(self, vertices, filled, blockType, blockData=0): # get the edges of the face edgesVertices = [] # persist first vertex From 5500ff1c5d61a8fdcf9c2f3d7ab4d216554ece8b Mon Sep 17 00:00:00 2001 From: caa Date: Mon, 11 Mar 2024 15:16:58 +0300 Subject: [PATCH 13/19] teleport redrawn --- mcpi_e/minecraft.py | 5 +++- test/testSetBlock.py | 68 +++++++++++++++++++++++++++----------------- 2 files changed, 46 insertions(+), 27 deletions(-) diff --git a/mcpi_e/minecraft.py b/mcpi_e/minecraft.py index 130c9d7..9940bf0 100644 --- a/mcpi_e/minecraft.py +++ b/mcpi_e/minecraft.py @@ -686,7 +686,10 @@ def giveItem(self, whom, item, count=1): def giveItemToMe(self, item, count=1): return self.rconn.sendReceive(f"/give {self.playerName} {item} {count}") - def teleport(self, x, y, z): + def teleport(self, whom, x, y, z): + return self.rconn.sendReceive(f"/tp {whom} {x} {y} {z}") + + def teleportMe(self, x, y, z): return self.rconn.sendReceive(f"/tp {self.playerName} {x} {y} {z}") def teleportToMe(self, whom): diff --git a/test/testSetBlock.py b/test/testSetBlock.py index 7caa2b1..e37596c 100644 --- a/test/testSetBlock.py +++ b/test/testSetBlock.py @@ -2,32 +2,48 @@ from mcpi_e import block from time import sleep from random import * +import random +from math import * +from numbers import * -address="192.168.1.155"#if not pass address, it will be localhost -port=4712 #default port for RaspberryJuice plugin -name="stoneskinkknn" -#mc = Minecraft.create() -mc=Minecraft.create(address,port,name) - -(x,y,z)=mc.player.getTilePos() -#mc.setBlock(x+1,y,z,0) -#wmc.setBlock(x+1,y,z,217) - -id=mc.getBlock(x+1,y,z) -#print("blockid get"+str(id)) +from mcpi_e import * +from mcpi_e import block +from mcpi_e.minecraft import * -# print("set "+str(i)) -i=0 -for a in range(1,3): - print(a) - for b in range(1,13): - # if(i>252): - # break - print("set blocke {} at {},{},{}".format(i,x+a,y+b,z)) - #mc.setBlock(x+a,y,z+b,0) - mc.setBlock(x+a,y,z+b,4) - i=i+1 - #id=mc.getBlock(x+a,y,z+b) - +serverAddress="127.0.0.1" # change to your minecraft server +pythonApiPort=4711 #default port for RaspberryJuice plugin is 4711, it could be changed in plugins\RaspberryJuice\config.yml +playerName="qqq" # change to your username +raspberryPort=4711 +rconPort=8711 +rconPassword="47118711" +mc = Minecraft.create(serverAddress,raspberryPort,rconPort,rconPassword,playerName) -# print("done") \ No newline at end of file +# +# address="192.168.1.155"#if not pass address, it will be localhost +# port=4712 #default port for RaspberryJuice plugin +# name="stoneskinkknn" +# #mc = Minecraft.create() +# mc=Minecraft.create(address,port,name) +# +# (x,y,z)=mc.player.getTilePos() +# #mc.setBlock(x+1,y,z,0) +# #wmc.setBlock(x+1,y,z,217) +# +# id=mc.getBlock(x+1,y,z) +# #print("blockid get"+str(id)) +# +# # print("set "+str(i)) +# i=0 +# for a in range(1,3): +# print(a) +# for b in range(1,13): +# # if(i>252): +# # break +# print("set blocke {} at {},{},{}".format(i,x+a,y+b,z)) +# #mc.setBlock(x+a,y,z+b,0) +# mc.setBlock(x+a,y,z+b,4) +# i=i+1 +# #id=mc.getBlock(x+a,y,z+b) +# +# +# # print("done") \ No newline at end of file From d9730ce13abb972207ab815a94f065813f946760 Mon Sep 17 00:00:00 2001 From: caa Date: Thu, 14 Mar 2024 10:31:14 +0300 Subject: [PATCH 14/19] rconn delay added --- mcpi_e/rconnection.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mcpi_e/rconnection.py b/mcpi_e/rconnection.py index 65eccbe..e4dfa30 100644 --- a/mcpi_e/rconnection.py +++ b/mcpi_e/rconnection.py @@ -31,6 +31,7 @@ def sendReceive(self, command): which is mildly distressing as it can't encode all of Unicode. """ print(f"-> sendReceive RCON: {command}") + time.sleep(3) result = self.rconn.command(command) print(f"<- sendReceive RCON: {result}") return result From 5e6fd40c64e81c57fa7f28c8c80a5c15042b352e Mon Sep 17 00:00:00 2001 From: caa Date: Thu, 14 Mar 2024 11:03:41 +0300 Subject: [PATCH 15/19] rconn delay added --- mcpi_e/rconnection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcpi_e/rconnection.py b/mcpi_e/rconnection.py index e4dfa30..6731246 100644 --- a/mcpi_e/rconnection.py +++ b/mcpi_e/rconnection.py @@ -31,7 +31,7 @@ def sendReceive(self, command): which is mildly distressing as it can't encode all of Unicode. """ print(f"-> sendReceive RCON: {command}") - time.sleep(3) + time.sleep(5) result = self.rconn.command(command) print(f"<- sendReceive RCON: {result}") return result From e4abb0269bf3a17188235bcdfd31beaa0d9ce839 Mon Sep 17 00:00:00 2001 From: caa Date: Thu, 14 Mar 2024 11:10:44 +0300 Subject: [PATCH 16/19] rconn delay added --- mcpi_e/rconnection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcpi_e/rconnection.py b/mcpi_e/rconnection.py index 6731246..716ffa3 100644 --- a/mcpi_e/rconnection.py +++ b/mcpi_e/rconnection.py @@ -31,7 +31,7 @@ def sendReceive(self, command): which is mildly distressing as it can't encode all of Unicode. """ print(f"-> sendReceive RCON: {command}") - time.sleep(5) + # time.sleep(5) result = self.rconn.command(command) print(f"<- sendReceive RCON: {result}") return result From eadc4390467efdd8741dabaf36b2fdd9de470efc Mon Sep 17 00:00:00 2001 From: caa Date: Thu, 14 Mar 2024 13:33:58 +0300 Subject: [PATCH 17/19] rconn delay added summonCreature --- mcpi_e/minecraft.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mcpi_e/minecraft.py b/mcpi_e/minecraft.py index 9940bf0..188b4eb 100644 --- a/mcpi_e/minecraft.py +++ b/mcpi_e/minecraft.py @@ -1,3 +1,5 @@ +import time + from mcpi_e.rconnection import Rconnection from .connection import Connection from .vec3 import Vec3 @@ -658,6 +660,7 @@ def ZSGN(a): ### + RCON ADDITIONAL COMMANDS ### def summonCreature(self, x, y, z, creature, data=""): + time.sleep(15) return self.rconn.sendReceive(f"/summon {creature} ~{x} ~{y} ~{z} {data}") def setTime(self, daytime): From 21de5b8db9632d53a035418c0b8024d1e2a1846f Mon Sep 17 00:00:00 2001 From: caa Date: Sun, 24 Mar 2024 20:15:30 +0300 Subject: [PATCH 18/19] summonCreature - > spawnCreature --- mcpi_e/minecraft.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcpi_e/minecraft.py b/mcpi_e/minecraft.py index 188b4eb..5357aae 100644 --- a/mcpi_e/minecraft.py +++ b/mcpi_e/minecraft.py @@ -659,7 +659,7 @@ def ZSGN(a): ### - DRAWING ADDITIONAL COMMANDS ### ### + RCON ADDITIONAL COMMANDS ### - def summonCreature(self, x, y, z, creature, data=""): + def spawnCreature(self, x, y, z, creature, data=""): time.sleep(15) return self.rconn.sendReceive(f"/summon {creature} ~{x} ~{y} ~{z} {data}") From 5126701617896d681380718da2b422b489b9c8bb Mon Sep 17 00:00:00 2001 From: caa Date: Sun, 24 Mar 2024 20:17:56 +0300 Subject: [PATCH 19/19] kill(self, whom) --- mcpi_e/minecraft.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mcpi_e/minecraft.py b/mcpi_e/minecraft.py index 5357aae..395d071 100644 --- a/mcpi_e/minecraft.py +++ b/mcpi_e/minecraft.py @@ -660,9 +660,12 @@ def ZSGN(a): ### + RCON ADDITIONAL COMMANDS ### def spawnCreature(self, x, y, z, creature, data=""): - time.sleep(15) + time.sleep(2) return self.rconn.sendReceive(f"/summon {creature} ~{x} ~{y} ~{z} {data}") + def kill(self, whom): + return self.rconn.sendReceive(f"/kill {whom}") + def setTime(self, daytime): return self.rconn.sendReceive(f"/time set {daytime}")