Skip to content

Commit

Permalink
Merge pull request #32 from Zhou-Shilin/feat-mcfunction-export
Browse files Browse the repository at this point in the history
feat: Add *.mcfunction export support
  • Loading branch information
Zhou-Shilin committed Jul 5, 2024
2 parents d3e36c8 + c805620 commit 0eeffce
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 44 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ BuilderGPT is an open source, free, AI-powered Minecraft structure generator. It
- [x] Generate structures
- [x] Preview rendered schematic in-program
- [x] Export generated structures to `*.schem` files
- [ ] Export generated structures to `*.mcfunction` files
- [x] Export generated structures to `*.mcfunction` files
- [x] **Advanced Mode** (Use Stable Diffusion/DALL-E to generate the design image and let `gpt-4-vision` generate the struture base on it.)
- [ ] Edit structures

Expand Down Expand Up @@ -149,6 +149,9 @@ pip install -r requirements.txt
5. Import the file into the game via worldedit or other tools. (Google is your best friend~~)
-->

Moved to [Wiki](https://github.com/CubeGPT/BuilderGPT/wiki).

<!--
### Python/UI (RECOMMEND)
1. Download `Source Code.zip` from [the release page](https://https://github.com/CubeGPT/BuilderGPT/releases) and unzip it.
2. Edit `config.yaml`, fill in your OpenAI Apikey. If you don't know how, remember that [Google](https://www.google.com/) and [Bing](https://www.bing.com/) are always your best friends.
Expand All @@ -163,6 +166,8 @@ pip install -r requirements.txt
4. Find your structure in `/generated/<name>.schem`.
5. Import the file into the game via worldedit or other tools. (Google is your best friend~~)
-->

## Contributing
If you like the project, you can give the project a star, or [submit an issue](https://github.com/CubeGPT/BuilderGPT/issues) or [pull request](https://github.com/CubeGPT/BuilderGPT/pulls) to help make it better.

Expand Down
68 changes: 47 additions & 21 deletions core.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def ask_dall_e(description: str):

return image_url

def text_to_schem(text: str):
def text_to_schem(text: str, export_type: str = "schem"):
"""
Converts a JSON string to a Minecraft schematic.
Expand All @@ -130,28 +130,54 @@ def text_to_schem(text: str):
try:
data = json.loads(text)
logger(f"text_to_command: loaded JSON data {data}")
schematic = mcschematic.MCSchematic()

for structure in data["structures"]:
block_id = structure["block"]
x = structure["x"]
y = structure["y"]
z = structure["z"]

if structure["type"] == "fill":
to_x = structure["toX"]
to_y = structure["toY"]
to_z = structure["toZ"]

for x in range(x, to_x + 1):
for y in range(y, to_y + 1):
for z in range(z, to_z + 1):
schematic.setBlock((x, y, z), block_id)

else:
schematic.setBlock((x, y, z), block_id)
if export_type == "schem":
schematic = mcschematic.MCSchematic()

for structure in data["structures"]:
block_id = structure["block"]
x = structure["x"]
y = structure["y"]
z = structure["z"]

if structure["type"] == "fill":
to_x = structure["toX"]
to_y = structure["toY"]
to_z = structure["toZ"]

for x in range(x, to_x + 1):
for y in range(y, to_y + 1):
for z in range(z, to_z + 1):
schematic.setBlock((x, y, z), block_id)

else:
schematic.setBlock((x, y, z), block_id)

return schematic

elif export_type == "mcfunction":
with open("generated/temp.mcfunction", "w") as f:
for structure in data["structures"]:
block_id = structure["block"]
x = structure["x"]
y = structure["y"]
z = structure["z"]

if structure["type"] == "fill":
to_x = structure["toX"]
to_y = structure["toY"]
to_z = structure["toZ"]

for x in range(x, to_x + 1):
for y in range(y, to_y + 1):
for z in range(z, to_z + 1):
f.write(f"setblock {x} {y} {z} {block_id}\n")

else:
f.write(f"setblock {x} {y} {z} {block_id}\n")

return None

return schematic

except (json.decoder.JSONDecodeError, KeyError, TypeError, ValueError, AttributeError, IndexError) as e:
logger(f"text_to_command: failed to load JSON data. Error message: {e}")
Expand Down
43 changes: 21 additions & 22 deletions ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import core
import browser

def get_schematic(description, progressbar):
def get_schematic(description, progressbar, export_type):
"""
Generate a schematic based on the given description.
Expand Down Expand Up @@ -46,12 +46,12 @@ def get_schematic(description, progressbar):
# Wait for the progress thread to finish
progress_thread.join()

schem = core.text_to_schem(response)
schem = core.text_to_schem(response, export_type=export_type)
progressbar.set(100)

return schem

def get_schematic_advanced(description, progressbar):
def get_schematic_advanced(description, progressbar, export_type):
"""
Generates a schematic using advanced mode.
Expand Down Expand Up @@ -79,7 +79,7 @@ def get_schematic_advanced(description, progressbar):
response = core.askgpt(config.SYS_GEN_ADV, config.USR_GEN_ADV.replace("%DESCRIPTION%", description), config.VISION_MODEL, image_url=image_url)
progressbar.set(90)

schem = core.text_to_schem(response)
schem = core.text_to_schem(response, export_type=export_type)
progressbar.set(100)

return schem
Expand All @@ -98,23 +98,28 @@ def generate(args: dict):
"""
description = args["Description"].get()
game_version = args["Game Version"].get()
export_type = args["Export Type"].get()

progressbar = args["Generation Progress"]

if config.ADVANCED_MODE:
schem = get_schematic_advanced(description, progressbar)
schem = get_schematic_advanced(description, progressbar, export_type)
else:
schem = get_schematic(description, progressbar)
schem = get_schematic(description, progressbar, export_type)

raw_name = core.askgpt(config.SYS_GEN_NAME, config.USR_GEN_NAME.replace("%DESCRIPTION%", description), config.NAMING_MODEL, disable_json_mode=True)

name = raw_name + "-" + str(uuid.uuid4())

version_tag = core.input_version_to_mcs_tag(game_version)

schem.save("generated", name, version_tag)

print("Schematic saved as " + name + ".schem")
if schem is None:
shutil.copy("generated/temp.mcfunction", "generated/" + name + ".mcfunction")
os.remove("generated/temp.mcfunction")
print("Schematic saved as " + name + ".mcfunction")
else:
schem.save("generated", name, version_tag)
print("Schematic saved as " + name + ".schem")

return True

Expand All @@ -137,6 +142,10 @@ def render(args: dict):

print(f"Set schematic path to {schematic_path}.")

if ".mcfunction" in schematic_path:
MessageBox.error("*.mcfunction rendering is not supported. Please select a schematic file (.schem) instead of a mcfunction file.")
return

try:
os.remove("temp/waiting_for_upload.schem")
os.remove("temp/screenshot.png")
Expand All @@ -149,16 +158,9 @@ def render(args: dict):
shutil.copy(schematic_path, "temp/waiting_for_upload.schem")
progress_bar.set(20)

if args["Headless-Enable"].get() == 1:
print("Headless mode enabled.")
is_headless = True
else:
print("Headless mode disabled.")
is_headless = False

print("Rendering...")
with sync_playwright() as playwright:
browser.run(playwright, progress_bar, is_headless=is_headless)
browser.run(playwright, progress_bar, is_headless=True)

print("Rendering finished. Result:")
root.print_image("temp/screenshot.png")
Expand Down Expand Up @@ -296,18 +298,15 @@ def raise_error(args: dict):
# Generate Page
root.add_notebook_tool(InputBox(name="Game Version", default="1.20.1", label_info="Game Version", tab_index=0))
root.add_notebook_tool(InputBox(name="Description", default="A simple house", label_info="Description", tab_index=0))
root.add_notebook_tool(RadioObviousToolButton(options=["schem", "mcfunction"], default="schem", name="Export Type", title="Export Type", tab_index=0))

root.add_notebook_tool(Progressbar(name="Generation Progress", title="Progress", tab_index=0))
root.add_notebook_tool(RunButton(bind_func=generate, name="Generate", text="Generate", tab_index=0))

# Render Page
root.add_notebook_tool(ChooseFileTextButton(name="Schematic File Path", label_info="Schematic File", tab_index=1))
root.add_notebook_tool(Progressbar(name="Rendering Progress", title="Progress", tab_index=1))
render_button = HorizontalToolsCombine([
ToggleButton(options=("Enable", 1), name="Headless", title="Headless",tab_index=1),
RunButton(bind_func=render, name="Render", text="Render", tab_index=1)
])
root.add_notebook_tool(render_button)
root.add_notebook_tool(RunButton(bind_func=render, name="Render", text="Render", tab_index=1))

# Settings Page
root.add_notebook_tool(InputBox(name="API_KEY", default=config.API_KEY, label_info="API Key", tab_index=2))
Expand Down

0 comments on commit 0eeffce

Please sign in to comment.