Oct 5, 2017 - MCEdit-Unified Dev Log: Road to v1.6.0.0 Part 1

Comments

Part 1: Getting builds flowing

Background

Since the beginning, the MCEdit-Unified team has had a very interesting build/release pipeline. Up to this point, all of our releases had been built by Project Organizer/Leader @Khroki. He had access to both Mac and Windows machines, so he was in the best position to build our releases. However, if we wanted to test a feature when it was compiled, we would have to wait for him to build a testing build. We thought about finding a continuous integration service to periodically build testing releases, but we couldn’t find one that ran a Windows environment, so we just stayed with out current system.

The problem

While our system had been successful so far, it relied on Khroki’s availability and resulted in us having to find days when both we were available to test the compiled build and address any last minute issues and that Khroki had enough time to build the release and notify us of any errors. With our various schedules and the fact that we’re located around the world, it took a lot of effort. However, now with none of us being able to contact Khroki, we had to build testing releases ourselves and without any references to our old build set up.

Setting the stage (Environment)

When I started looking for a Continuous Integration system that supported a Windows environment, I recalled that @codewarrior0 had a system similar to the one we wanted to implement. The service he used called Appveyor had an option for a Windows environment. The next step is to configure the said environment. Appveyor uses a YAML file to handle the environment and build stages. Since we can just build MCEdit-Unified from two commands, we just set one build stage to run

 pip install -r requirements.txt 

which installs all of the requirements needed to run MCEdit and another to run

 pyinstaller mcedit-ci.spec -y 

which runs our PyInstaller script to actually build the executable.

Building the build script

With the environment set up, I now needed to create a completely new build script for PyInstaller since we couldn’t get the one we’ve previously used for our releases from Khroki (see “The problem” section). While looking over how CodeWarrior0 organized his build system, I realized that the PyInstaller build script is actually just a python script with some fancy functions that PyInstaller imports to know how to build the executable. Using MCEdit2’s script as a reference, we created a automated build script that includes all of our data files (like filters, schematics, etc) andmoves DLL binaries into the executable’s folder. After all of this happens, the directory is collected into a ZIP file for uploading.

Conclusion

With all of this inplace, we’re now able to build testing releases on a regular basis without needing to install various utilities and programs. While we would’ve liked something like this set up earlier, we feel that introducing this for 1.6.0.0 will help us make a smoother and more stable release.

I’m going to try to make weekly posts on projects I’m working on and going into detail about design choices I make. I know this post wasn’t very technical and was a wall of text, but I’ll try to keep posts like this to a minimum. If you have any tips or things that I might be able to improve on, please put those in the comments or contacting me on Twitter.

Aug 23, 2016 - What's New in MCEdit-Unified

Comments

Since there has been a decent amount of time since our last release, I decided to write a quick summary of all the features/changes that have been made. Please note that some of these changes are internal, so you won’t necessarily notice them. This is probably not an exhaustive list, and is mostly just the feature I worked on.


Structure Blocks

In Minecraft 1.10, Structure Blocks were introduced to save in-game buildings to files, where you could them share them with friends or reimport them in another location in the world in-game via another Structure Block. (IE: Vanilla Minecraft’s version of Schematics) Well, MCEdit-Unified now has the ability to import and export .nbt files! Importing them is the same way as importing a regular schematic. However, for exporting, you will need to change the file type in the save dialog to change the format the file is saved in. (Schematic vs. Structure .nbt)

Filter creators can also use Structure files in their filters, but this will be detailed in a later section.


Blockstates

Blockstates have been present in Minecraft for a few versions, however, they way they would be saved wasn’t, until Structure .nbt files were introduced. To achieve this, I created an API to convert from Integer-based IDs to Blockstates. Here is a very brief and bare-bones snippet of how to use it:

from pymclevel import materials

api = materials.alphaMaterials.blockstate_api # Recommened way to access the API
api = BlockstateAPI.material_map[materials.alphaMaterials] # Another way

api.block_map[<id>] # Same as it's always been, give an ID and it will give the basename for the block
print api.block_map[3] # Prints "minecraft:dirt"

example_1 = api.idToBlockState(1, 2) # Give the ID and Data values, returns the Blockstate as a tuple
print example_1 # Prints "('stone', {'variant': 'smooth_granite'})"
# First index is the basename without the "minecraft:", the second is a dict with the Properties of that Blockstate

example_2 = api.blockstateToID("minecraft:stone", {"variant": "andesite"}))
# Give it the basename and a dict of properties
print example_2 # Prints a tuple of (<id>, <data>) In this case: (1, 5)

example_3 = materials.idToBlockstate(1, 2) # Same as "example_1", but this is not recommened

Note: This currently only works with PC Blocks, but materials.<function> (like the way in example_3) will always point to PC Block definitions/”alphaMaterials”


Additions to Filters

BlockstateAPI

With Minecraft moving away from IDs and using a string/name system for identifying blocks, you can now have blocks inputs based on a Blockstate string. Example:

