Overview
Each Allay project resembles one or more coherent packs. For example adding a new block into the game requires both a behavior pack for the behavior of the block and a resource pack for the textures. If two or more packs are not coherent they should not belong to the same Allay project.
Allay is a command-line application which can be used to create add-ons for Minecraft: Bedrock Edition. It’s goal is to take over the boring work you would do when manually developing an add-on such as generating UUIDs and handling localization.
Allay is a command-line tool which is controlled from a terminal. We expect that you know how to navigate between files in a terminal and how to edit files.
You can use an editor like Visual Studio Code
which lets you use a terminal within the editor. This means you can easily
navigate between files and edit them in a comfortable way while using the terminal
to run simple commands like allay build
.
This project is in a work-in-progress status. Many features as well as links might not work yet. Consider waiting for a stable release if you want to use this program.
Philosophy
allay build
and double-click on the built add-on. It can be that simple.- We believe that the JSON format should be used by machines and not humans.
Therefore TOML is used for configuartion and scripts
can be used to easily convert formats like YAML into
JSON which can be read by Minecraft. The
manifest.json
is generated automatically by Allay. - Generating UUIDs over and over again can be troublesome so Allay fully takes
care of managing them in your project. For instance the
allay uuid
command can be used to get a nice overview of used UUIDs. - Built-in advanced handling of languages: Translate your project to mexican
spanish (
es-mx
) and it will be available for spanish spanish (es-es
) as well. Allay extends your existing.lang
file (or creates new ones until each language is covered) by adding the appropiate name and description of the pack. - Written in the Rust programming language which is blazingly fast.1
Glossary
To avoid misconception between interchangably used terms, Allay strictly defines the following terms. They are used this way in the documentation and projects related to Allay.
Term | Meaning |
---|---|
Add-on | An add-on might be either a built behavior pack, resource pack, skin pack or world template. |
Directory | Windows users might not be familar with this word. Throughout the documentation we will use this term which basically just means “folder”. |
Pack | Same as an add-on but not the built version. |
Scripts
Scripts enable a powerful way of automating several tasks. The script templating
provides support for ERB enabling a way of flow
control and generation.
allay add templating
<%# using erb %>
<% 10.times do |i| %>
scoreboard objectives add position<%= i %> dummy
<% end %>
[scripts]
base-path = "scripts"
pre = [
{ run = "templating.rb", with = "ruby" }
]
What’s next?
There is much more to discover and we hope this tool makes your Minecraft developing experience more confortable. You can continue reading through the documentation to learn how to use Allay.
Installation
Windows
TODO
Android
TODO
Linux / macOS
TODO
Example Project
In this chapter, we will cover how to create a world template which includes a behavior pack as well as an resource pack.
Step 1: Initialize the project
mkdir lost-sword
cd lost-sword
allay init
New-Item -Path . -Name "lost-sword" -ItemType "directory"
Set-Location -Path '.\lost-sword'
allay init
Step 2: Configure the World Template
First we move a Minecraft world into the src/WT/
directory.
TODO
Scripts
Each script is a program run either before building the add-on(s) (pre-built state) or after that (post-build state). Scripts can for instance be used to convert between file types such as JSON5 to JSON which then can be read by Minecraft.
Scripts are executed in order how they are specified in the configuration file.
[scripts]
base-path = "scripts/"
pre = [
{ run = "foo.rb", with = "ruby" }, # executed first
{ run = "bar.py", with = "python" }, # executed second
]
Always keep this in mind when adding new scripts.
Scripts are by convention located in the scripts/
directory but you can choose
another directory if you want to. The base path should be specified in the
base-path
field.
Scripts can also be nested within that directory. Considering the following project structure:
project
├── allay.toml
├── build/
├── pack_icon.png
├── scripts/
│ ├── converters/
│ │ ├── yaml_to_json.rb
│ │ └── toml_to_json.rb
│ └── linters/
│ └── yaml.py
└── src/
Then you can refer to the scripts like this in your configuration file.
[scripts]
base-path = "scripts"
pre = [
{ run = "linters/yaml.py", with = "python" },
{ run = "converters/yaml_to_json.rb", with = "ruby" },
{ run = "converters/toml_to_json.rb", with = "ruby" },
]
post = []
There are some scripts provided in the allay-mc/scripts repository. They are entirely written in Ruby without any dependencies. Just make sure you have Ruby installed when using scripts from there.
You can use the allay add
command to quickly download scripts from that
repository. For more info, type allay add --help
in the command-line.
Post scripts are less common because they work with the finalized built add-on. Ususally post-scripts only move the add-on to another location in the file system or upload the add-on.
Additional Options
Some scripts might accept additional options which can be set with the args
field.
[scripts]
base-path = "scripts/"
pre = [
{
run = "templating.rb",
with = "ruby",
args = [
"--trim-mode=%" # enables Ruby code processing for lines beginning with `%`
],
}
]
All arguments must be of type string even if the script expects a different type. In reality the string is converted by the script just like it would be provided in the command-line. Note that arguments are not pre-processed like shells do meaning you cannot use glob patterns or environment variables.
[scripts]
base-path = "scripts/"
pre = [
{
run = "templating.rb",
with = "ruby",
args = [
"--trim-mode=$ERB_TRIM_MODE" # `$ERB_TRIM_MODE` will be passed to the program as is
]
}
]
A way to achive that is by modifying the source code or wrapping the program with another program which is discussed in the next chapter.
Conditional Scripts
There are often cases where one want to execute simple commands such as
tsc
to
transpile TypeScript along with dynamic arguments such as environment variables.
[scripts]
base-path = "scripts/"
pre = [
{
run = "ts_to_js.sh",
with = "sh",
}
]
#!/usr/bin/env bash
shopt -s globstar # allow recursive glob patterns
tsc $ALLAY_PREBUILD/*.ts # transpile to JavaScript
rm **/*.ts # remove TypeScript files (from prebuild)
However this cannot be used from operating systems other than Linux such as Windows
as they do not have the sh
shell. In order to make it compatible with Windows,
the same script needs to be written in a supported language such as PowerShell:
tsc $Env:ALLAY_PREBUILD/*.ts # transpile to JavaScript
Get-ChildItem * -Include *.ts -Recurse | Remove-Item # remove TypeScript files (from prebuild)
The configuration would then look like this:
[scripts]
base-path = "scripts/"
pre = [
{
run = "ts_to_js.sh",
with = "bash",
when = "os = 'linux'",
},
{
run = "ts_to_js.ps1",
with = "powershell",
when = "os = 'windows'",
}
]
Options
arch
Possible values:
'x86'
'x86_64'
'mips'
'powerpc'
'powerpc64'
'arm'
'aarch64'
os
Possible values:
'windows'
'macos'
'ios'
'linux'
'android'
'freebsd'
'dragonfly'
'openbsd'
'netbsd'
family
Possible values:
'unix'
'windows'
'wasm'
allay_version
Possible values:
0.1.0-alpha.1
0.1.0
- …
Operators and Grouping
In addition, conditions can be grouped with parenthesis meaning they are evaluated first or chained and modified with the following operators:
- condition
&
condition — evaluatestrue
when both conditions are met - condition
|
condition — evaluatestrue
when at least one of the condition is met - condition
^
condition — evaluatestrue
when exactly one of the condition is met !
condition — evaluatestrue
when the condition is not met (example:!os = 'linux'
)
Conditions can be wrapped in parentheses to group them:
(condition1 & condition2) | condition3
This would require both condition1 and condition2 to evaluate true or just condition3.
Running Executables
[scripts]
# ...
pre = [
{
run = "name-of-executable",
args = ["arg1", "arg2"]
}
]
This will invoke the name-of-executable
executable. Note that the executable is not
required to be in the base-path
directory. If you want to supply dynamic values such
as environment variables use a shell script (with conditions or
some other scripting language as a wrapper instead.
Executables are usually installed through some package manager like pip or Cargo. They may be in text or binary format. Because executables in binary format are not cross platform, they are either used with a wrapper and conditions or are present somewhere outside the project’s directory. In that case it is good practise to describe how the executable can be installed.
Reference
The reference covers the details of various areas of Allay.
Configuration
An Allay project only exists if an allay.toml
file is present. It contains
several configuration options for the project and can be used to configure
scripts as well.
The [project.name]
section
The name of the project which will be used for the individual add-ons as well
unless overridden with pack.*.name
where *
is one of bp
, rp
, sp
or wt
.
[project.name]
de-de = "Hallo Welt"
en-us = "Hello World"
The [project.description]
section
The description of the project which will be used for the individual add-ons as
well unless overridden with pack.*.description
where *
is one of bp
, rp
,
sp
or wt
.
[project.description]
de-de = "Das ist mein tolles Projekt."
en-us = "This is my amazing project."
The [project]
section
The version
field
The version of the project of the form n.n.n
where n
is any natural number or
zero.
[project]
# ...
version = "0.1.0"
The development
field
Specifies whether the project is currently in development. This has impact on the
build process and maybe some scripts. This behavior can be overridden with
allay build --release
.
[project]
# ...
development = true
The min-engine-version
field
This is the minimum version of the game that this pack was written for. This is a required field for resource and behavior packs. This helps the game identify whether any backwards compatibility is needed for your pack. You should always use the highest version currently available when creating packs.
The authors
field
An array of people that are considered the “authors” of the project. An optional email address may be included within angled brackets at the end of each author entry.
[project]
# ...
authors = ["John Doe", "Joana Doe <joana.doe@example.org>"]
The license
field
The name of the license(s). You might want to include a LICENSE.txt
file in the
root of your project which contains the content of the license.
[project]
# ...
license = "MIT OR Apache-2.0"
The url
field
The URL to a site that is the home page for your project.
[project]
# ...
url = "https://github.com/allay-mc/example"
The [localization]
section
The primary-language
field
The primary language the add-on use. This is the language that untranslated languages will ultimatively fall back to.
[localization]
primary-language = "en-us"
The localization-groups
field
Using this field allows you to override existing language groups or create new ones. This is explained in detail in chapter 4
[localization]
localization-groups = [
["ru-ru", "uk-ua"],
# puts russian and ukrainian in the same group meaning if a translation for
# russian exists, ukrainian will fall back to that and vice versa
["de-de", "de-at"]
# puts german german and austrian german, which is not supported natively but
# could be implemented with a resource pack, in one group
]
The [scripts]
section
The base-path
field
This is the path relative to the project’s root directory (the directory that
contains the allay.toml
file) where scripts should be searched for. This is
the scripts
directory by convention but you are free to choose whatever
directory you want.
The pre
field
An array of scripts run before building the add-on. This is explained in detail in chapter 3
The post
field
An array of scripts run afte the add-ons have been built. This is explained in detail in chapter 3
The [build]
section
The include
and exclude
field
These fields allow you to specify glob patterns which affect the presence of specific files in the built add-on.
include = [
"**/*.json",
"**/*.md",
"**/*.js",
"**/*.png",
"**/*.mcfunction",
"**/*.mcstructure",
"**/*.lang",
]
The configuration above makes Allay remove all files except the ones that have one of the above mentioned file extensions. Using this field is generally discouraged because an add-on uses several different files with different names and extensions. Minecraft will not complain if the add-on contains unrecognized files. It should be the job of scripts which convert files that are not used in the final build to remove those files.
Using the exclude
file on the other hand is acceptable if a script for example
does not remove unused files or if you just want to include notices in the source
but remove them in the build.
exclude = ["**/LICENSE.txt", "**/README.md"]
The above example keeps all files except those named LICENSE.txt
and README.md
.
The manifest.json
file is the only file that is kept no matter what patterns are
specified in include
and exclude
.
The use-custom-manifest
field
Because the allay.toml
removes the need of a manifest.json
file, Allay will
display a warning if it found such file and ignores it. If you, for some reason
need to use a custom manifest.json
, then set this option to true
.
Commands
You can use commands anywhere inside an Allay project meaning you can allay build
it from within the src/WT/
directory for example.
Introduction
In this part of the book we will cover topics that are useful for “developers”. With developer, we mean people who extend the Allay code base or develop software that extends Allay. One example which will be our first topic is writing scripts. Earlier, we have discovered how to use scripts. Now the development of scripts is discussed. This requires another scope of knowledge because you have to know how to program. You can use your scripts for yourself or share them with others.
Scripts
While the official scripts are entirely written in Ruby doesn’t mean you cannot use some other programming language. In fact, you can use any programming language.
It’s generally preferred to use scripting languages such as Lua, Python or Ruby. They all don’t require a compilation step and can be run directly independet from the operating system and architecture.
Consider checking out the official scripts to see how scripts are structured and how they function.
Examples
TOML to JSON
#!/usr/bin/env python3
import sys
assert sys.version_info >= (3, 11), "tomllib is only available in Python 3.11 and above"
import json
import os
from pathlib import Path
import tomllib
BP = Path(os.environ["ALLAY_BP_PATH"])
RP = Path(os.environ["ALLAY_RP_PATH"])
SP = Path(os.environ["ALLAY_SP_PATH"])
WT = Path(os.environ["ALLAY_WT_PATH"])
paths = [BP, RP, SP, WT]
for path in paths:
for p in path.rglob("*.toml"):
if p.is_file():
# read TOML file
with p.open("rb") as f:
data = tomllib.load(f)
# remove TOML file
p.unlink()
# write JSON file
with p.with_suffix("json") as f:
json.dump(f, data)
auto-import add-on
This example requires the environment variable MINECRAFT_FOLDER
to set to the
location of the Minecraft data:
- Windows:
%localappdata%\Packages\Microsoft.MinecraftUWP_8wekyb3d8bbwe\LocalState\games\com.mojang
- Android:
storage/emulated/0/Android/data/com.mojang.minecraftpe/files/games/com.mojang
- iOS & iPadOS:
On My iPhone/Minecraft/games/com.mojang
#!/usr/bin/env python3
import os
from pathlib import Path
import shutil
TODO
Structure
Environment Variables
When Allay runs a script, it will also provide several environment variables that can be used within the script. Note that these are only set when Allay executes scripts meaning they are not accessible normally.
Example
# FILE: myscript.py import os os.environ["ALLAY_BP_PATH"]
ALLAY_BP_PATH
— where the prebuilt behavior pack is locatedALLAY_BUILD
— where the built add-ons are locatedALLAY_CONFIG
— where the configuration file is locatedALLAY_PREBUILD
— where the prebuilt add-ons are located1ALLAY_RP_PATH
— where the prebuilt resource pack is locatedALLAY_RELEASE
— whether the add-ons are beeing built in release modeALLAY_SP_PATH
— where the prebuilt skin pack is locatedALLAY_VERSION
— the version of Allay that is beeing usedALLAY_WT_PATH
— where the prebuilt world template is locatedALLAY_ROOT
— where the project is located
Always use paths provided by Allay’s environment variables instead of specifying them on your own unless the programming languages doesn’t support accessing environment variables.
Never modify the original source. Allay copies the directories in src
into
a seperate directory (.allay/prebuild/
) which is allowed to be modified. The
environment variables refer to that location.
The reason for this is, that some scripts may be nested inside a directory containig all scripts. Now if a script tries to access some path relatively it might break for some users due to their different structure.
# BAD:
from pathlib import Path
path = Path("../.allay/prebuild/BP/")
...
# WORSE:
from pathlib import Path
path = Path("../src/BP/")
...
# GOOD:
import os
from pathlib import Path
path = Path(os.environ["ALLAY_BP_PATH"])
...
Usually the ALLAY_*_PATH
variables should be used instead
of this.
Configuration
Some scripts might rely on options that can be configured by the user. Instead of manually editing constants in the script, one can use the configuartion file.
Using command-line options
Most programming languages a capable of reading arguments provided in the command-line. Rust uses these method to allow users to provide options specific to a script in the configuration file.
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("name")
parser.add_argument("age", type=int)
args = parser.parse_args()
print(f"You are {args.name} and you are {args.age} years old.")
One can then provide arguments in their configuration file like this:
[scripts]
base-path = "scripts"
pre = [
{ run = "helloworld.py", with = "python", args = ["John Doe", "42"] }
]
Remember to document the arguments somewhere.
Parsing Libraries
Using [metadata]
Another less recommended approach is the usage of the
[metadata]
section in the configuration
file. It is designed to be used by external programs but parsing TOML is less
supported than parsing command-line arguments in many programming languages.
import os
from pathlib import Path
import tomllib
config_file = Path(os.environ["ALLAY_CONFIG"])
with config_file.open("rb") as f:
data = tomllib.load(f)["metadata"]["helloworld"]
print(f"You are {data['name']} and you are {data['age']} years old.")
One can then provide arguments in their configuration file like this:
[metadata.helloworld]
name = "John Doe"
age = 42
Acknowledgement
The base idea originated from the wonderful Regolith project by Bedrock OSS. Features like scripts (filters) and the project structure are inspired by Regolith. The Bedrock Wiki helped a lot as well for references like localization and the manifest file format.
mdBook is used for the documentation website. Its pretty design and customization support allows us to document Allay in the best possible way.