Task 384: .MCTEMPLATE File Format
Task 384: .MCTEMPLATE File Format
.MCTEMPLATE File Format Specifications
The .MCTEMPLATE file format is used for Minecraft Bedrock Edition world templates. It is essentially a ZIP archive (using standard ZIP compression) renamed with the .mctemplate extension. The format contains a specific set of files and folders that define a pre-configured world template for the game. The specifications are based on official Minecraft documentation from Microsoft, requiring a valid manifest.json for importability into the game. The format is not a proprietary binary structure but a container for game data files.
1. List of All Properties Intrinsic to This File Format
The "properties" refer to the internal structure, required/optional files, and key elements that make up the file format. These are intrinsic to how the file is organized on the file system (as a ZIP container) and how Minecraft interprets it. The format has no custom header or binary metadata outside of standard ZIP; all properties are derived from the contained files and their contents. Here's the comprehensive list:
ZIP Container Properties:
- Compression method: Deflate (standard ZIP compression).
- File extension: .mctemplate (renamed from .zip to indicate it's a Minecraft world template).
- MIME type: application/zip (implicit, as it's a ZIP file).
- Root level files/folders: No extra nesting; files are zipped directly (zipping a folder would create an invalid structure for import).
Required Files and Their Properties:
- manifest.json: A JSON file at the root defining the template metadata.
- Structure (format_version: 2):
- header: Object with name ("pack.name"), description ("pack.description"), version (array e.g. [1, 0, 0]), uuid (string, unique UUID).
- modules: Array of objects, each with version (array e.g. [1, 0, 0]), type ("world_template"), uuid (string, unique UUID different from header's).
- Optional fields: allow_random_seed (boolean, true to enable random seeds; requires deleting the db folder).
- level.dat: NBT (Named Binary Tag) file containing world data like seed, game rules, player spawn, etc. Compressed with GZip.
- Properties: Binary structure with root compound tag including LevelName, SpawnX/Y/Z, GameType, etc.
Optional but Common Files and Folders:
- texts folder: Contains localization files.
- en_US.lang: Text file with key-value pairs, e.g., "pack.name=Template Name\npack.description=Template Description".
- languages.json: JSON array listing supported languages, e.g., ["en_US"] (English required; others like "es_ES" for multi-language support).
- world_icon.jpeg: JPEG image (800x450 pixels recommended) for the template icon in the game UI.
- db folder: LevelDB database folder containing chunk data (world terrain, entities, etc.).
- Properties: Binary key-value store; files like 000001.ldb, LOG, MANIFEST, etc.
- Add-On Related Files (if template includes packs):
- behavior_packs folder: Contains add-on behavior packs (short folder names ≤10 chars to avoid path issues).
- resource_packs folder: Contains add-on resource packs (similar naming constraint).
- world_behavior_pack_history.json: JSON history of applied behavior packs.
- world_behavior_packs.json: JSON list of current behavior packs.
- world_resource_pack_history.json: JSON history of applied resource packs.
- world_resource_packs.json: JSON list of current resource packs.
These properties ensure the template can be imported and used to generate new worlds in Minecraft Bedrock Edition. Invalid structures (e.g., missing manifest.json or extra folder levels) prevent import.
2. Two Direct Download Links for .MCTEMPLATE Files
- https://github.com/kirbycope/SkyBlock-Bedrock/raw/main/SkyBlock-Bedrock.mctemplate (SkyBlock world template)
- https://drive.google.com/uc?export=download&id=1e9sFJ42Z3j5momPoDXFbNAgFSc0dVv9E (Star Block world template)
3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .MCTEMPLATE File Dump
This is a self-contained HTML page with embedded JavaScript that allows dragging and dropping a .mctemplate file. It uses JSZip to unzip the file, parses the key files (manifest.json, en_US.lang, etc.), and dumps the properties to the screen. Include JSZip library via CDN for simplicity. This can be embedded in a Ghost blog post as raw HTML.
4. Python Class for .MCTEMPLATE Handling
This Python class uses zipfile
to open the file, extracts and parses properties, and supports reading, writing (creating a new .mctemplate from properties), and printing.
import zipfile
import json
import os
from io import BytesIO
import zlib # For potential NBT, but simplified here
class MCTemplateHandler:
def __init__(self, filepath):
self.filepath = filepath
self.properties = {}
self._read()
def _read(self):
with zipfile.ZipFile(self.filepath, 'r') as zf:
# ZIP properties
self.properties['zip_info'] = {
'filename': os.path.basename(self.filepath),
'size': os.path.getsize(self.filepath)
}
# manifest.json
if 'manifest.json' in zf.namelist():
with zf.open('manifest.json') as f:
self.properties['manifest'] = json.load(f)
# texts/en_US.lang
if 'texts/en_US.lang' in zf.namelist():
with zf.open('texts/en_US.lang') as f:
self.properties['en_US_lang'] = f.read().decode('utf-8')
# languages.json
if 'texts/languages.json' in zf.namelist():
with zf.open('texts/languages.json') as f:
self.properties['languages'] = json.load(f)
# world_icon.jpeg
if 'world_icon.jpeg' in zf.namelist():
with zf.open('world_icon.jpeg') as f:
self.properties['world_icon'] = {'size': len(f.read())}
# level.dat (simple check, NBT parsing omitted for brevity)
if 'level.dat' in zf.namelist():
self.properties['level_dat'] = 'Present'
# db folder
db_files = [f for f in zf.namelist() if f.startswith('db/')]
if db_files:
self.properties['db'] = {'file_count': len(db_files)}
# Add-on files
addon_keys = ['behavior_packs/', 'resource_packs/', 'world_behavior_pack_history.json', 'world_behavior_packs.json', 'world_resource_pack_history.json', 'world_resource_packs.json']
for key in addon_keys:
if any(f.startswith(key) for f in zf.namelist()) or key in zf.namelist():
self.properties[key.strip('/')] = 'Present'
def print_properties(self):
print(json.dumps(self.properties, indent=4))
def write(self, new_filepath):
with zipfile.ZipFile(new_filepath, 'w', compression=zipfile.ZIP_DEFLATED) as zf:
# Write manifest
if 'manifest' in self.properties:
zf.writestr('manifest.json', json.dumps(self.properties['manifest']))
# Write en_US.lang
if 'en_US_lang' in self.properties:
zf.writestr('texts/en_US.lang', self.properties['en_US_lang'])
# Write languages.json
if 'languages' in self.properties:
zf.writestr('texts/languages.json', json.dumps(self.properties['languages']))
# Other files would require full data; simplified to key ones
# For full write, extract all in read and re-zip
# Example usage
# handler = MCTemplateHandler('example.mctemplate')
# handler.print_properties()
# handler.write('new.mctemplate')
5. Java Class for .MCTEMPLATE Handling
This Java class uses java.util.zip
to handle the ZIP, parses properties, supports read/write/print.
import java.io.*;
import java.util.*;
import java.util.zip.*;
import org.json.simple.*;
import org.json.simple.parser.*;
public class MCTemplateHandler {
private String filepath;
private Map<String, Object> properties = new HashMap<>();
public MCTemplateHandler(String filepath) {
this.filepath = filepath;
read();
}
private void read() {
try (ZipFile zf = new ZipFile(filepath)) {
// ZIP properties
properties.put("zip_info", Map.of("filename", new File(filepath).getName(), "size", new File(filepath).length()));
Enumeration<? extends ZipEntry> entries = zf.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
if (name.equals("manifest.json")) {
try (InputStream is = zf.getInputStream(entry)) {
properties.put("manifest", JSONValue.parse(new InputStreamReader(is)));
}
} else if (name.equals("texts/en_US.lang")) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(zf.getInputStream(entry)))) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) sb.append(line).append("\n");
properties.put("en_US_lang", sb.toString());
}
} else if (name.equals("texts/languages.json")) {
try (InputStream is = zf.getInputStream(entry)) {
properties.put("languages", JSONValue.parse(new InputStreamReader(is)));
}
} else if (name.equals("world_icon.jpeg")) {
properties.put("world_icon", Map.of("size", entry.getSize()));
} else if (name.equals("level.dat")) {
properties.put("level_dat", "Present");
} else if (name.startsWith("db/")) {
List<String> dbFiles = (List<String>) properties.getOrDefault("db_files", new ArrayList<>());
dbFiles.add(name);
properties.put("db_files", dbFiles);
} else if (name.startsWith("behavior_packs/") || name.equals("world_behavior_pack_history.json") /* add others */) {
properties.put(name.split("/")[0], "Present");
}
}
if (properties.containsKey("db_files")) {
properties.put("db", Map.of("file_count", ((List) properties.get("db_files")).size()));
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void printProperties() {
System.out.println(JSONValue.toJSONString(properties));
}
public void write(String newFilepath) {
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(newFilepath))) {
if (properties.containsKey("manifest")) {
ZipEntry ze = new ZipEntry("manifest.json");
zos.putNextEntry(ze);
zos.write(JSONValue.toJSONString(properties.get("manifest")).getBytes());
zos.closeEntry();
}
if (properties.containsKey("en_US_lang")) {
ZipEntry ze = new ZipEntry("texts/en_US.lang");
zos.putNextEntry(ze);
zos.write(((String) properties.get("en_US_lang")).getBytes());
zos.closeEntry();
}
// Add similar for other files; full implementation would copy all data
} catch (Exception e) {
e.printStackTrace();
}
}
// Example usage
// public static void main(String[] args) {
// MCTemplateHandler handler = new MCTemplateHandler("example.mctemplate");
// handler.printProperties();
// handler.write("new.mctemplate");
// }
}
(Note: Requires json-simple library for JSON handling.)
6. JavaScript Class for .MCTEMPLATE Handling
This JS class uses JSZip for handling in browser/node. For node, require 'jszip' and 'fs'.
const JSZip = require('jszip'); // For Node.js; in browser use CDN
const fs = require('fs'); // For Node.js
class MCTemplateHandler {
constructor(filepath) {
this.filepath = filepath;
this.properties = {};
this.read();
}
async read() {
const data = fs.readFileSync(this.filepath);
const zip = await JSZip.loadAsync(data);
// ZIP properties
this.properties.zip_info = { filename: this.filepath.split('/').pop(), size: data.length };
// manifest.json
if (zip.file('manifest.json')) {
const content = await zip.file('manifest.json').async('string');
this.properties.manifest = JSON.parse(content);
}
// texts/en_US.lang
if (zip.file('texts/en_US.lang')) {
this.properties.en_US_lang = await zip.file('texts/en_US.lang').async('string');
}
// languages.json
if (zip.file('texts/languages.json')) {
const content = await zip.file('texts/languages.json').async('string');
this.properties.languages = JSON.parse(content);
}
// world_icon.jpeg
if (zip.file('world_icon.jpeg')) {
const icon = await zip.file('world_icon.jpeg').async('nodebuffer');
this.properties.world_icon = { size: icon.length };
}
// level.dat
if (zip.file('level.dat')) {
this.properties.level_dat = 'Present';
}
// db folder
const dbFiles = Object.keys(zip.files).filter(f => f.startsWith('db/'));
if (dbFiles.length) {
this.properties.db = { file_count: dbFiles.length };
}
// Add-on files
const addonKeys = ['behavior_packs/', 'resource_packs/', 'world_behavior_pack_history.json', 'world_behavior_packs.json', 'world_resource_pack_history.json', 'world_resource_packs.json'];
addonKeys.forEach(key => {
if (Object.keys(zip.files).some(f => f.startsWith(key) || f === key)) {
this.properties[key.replace('/', '')] = 'Present';
}
});
}
printProperties() {
console.log(JSON.stringify(this.properties, null, 2));
}
async write(newFilepath) {
const zip = new JSZip();
if (this.properties.manifest) {
zip.file('manifest.json', JSON.stringify(this.properties.manifest));
}
if (this.properties.en_US_lang) {
zip.file('texts/en_US.lang', this.properties.en_US_lang);
}
if (this.properties.languages) {
zip.file('texts/languages.json', JSON.stringify(this.properties.languages));
}
// Add others similarly; for full, need all data stored
const content = await zip.generateAsync({ type: 'nodebuffer' });
fs.writeFileSync(newFilepath, content);
}
}
// Example usage (Node.js)
// const handler = new MCTemplateHandler('example.mctemplate');
// handler.printProperties();
// await handler.write('new.mctemplate');
7. C Class for .MCTEMPLATE Handling
This is in C++ (as "c class" likely means C++ for class support). Uses minizip or similar for ZIP; here assuming zlib/minizip included. Simplified parsing (JSON/NBT require libs like cJSON, nbt++).
#include <iostream>
#include <string>
#include <map>
#include <fstream>
#include <zip.h> // Assume minizip library
#include <cjson/cJSON.h> // For JSON parsing
class MCTemplateHandler {
private:
std::string filepath;
std::map<std::string, std::string> properties; // Simplified to string values
public:
MCTemplateHandler(const std::string& fp) : filepath(fp) {
read();
}
void read() {
zip_t* z = zip_open(filepath.c_str(), ZIP_RDONLY, nullptr);
if (!z) return;
// ZIP properties
struct stat st;
stat(filepath.c_str(), &st);
properties["zip_filename"] = filepath.substr(filepath.find_last_of("/\\") + 1);
properties["zip_size"] = std::to_string(st.st_size);
int num_entries = zip_get_num_entries(z, 0);
for (int i = 0; i < num_entries; ++i) {
const char* name = zip_get_name(z, i, 0);
if (std::string(name) == "manifest.json") {
zip_file_t* f = zip_fopen_index(z, i, 0);
char buf[4096];
zip_fread(f, buf, sizeof(buf));
zip_fclose(f);
cJSON* json = cJSON_Parse(buf);
properties["manifest"] = cJSON_Print(json);
cJSON_Delete(json);
} else if (std::string(name) == "texts/en_US.lang") {
zip_file_t* f = zip_fopen_index(z, i, 0);
char buf[4096];
zip_fread(f, buf, sizeof(buf));
zip_fclose(f);
properties["en_US_lang"] = buf;
} // Add similar for others, check existence for level.dat, db, etc.
}
zip_close(z);
}
void printProperties() {
for (const auto& p : properties) {
std::cout << p.first << ": " << p.second << std::endl;
}
}
void write(const std::string& newFilepath) {
zip_t* z = zip_open(newFilepath.c_str(), ZIP_CREATE | ZIP_TRUNCATE, nullptr);
if (!z) return;
if (properties.find("manifest") != properties.end()) {
zip_source_t* src = zip_source_buffer(z, properties["manifest"].c_str(), properties["manifest"].size(), 0);
zip_file_add(z, "manifest.json", src, ZIP_FL_OVERWRITE);
}
// Add similar for other files
zip_close(z);
}
};
// Example usage
// int main() {
// MCTemplateHandler handler("example.mctemplate");
// handler.printProperties();
// handler.write("new.mctemplate");
// return 0;
// }
(Note: Requires minizip and cJSON libraries; full NBT parsing would need additional libs.)