from pymclevel.materials import alphaMaterials
inputs = (
	("Block Method #1", ("block", "minecraft:stone[variant=granite]")),
	("Block Method #2", alphaMaterials["minecraft:stone[variant=granite]"]),
	("Another Option", true),<br>
    )


This will still present a regular Block picker option to the user

StructureNBT

You can also now interact with Structure NBT files by using the StructureNBT class in pymclevel.schematics. Here is an example:

from pymclevel.schematic import StructureNBT

structure = StructureNBT(filename="/Some/Test/File.nbt")

structure.Blocks[0,0,0] = (5, 3) # Coordinate order is x,y,z, values are (\<id\>, \<data\>) tuples

structure.Palette # List of Blockstates in complete form. IE: minecraft:planks[variant=jungle]

schem = structure.toSchematic() # Converts and returns the StructureNBT in Schematic format

new_schem = Structure.fromSchematic("/Some/Schematic.Schematic") # Converts a Schematic to StructureNBT

structure.save("/New_Structure.nbt") # I'll let you figure out what this one does...


Block Info Summaries

Hovering over a block now gives a summary of important information about the Block. Example


Static Block Definitions

Due to various reasons, I am (unofficially) stating that accessing the static block definitions is deprecated and shouldn’t be used. They will still work, however. I’ve come to this decision due to the following reasons:

  • Static Block definitions are missing lots of blocks
    • It’s hard to find which blocks are missing
  • Naming and Data values are inconsistent
    • Some Blocks have all of their various Data values present, others have none
    • Facing Direction Data values are also inconsistent (in Minecraft)
  • Blocks with Data values are sometimes hard to name (IE: Blocks that can face different directions)
  • Blockstates/names can exist across various editions, while Static definitions don’t
    • IE: “minecraft:grass_path” points to 208 for PC/Java, while it points to 198 for Pocket Edition
      • We actually have this problem with our renderer, to check it out, open up a MCPE world and look at a Grass Path block or here
  • Aren’t based off of our definition files
    • They have to manually added to pymclevel/materials.py
  • Requires knowledge of what kind of materials are being used
    • While PC (Java) edition supports Command Blocks, MCPE doesn’t, so if a filter puts a Command Block in a MCPE world, it could cause the game to crash or perform unexpected behaviour

Here’s an example of why using Static Block Definitions is bad:

 from pymclevel.materials import alphaMaterials
 
 inputs = (
	("Block", alphaMaterials.EndRod),
 )
 
 def perform(level, box, options):
	
	block = options["Block"].ID
	origin_x = box.minx + 1
	origin_y = box.miny + 1
	origin_z = box.minz + 1
	
	level.setBlockAt(origin_x, origin_y, origin_z, alphaMaterials.Purpur.ID)
	
	level.setBlockAt(origin_x, origin_y + 1, origin_z, block)
	level.setBlockAt(origin_x, origin_y - 1, origin_z, block)
	
	level.setBlockAt(origin_x + 1, origin_y, origin_z, block)
	level.setBlockAt(origin_x - 1, origin_y, origin_z, block)
	
	level.setBlockAt(origin_x, origin_y, origin_z + 1, block)
	level.setBlockAt(origin_x, origin_y, origin_z - 1, block)

As you could probably tell, this is a simple filter, it just puts End Rods on every side of a Purpur block. However, it places completely different blocks if it’s in a PC world vs. a Pocket Edition world:
PC World Output
Pocket Edition World Output (This is renderer issue I mentioned earlier, those wierdly textured End Rods are actually Grass path blocks, and would be so if opened in MCPE)

This is not only inconsistent behaviour (the Grass Path block vs. the End Rod), but it introduces a Unknown Block into the world, which could cause a crash. It also makes the output unpredicatable, if the filter is ran with default inputs (and in a MCPE world), the block picked is not the block actually placed, which would confuse the user.

So in conclusion, I would like to ask filter creators to use the Blockstate API for any new filters they create after the next release (preferably use the way used in “Block Method #1”). Again, the old way will still work, so your filters won’t break, at least until PC switches completely from IDs to Blockstates.

Sep 8, 2015 - Two Together and status update

Hello again! I felt it was time for another update on a variety of things since it has been about a month since my last post. So let’s get on with it.

Two Together

Ever since the public demo of Two Together, I have gotten amazing feedback on how to improve the game and make it better. However, with school and life sometimes getting in the way of me being able to work on the various projects I love to contribute to, I have decided to open source it so anyone who wants to learn how to make games (with my method/code layout) can have some help. This will also allow me to be able to get help when I get stuck on a problem/bug. The Github repository can be found here

MCEdit-Unified

Recently I tweeted a picture teasing a new “waypoints” feature in MCEdit-Unified. I want to explain the details of how they will be stored before the update is released. To start off, all the waypoints will be stored in a NBT file named “mcedit_waypoints.dat” in the world save folder. This NBT format outline can be found here Each world will have their own mcedit_waypoints.dat file, and to remove all waypoints in a world, you just need to delete the file. You will also be able to create/delete waypoints through the waypoint dialog inside MCEdit