summaryrefslogtreecommitdiffstats
path: root/Tools
diff options
context:
space:
mode:
Diffstat (limited to 'Tools')
-rw-r--r--Tools/BlockTypePaletteGenerator/.gitignore5
-rw-r--r--Tools/BlockTypePaletteGenerator/Generator.lua180
-rw-r--r--Tools/BlockTypePaletteGenerator/Readme.md63
-rw-r--r--Tools/BlockTypePaletteGenerator/UpgradeGenerator.lua223
4 files changed, 355 insertions, 116 deletions
diff --git a/Tools/BlockTypePaletteGenerator/.gitignore b/Tools/BlockTypePaletteGenerator/.gitignore
new file mode 100644
index 000000000..234b0c873
--- /dev/null
+++ b/Tools/BlockTypePaletteGenerator/.gitignore
@@ -0,0 +1,5 @@
+# Ignore the scripts' outputs:
+*.json
+*.btp.txt
+UpgradeBlockTypePalette.txt
+
diff --git a/Tools/BlockTypePaletteGenerator/Generator.lua b/Tools/BlockTypePaletteGenerator/Generator.lua
index f33f2b789..b052ffa92 100644
--- a/Tools/BlockTypePaletteGenerator/Generator.lua
+++ b/Tools/BlockTypePaletteGenerator/Generator.lua
@@ -1,14 +1,33 @@
--- lib/lunajson/src/ is not in default Lua package paths
+-- Generator.lua
+
+--[[
+Crafts an intermediate block palette format to be read by Cuberite.
+It processes the blocks.json report file (https://wiki.vg/Data_Generators)
+into a file that can be loaded into a BlockTypePalette (and is to be stored
+as Server/Protocol/<version>/base.btp.txt).
+
+The output format is the regular TSV BlockTypePalette, described in the
+$/src/BlockTypePalette.h file.
+--]]
+
+
+
+
+-- Allow Lua to load libraries in our subfolder:
package.path = 'lib/lunajson/src/?.lua;' .. package.path;
+
+
+
+
--- Prints usage instructions to stdout.
--- If the optional `message` is passed, output is prepended by message _and_
+-- If the optional `aMessage` is passed, output is prepended by message _and_
-- redirected to stderr.
-function usage(message)
- if message then
+local function usage(aMessage)
+ if aMessage then
io.output(io.stderr);
- io.write(message, "\n\n");
+ io.write(aMessage, "\n\n");
end
io.write(
"Usage: lua Generator.lua INPUTFILE OUTPUTFILE\n"..
@@ -22,70 +41,125 @@ function usage(message)
end
--- Test whether the script is run in a path where it can load it's libraries
-if not pcall(function() require("lunajson.decoder") end) then
- usage("Could not load required libraries, please run `Generator.lua` "..
- "within its directory and make sure to run `git submodule update`.");
-end
--- Check/Prepare CLI arguments
-local inpath, outpath = ...;
-io.input(io.stdin);
-io.output(io.stdout);
-if select("#", ...) ~= 2 then
- usage("Incorrect number of arguments.");
+--- Parses the JSON registry into a Lua table
+--[[ The returned array-table has the following format:
+{
+ { id = 1, blockTypeName = "minecraft:stone", properties = {key = value, ...} },
+ ...
+}
+--]]
+local function parseRegistry(aBlockRegistryJsonStr)
+ assert(type(aBlockRegistryJsonStr) == "string")
+
+ local lj = require("lunajson")
+ local input = lj.decode(aBlockRegistryJsonStr)
+ local registry = {}
+ local idx = 1
+ for blockTypeName, blockData in pairs(input) do
+ for _, state in pairs(blockData.states) do
+ registry[idx] = {
+ id = state.id,
+ blockTypeName = blockTypeName,
+ properties = state.properties,
+ }
+ idx = idx + 1
+ end
+ end
+ return registry
end
-if inpath ~= "-" then
- local handle, err = io.open(inpath, "r");
- io.input(handle or usage(err));
-end
-if outpath ~= "-" then
- local handle, err = io.open(outpath, "w");
- io.output(handle or usage(err));
-end
--- Main program starts here
-local decode = (require("lunajson.decoder"))();
-local encode = (require("lunajson.encoder"))();
-local input = decode(io.input():read("*a"));
-local registry = {};
-local max_id = -1;
+--- Serializes the properties from the JSON / array table format into a single output string
+-- Concatenates all properties with \t as the delimiting character
+local function serializeProperties(aProperties)
+ local res = {}
+ local idx = 1
+ for k, v in pairs(aProperties or {}) do
+ res[idx] = k
+ res[idx + 1] = v
+ idx = idx + 2
+ end
+ return table.concat(res, "\t")
+end
+
-for blockname, blockdata in pairs(input) do
- for i = 1, #(blockdata.states or {}) do
- local state = blockdata.states[i];
- assert(registry[state.id + 1] == nil, "Ensure no duplicate IDs");
- -- needed in the end to verify we got no holes in the array:
- max_id = math.max(max_id, state.id);
- registry[state.id + 1] = {
- id = assert(state.id, "id is required."),
- name = assert(blockname, "Block type name is required."),
- -- default = state.default or nil, -- may need this later
- props = state.properties,
- };
+--- Returns the prefix that is common for all block type names in the registry
+-- aRegistry is the parsed registry, as returned from parseRegistry()
+local function findCommonPrefix(aRegistryTable)
+ local prefix = aRegistryTable[1].blockTypeName
+ local len = string.len(prefix)
+ local sub = string.sub
+ for _, block in ipairs(aRegistryTable) do
+ while (sub(block.blockTypeName, 1, len) ~= prefix) do
+ len = len - 1
+ if (len == 0) then
+ return ""
+ end
+ prefix = sub(prefix, 1, len)
+ end
end
+ return prefix
end
--- The following assertion is not necessary by the current spec, but is required
--- by how lunajson distinguishes objects from arrays. Also if this fails, it is
--- _very_ likely that the input file is faulty.
-assert(#registry == max_id + 1, "Ensure that registry has contiguous keys");
-local out = {
- Metadata = {
- ProtocolBlockTypePaletteVersion = 1
- },
- Palette = registry
-};
-io.write(encode(out), "\n");
+
+-- Test whether the script is run in a path where it can load it's libraries
+if not(pcall(function() require("lunajson") end)) then
+ usage(
+ "Could not load required libraries, please run `Generator.lua` " ..
+ "within its directory and make sure to run `git submodule update`."
+ )
+end
+
+-- Check/Prepare CLI arguments
+local inpath, outpath = ...;
+inpath = inpath or "blocks.json"
+outpath = outpath or "base.btp.txt"
+if (inpath ~= "-") then
+ local handle, err = io.open(inpath, "r")
+ io.input(handle or usage(err))
+end
+if (outpath ~= "-") then
+ local handle, err = io.open(outpath, "w")
+ io.output(handle or usage(err))
+end
+
+-- Parse the registry:
+local registry = parseRegistry(io.input():read("*a"))
+local commonPrefix = findCommonPrefix(registry)
+
+-- Sort the entries:
+table.sort(registry,
+ function (entry1, entry2)
+ return (entry1.id < entry2.id)
+ end
+)
+
+-- Write out the output format:
+io.write("BlockTypePalette\n")
+io.write("FileVersion\t1\n")
+io.write("CommonPrefix\t", commonPrefix, "\n")
+io.write("\n")
+local prefixLen = string.len(commonPrefix) + 1
+for _, entry in ipairs(registry) do
+ local props = serializeProperties(entry.properties)
+ if (props ~= "") then
+ props = "\t" .. props
+ end
+ io.write(
+ entry.id, "\t",
+ string.sub(entry.blockTypeName, prefixLen),
+ props, "\n"
+ )
+end
diff --git a/Tools/BlockTypePaletteGenerator/Readme.md b/Tools/BlockTypePaletteGenerator/Readme.md
deleted file mode 100644
index dc479d16f..000000000
--- a/Tools/BlockTypePaletteGenerator/Readme.md
+++ /dev/null
@@ -1,63 +0,0 @@
-This generator crafts an intermediate index format to be read by cuberite
-
-# Running
-
-Run `lua ./Generator.lua`, pass `blocks.json` as first argument to the script
-and the desired output location as 2nd argument.
-
-Make sure to run the Generator from within its directory (`cd` into the path
-where `Generator.lua` is.)
-
-## Examples
-
-```bash
-SERVER=/path/to/server.jar
-java -cp "$SERVER" net.minecraft.data.Main --reports &&
-lua Generator.lua \
- generated/reports/blocks.json \
- ../../Server/Protocol/1.13/ProtocolBlockTypePalette.json
-```
-
-```bash
-SERVER=/path/to/server.jar
-java -cp "$SERVER" net.minecraft.data.Main --reports &&
-lua Generator.lua - -\
- < generated/reports/blocks.json \
- > ../Server/Protocol/1.13/ProtocolBlockTypePalette.json
-```
-
-## Output format
-
-The Format is a `JSON` document containing an object with at least two keys at
-the top level: `Metadata` and `Palette`.
-
-`Metadata` contains document metadata, namely a key `"ProtocolBlockType": 1`.
-
-`Palette` contains an array of objects. Each of these objects has at least the
-keys `id`, `name` and an optional `props` key that contains the individual
-properties of the current state. These properties are a KV dict of pure strings.
-
-The order of the array or object elements is not significant. `id` is unique.
-
-```json
-{
- "Metadata": {
- "ProtocolBlockType": 1
- },
- "Palette": [{
- "id": 0,
- "name": "minecraft:air"
- }, {
- "id": 1,
- "name": "minecraft:stone"
- }, {
- "id": 221,
- "name": "minecraft:dark_oak_leaves",
- "props": {
- "persistent": "false",
- "distance": "4"
- }
- }
- ]
-}
-``` \ No newline at end of file
diff --git a/Tools/BlockTypePaletteGenerator/UpgradeGenerator.lua b/Tools/BlockTypePaletteGenerator/UpgradeGenerator.lua
new file mode 100644
index 000000000..47fa62109
--- /dev/null
+++ b/Tools/BlockTypePaletteGenerator/UpgradeGenerator.lua
@@ -0,0 +1,223 @@
+-- UpgradeGenerator.lua
+
+--[[ Creates the UpgradeBlockTypePalette out of JSON data of the Minutor project
+(https://github.com/mrkite/minutor/blob/master/definitions/vanilla_ids.json
+
+Parses the JSON into memory, then walks each block's "id" member and possibly
+the "variants" sub-member to read the block types. The name is either present as "flatname",
+or is synthesized from the internal Minutor "name" by lowercasing and replacing spaces
+with underscores.
+
+Expects two parameters, the input file and output file; either can be replaced by
+a "-" to use stdin / stdout instead. If not given, the input file defaults to
+"vanilla_ids.json" and the output file defaults to "UpgradeBlockTypePalette.txt"
+
+The output format is the upgrade TSV BlockTypePalette, described in the
+$/src/BlockTypePalette.h file.
+--]]
+
+
+
+
+
+
+-- Allow Lua to load libraries in our subfolder:
+package.path = 'lib/lunajson/src/?.lua;' .. package.path;
+
+
+
+
+
+
+--- Splits the full flat name into flat name and properties
+-- "minecraft:carrots:age:0" -> "minecraft:carrots", {age = 0}
+local function splitFlatName(aFullFlatName)
+ local props = {}
+ local numParts = 0
+ local flatName = ""
+ local propKey = ""
+ aFullFlatName:gsub("([^:]+)",
+ function (aPart)
+ if (numParts == 0) then
+ flatName = aPart
+ elseif (numParts == 1) then
+ flatName = flatName .. ":" .. aPart
+ elseif (numParts % 2 == 0) then
+ propKey = aPart
+ else
+ props[propKey] = aPart
+ end
+ numParts = numParts + 1
+ end
+ )
+ return flatName, props
+end
+
+
+
+
+
+--- Returns the minecraft block name, created from the flat name if present, or synthesized
+-- from the Minutor name
+-- If the flat name contains encoded block properties, it returns those properties as a dict-table
+-- in the second return value
+local function processBlockName(aFlatName, aMinutorName)
+ if (aFlatName) then
+ assert(type(aFlatName) == "string")
+ return splitFlatName(aFlatName)
+ end
+ if not(type(aMinutorName) == "string") then
+ return nil
+ end
+ return "minecraft:" .. (aMinutorName:lower():gsub(" ", "_")), {}
+end
+
+
+
+
+
+
+--- Serializes the properties from the JSON / array table format into a single output string
+-- Concatenates all properties with \t as the delimiting character
+local function serializeProperties(aProperties)
+ local res = {}
+ local idx = 1
+ for k, v in pairs(aProperties or {}) do
+ res[idx] = k
+ res[idx + 1] = v
+ idx = idx + 2
+ end
+ return table.concat(res, "\t")
+end
+
+
+
+
+
+--- Parses the vanilla_ids.json into a common registry format
+-- The returned registry is an array-table of
+-- {blockType = 1, blockMeta = 2, blockTypeName = "name", properties = {key = value, ...}}
+local function parseRegistry(aJsonString)
+ assert(type(aJsonString) == "string")
+
+ -- Parse the JSON:
+ local lj = require("lunajson")
+ local input = lj.decode(aJsonString)
+ if (not(input) or (input["type"] ~= "block") or not(input["data"])) then
+ error("The input file doesn't contain vanilla IDs.")
+ end
+
+ -- Create the registry:
+ local registry = {}
+ local idx = 1
+ for _, entry in pairs(input["data"]) do
+ local id = entry["id"]
+ local parentBlockTypeName, props = processBlockName(entry["flatname"], entry["name"])
+ registry[idx] =
+ {
+ blockType = id,
+ blockMeta = 0,
+ blockTypeName = parentBlockTypeName,
+ properties = props,
+ }
+ idx = idx + 1
+ for _, variant in pairs(entry["variants"] or {}) do
+ local blockTypeName, props = processBlockName(variant["flatname"], variant["name"])
+ if not(blockTypeName) then
+ -- Some blocks don't have all their variants named ("brown mushroom block"), use the parent name in such a case
+ blockTypeName = parentBlockTypeName
+ end
+ registry[idx] =
+ {
+ blockType = id,
+ blockMeta = variant["data"],
+ blockTypeName = blockTypeName,
+ properties = props,
+ }
+ idx = idx + 1
+ end
+ end
+ return registry
+end
+
+
+
+
+
+--- Returns the prefix that is common for all block type names in the registry
+-- aRegistry is the parsed registry, as returned from parseRegistry()
+local function findCommonPrefix(aRegistryTable)
+ local prefix = aRegistryTable[1].blockTypeName
+ local len = string.len(prefix)
+ local sub = string.sub
+ for _, block in ipairs(aRegistryTable) do
+ while (sub(block.blockTypeName, 1, len) ~= prefix) do
+ len = len - 1
+ if (len == 0) then
+ return ""
+ end
+ prefix = sub(prefix, 1, len)
+ end
+ end
+ return prefix
+end
+
+
+
+
+
+-- Test whether the script is run in a path where it can load it's libraries
+if not(pcall(function() require("lunajson") end)) then
+ error(
+ "Could not load required libraries, please run `UpgradeGenerator.lua` " ..
+ "within its directory and make sure to run `git submodule update`."
+ )
+end
+
+-- Check/Prepare CLI arguments
+local inpath, outpath = ...;
+inpath = inpath or "vanilla_ids.json"
+outpath = outpath or "UpgradeBlockTypePalette.txt"
+if (inpath ~= "-") then
+ local handle, err = io.open(inpath, "r")
+ io.input(handle or usage(err))
+end
+if (outpath ~= "-") then
+ local handle, err = io.open(outpath, "w")
+ io.output(handle or usage(err))
+end
+
+-- Parse the registry:
+local registry = parseRegistry(io.input():read("*a"))
+local commonPrefix = findCommonPrefix(registry)
+
+-- Sort the entries:
+table.sort(registry,
+ function (entry1, entry2)
+ if (entry1.blockType < entry2.blockType) then
+ return true
+ elseif (entry1.blockType > entry2.blockType) then
+ return false
+ else
+ return (entry1.blockMeta < entry2.blockMeta)
+ end
+ end
+)
+
+-- Write out the output format:
+io.write("UpgradeBlockTypePalette\n")
+io.write("FileVersion\t1\n")
+io.write("CommonPrefix\t", commonPrefix, "\n")
+io.write("\n")
+local prefixLen = string.len(commonPrefix) + 1
+for _, entry in ipairs(registry) do
+ local props = serializeProperties(entry.properties)
+ if (props ~= "") then
+ props = "\t" .. props
+ end
+ io.write(
+ entry.blockType, "\t", entry.blockMeta, "\t",
+ string.sub(entry.blockTypeName, prefixLen),
+ props, "\n"
+ )
+end