Skip to content

Commit

Permalink
Manually merged branch 'master' into refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
avdstaaij committed Dec 18, 2022
2 parents f6eb624 + be409ec commit 47eb8d7
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 30 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# GDPC 5.0 (Manicule)

GDPC (Generative Design Python Client) is a framework for use in conjunction with the [Minecraft HTTP Interface Mod](https://github.com/Niels-NTG/gdmc_http_interface) (version 0.5.X), built for the [GDMC competition](https://gendesignmc.engineering.nyu.edu).
GDPC (Generative Design Python Client) is a framework for use in conjunction with the [Minecraft HTTP Interface Mod](https://github.com/Niels-NTG/gdmc_http_interface) (version 0.6.X), built for the [GDMC competition](https://gendesignmc.engineering.nyu.edu).

You need to be playing in a Minecraft world with the mod installed to use the framework.

Expand Down Expand Up @@ -39,6 +39,7 @@ To download one of the following scripts, click on the link, then right-click an
- Blinkenlights
- Nils Gawlik
- Claus Aranha
- Niels NTG Poldervaart

with contributions from:
- Mayank Jain
79 changes: 57 additions & 22 deletions gdpc/direct_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"""


from typing import Union, Tuple, Optional
from typing import Union, Tuple, Optional, List, Dict, Any
import json

import requests
from requests.exceptions import ConnectionError as RequestConnectionError
Expand Down Expand Up @@ -55,17 +56,37 @@ def _post(*args, retries: int, **kwargs):
retriesLeft -= 1


# TODO: add includeState option
def getBlock(x: int, y: int, z: int, retries=5, timeout=None):
"""Returns the namespaced ID of the block at the given coordinates.
def getBlock(x: int, y: int, z: int, dx: Optional[int] = None, dy: Optional[int] = None, dz: Optional[int] = None, dimension: Optional[str] = None, includeState=False, includeData=False, retries=5, timeout=None):
"""Returns the blocks in the specified region.
If the given coordinates are invalid, returns "minecraft:void_air".
"""
url = f"{HOST}/blocks?x={x}&y={y}&z={z}"
return _get(url, retries=retries, timeout=timeout).text
<dimension> can be one of {"overworld", "the_nether", "the_end"} (default "overworld").
Returns a list with a dict for each retrieved block. The dicts have the following keys/values:
- "x", "y", "z": Coordinates of the block (int).
- "id": Namespaced ID of the block (str).
- "state": Block state dict (Dict[str, str]). Present if and only if <includeState> is True.
- "data": NBT data dict (Dict[str,Any]). Present if and only if <includeData> is True.
def placeBlock(x: int, y: int, z: int, blockStr: str, doBlockUpdates=True, spawnDrops=False, customFlags: str = "", retries=5, timeout=None):
If a set of coordinates is invalid, the returned block ID will be "minecraft:void_air".
"""
url = f"{HOST}/blocks"
parameters = {
'x': x,
'y': y,
'z': z,
'dx': dx,
'dy': dy,
'dz': dz,
'includeState': True if includeState else None,
'includeData': True if includeData else None,
'dimension': dimension,
}
response = _get(url, params=parameters, headers={"accept": "application/json"}, retries=retries, timeout=timeout)
blocks: List[Dict[str, Any]] = response.json()
return blocks


def placeBlock(x: int, y: int, z: int, blockStr: str, dimension: Optional[str] = None, doBlockUpdates=True, spawnDrops=False, customFlags: str = "", retries=5, timeout=None):
"""Places one or multiple blocks in the world.
Each line of <blockStr> should describe a single block placement, using one of the
Expand All @@ -80,32 +101,38 @@ def placeBlock(x: int, y: int, z: int, blockStr: str, doBlockUpdates=True, spawn
tilde notation, in which case they are seen as relative to this function's <x>,<y>,<z>
parameters. Examples: "1 2 3", "~4 ~5 ~6"
<dimension> can be one of {"overworld", "the_nether", "the_end"} (default "overworld").
The <doBlockUpdates>, <spawnDrops> and <customFlags> parameters control block update behavior.
See the API documentation for more info.
Returns a string with one line for each block placement. If the block placement was successful,
the return line is "1" if the block changed, or "0" otherwise. If the placement failed, it is
the error message.
Returns a list with one string for each block placement. If the block placement was successful,
the string is "1" if the block changed, or "0" otherwise. If the placement failed, it is the
error message.
"""
url = f"{HOST}/blocks"
if customFlags != "":
blockUpdateQueryParam = f"customFlags={customFlags}"
blockUpdateParams = {"customFlags": customFlags}
else:
blockUpdateQueryParam = f"doBlockUpdates={doBlockUpdates}&spawnDrops={spawnDrops}"
blockUpdateParams = {"doBlockUpdates": doBlockUpdates, "spawnDrops": spawnDrops}

url = (f"{HOST}/blocks?x={x}&y={y}&z={z}&{blockUpdateQueryParam}")
return _put(url, blockStr, retries=retries, timeout=timeout).text
parameters = {'x': x, 'y': y, 'z': z}.update(blockUpdateParams)

return _put(url, data=bytes(blockStr, "utf-8"), params=parameters, retries=retries, timeout=timeout).text.split("\n")

def runCommand(command: str, retries=5, timeout=None):

def runCommand(command: str, dimension: Optional[str] = None, retries=5, timeout=None):
"""Executes one or multiple Minecraft commands (separated by newlines).
The leading "/" must be omitted.
Returns a string with one line for each command. If the command was successful, the return line
<dimension> can be one of {"overworld", "the_nether", "the_end"} (default "overworld").
Returns a list with one string for each command. If the command was successful, the string
is its return value. Otherwise, it is the error message.
"""
url = f"{HOST}/command"
return _post(url, bytes(command, "utf-8"), retries=retries, timeout=timeout).text
return _post(url, bytes(command, "utf-8"), params={'dimension': dimension}, retries=retries, timeout=timeout).text.split("\n")


def getBuildArea(retries=5, timeout=None) -> Tuple[bool, Union[Tuple[int,int,int,int,int,int],str]]:
Expand All @@ -132,20 +159,28 @@ def getBuildArea(retries=5, timeout=None) -> Tuple[bool, Union[Tuple[int,int,int
return True, (x1, y1, z1, x2, y2, z2)


def getChunks(x: int, z: int, dx: int = 1, dz: int = 1, asBytes=False, retries=5, timeout=None):
def getChunks(x: int, z: int, dx: int = 1, dz: int = 1, dimension: Optional[str] = None, asBytes=False, retries=5, timeout=None):
"""Returns raw chunk data.
<x> and <z> specify the position in chunk coordinates, and <dx> and <dz> specify how many
chunks to get.
<dimension> can be one of {"overworld", "the_nether", "the_end"} (default "overworld").
If <asBytes> is True, returns raw binary data. Otherwise, returns a human-readable
representation.
On error, returns the error message instead.
"""
url = f"{HOST}/chunks?x={x}&z={z}&dx={dx}&dz={dz}"
url = f"{HOST}/chunks"
parameters = {
"x": x,
"z": z,
"dx": dx,
"dz": dz,
"dimension": dimension,
}
acceptType = "application/octet-stream" if asBytes else "text/raw"
response = _get(url, headers={"Accept": acceptType}, retries=retries, timeout=timeout)
response = _get(url, params=parameters, headers={"Accept": acceptType}, retries=retries, timeout=timeout)
if response.status_code >= 400:
eprint(f"Error: {response.text}")

Expand Down
12 changes: 6 additions & 6 deletions gdpc/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def getWorldSlice(rect: Rect):
def runCommand(command: str):
"""Executes one or multiple Minecraft commands (separated by newlines).\n
The leading "/" can be omitted.\n
Returns a string with one line for each command. If the command was successful, the return line
Returns a list with one string for each command. If the command was successful, the string
is its return value. Otherwise, it is the error message."""
if command[0] == '/':
command = command[1:]
Expand Down Expand Up @@ -260,11 +260,11 @@ def getBlockGlobal(self, position: ivec3):
if self.caching and positionTuple in self._cache.keys():
return self._cache[positionTuple]

response = di.getBlock(*position)
blockId: str = di.getBlock(*position)[0]["id"]
if self.caching:
self._cache[positionTuple] = response
self._cache[positionTuple] = blockId

return response
return blockId


def placeBlock(
Expand Down Expand Up @@ -417,7 +417,7 @@ def flush(blockBuffer: List[Tuple[ivec3, str]], commandBuffer: List[str]):
response = di.placeBlock(0, 0, 0, blockStr, doBlockUpdates=self._bufferDoBlockUpdates, retries=retries)
blockBuffer.clear()

for line in response.split("\n"):
for line in response:
if not line.isnumeric():
eprint(f"{TCOLORS['orange']}Warning: Server returned error upon placing buffered block:\n\t{TCOLORS['CLR']}{line}")

Expand All @@ -427,7 +427,7 @@ def flush(blockBuffer: List[Tuple[ivec3, str]], commandBuffer: List[str]):
response = runCommand("\n".join(commandBuffer))
commandBuffer.clear()

for line in response.split("\n"):
for line in response:
if not line.isnumeric():
eprint(f"{TCOLORS['orange']}Warning: Server returned error upon sending buffered command:\n\t{TCOLORS['CLR']}{line}")

Expand Down
2 changes: 1 addition & 1 deletion tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def testSynchronisation():
for _z in range(z - 1, z + 2):
for i in range(0, 128):
bws = ws.getBlockAt(_x, i, _z)
bdi = direct_interface.getBlock(_x, i, _z)
bdi = direct_interface.getBlock(_x, i, _z).get('id')
if (bws != bdi and bws != 'minecraft:void_air'):
print("{}: ws: {}, di: {}".format((_x, i, _z), bws, bdi))
error = True
Expand Down

0 comments on commit 47eb8d7

Please sign in to comment.