# Basics

# Widget

A widget is the base element for basically everything in objD.
property
generate(Context) returns the underlying Widget Tree
toMap() Similar to toString, but returns a Map tree of the child in generate

Example: We can use an Widget to get or build functionality. e.g

// building our own widget:
class CustomCommand extends Widget {
	
	Widget generate(Context context){
		// using an existing widget
		return Command('custom')
	}
}

# Context

Maybe you already wondered what this context argument here is:

Widget generate(Context context){

The Context is a way to get certain important information from the parents.

properties
packId String of the current pack's name
file the current file name
prod see if project is in production mode(bool)
loadFile the filename of your load file
mainFile the filename of your main file
path the current Path induced if Folder Widget is used
prefixes a List of Strings that should be added in front of actions(mainly used by Groups)

You can use this context to build more modular Widgets and don't need to hardcode certain files and the pack id:

class LoadWidget extends Widget {
	
	Widget generate(Context context){
		return Command('function ' + context.packId + ":" + context.loadFile)
	}
}

# Macros / Function Arguments

Minecraft 1.20.2 added the feature to call functions with arguments that are replaced at runtime. These macro commands have the following form:

$say This is a macro line, using $(key_1)!

As objD is typesafe, this dynamic replacement proofs to be challenging to implement for all types. Here is a list of the supported types and the corresponding functions on Context.

Type function
String stringArgument
int intArgument
double doubleArgument
Entity entityArgument
Score scoreArgument
Location locationArgument
T buildArgument (custom builder)

A generic argument function is also available that figures out which of these methods to use based on the expected return type.

In order to use these arguments, call these functions in your generate method:

Widget generate(Context context) {
    final key_1 = context.stringArgument('key_1');
	return Say('This is a macro line, using $key_1')
}

To write the macro line $data merge entity ${eArg} {uuid:[I;1,2,3,${iArg}]}, you can use for instance:

Widget generate(Context context) {
    final iArg = c.intArgument('iArg');
    final Entity eArg = c.argument('eArg');
    return Data.merge(eArg, nbt: {
          "uuid": UUID(1, 2, 3, iArg),
    }),
}

Important to know: objD generates random numbers for every argument except String. After the command has generated with the variable, the number now in string format is replaced with the macro name. If you modify the variable in any way that alters the toString return value, this won't work. If you need a custom solution, use the buildArgument method.

# Custom Arguments

In cases where the types do not suffice or the string replacement does not work properly, you can create a custom argument with buildArgument. The context generates a non conflicting double for you, and you can provide an instance of the type and optionally override the string value that is going to be replaced:

final key_2 = context.buildArgument('key_2', (value) {
	final time = Time.duration(seconds: value);
	return (time, time.toString(reduce: false))
});

# Project

The project is a special Widget which is just defined once. It contains some built options, like description or name, but also the entire underlying tree of packs, files and actions.

constructor
name the name of the datapack folder
generate a widget that defines the projects content
[description] A description that will go in pack.mcmeta
[version] The minimal supported Minecraft version as int (mc 1.16 -> 16)
[packFormat] Override the pack format in the mcmeta
[supportedFormats] A list of supported pack formats for the mcmeta

Example:

main() {
// create Project takes in one project and compiles it
createProject(
	Project(
		name:  "tested",
		version: 16,
		generate:  MainWidget(),
	)
);
}

# Pack

A pack is logically the next step. This defines a sub-pack with an name again that will be our namespace afterwards. Here we can also define included files as well as the main and load function:

constructor
name the name of the sub-pack
[main] the main file that is ran every tick
[load] the load file that is ran on reload
[files] A List of type File witch includes other needed files
[modules] used to inject a List of Modules(read more about modules in the Modules section)

Example:

Pack(
	name:"tpcraft",
	main: File(...),
	load: File(...),
	modules: [
		MyModule()
	]
	files: List<File> [
		File(...)
	]
)

The Pack class can be used as often as you want and where you want, so you can also define a new pack in some file.

Notice: The namespace of the pack is accessible in these files by using the context variable. e.g: Command("function" + context.packId + ":run")

# File

The Pack class already required some files. The file class simply generates a new mcfunction file with content and a path.

The File constructor has two required arguments:

constructor
String the desired file path going from /data/:packId:/functions/ on
child the content of the file
execute bool if the function should be executed directly(optional)
create bool if the file should be created or just interpreted with execute(optional, default = true)
inheritFolder bool if the file should use the inherited path from using Folder Widgets or use the root(default = true)
pack overrides the automatically detected namespace(optional)
arguments a Map, Storage, Entity or Location to get properties to replace 1.20 macros with

The File class can be used as often as you want and where you want, so you can also define a new file in a For container for example. Example:

Pack(
	name:"tpcraft",
	main: File(
		"main",
		// and defining a new file inside of an widget
		child: File.execute( // same as execute: true
			path: 'new'
			child: Command(...)
		)
	),
)

Tip: Execute .run() on an existing File object and get an execute representation.

# Folder

The Folder Widget gives you a way to place all following files inside a specific folder path.

constructor
String the folder path
child another Widget that inherits the given path

Example:

Folder(
	'custom',
	child: For.of([
		File('main')
		File('subfolder/test')
	]),
)

Would create 2 functions: functions/custom/main.mcfunction and functions/custom/subfolder/test.mcfunction

Tip: in case you need the path at any time, access it using context.path

# IndexedFile

The IndexedFile behaves similar to File. Additionally it makes sure that each File, created with IndexedFile, is unique and does not get overwritten. In order to do that IndexedFile saves for each inputted name an id, which gets incremented after each use. This helps with large scale third-party file generation for example with Group, Execute or If.

constructor
String the name of the desired file
child the content of the file
execute bool if the function should be executed directly(optional)
custom a custom name that overrides the id(useful for customization in Execute)
path an optional folder to add the new function(like objd for Example)
pack overrides the automatically detected namespace
inheritFolder bool if the file should use the inherited path from using Folder Widgets or use the root(default = false)

Example:

IndexedFile("index",child:...)
IndexedFile("index",child:...)
IndexedFile(
	"index",
	child:...,
	execute: true,
	path: "objd",
	pack: "custom"
)

Creates 3 files: index1, index2, objd/index3 and adds function custom:objd/index3 into the current file.

# RawFile

The RawFile Widget enables you to generate your own Files right in the Wiget tree. Here you can define your own file extension, the file path and the content.

constructor
String the name of the file(with fileextension)
String the content of the file
path changes the default path(in your pack) of the new file
inheritFolder bool if the file should use the inherited path from using Folder Widgets or use the root(default = false)

Example:

RawFile(
	"predicate.json",
	'{...}',
	path: "/predicates",
)

# JsonFile

As a way to easily generate .json files, you can use the JsonFile wrapper. Just provide a map or a list and a path

constructor
String path of the file(without .json extension)
Map the content of the file
useGson whether to generate gson or plain json
inheritFolder bool if the file should use the inherited path from using Folder Widgets or use the root(default = false)

For a List just use JsonFile.list.

Example:

JsonFile(
	"predicates/map",
	{},
)

# Extend

Extend is very similar to File, but instead of creating a new file it adds content to an existing file.

constructor
String the desired file path going from /data/:packId:/functions/ on
child the additional content
first Boolean if the content should be added in front of the original(default = false)
inheritFolder bool if the file should use the inherited path from using Folder Widgets or use the root(default = true)

So lets say we already have some files in our pack, but want to add something to the main file somewhere entirely else in our project:

Extend(
	"main",
	child: Command("say okay"),
	first: true
)

This would add the command say okay in front of our main.mcfunction.

# Command

A Command is a raw action, that is executed by Minecraft directly.

constructor
String the raw command in the format "/command" or "command"

Example:

// using a command in a file:
File(
	path:"command",
	child: Command('/say hey')
)
// uses the say command in command.mcfunction:
say hey

# For

The For class enables you to add multiple endpoints to one Widget.

There is always a List of Widgets involved.

constructor
to the ending index including itself
create a function returning a Widget based on the index argument
[from] the starting index(default 0)
[step ] the value used in each iteration to increases the index(default 1)

So the basic for class utilizes a loop from a value to another value:

File(
	path:"for",
	child: For(
		from: 0,
		to: 5,
		create: (index){
			return Command('/say ' + index.toString())
		}
	)
)
// results in:
say 0
say 1
say 2
say 3
say 4
say 5

There is also an other Constructor for looping through a given list of widgets:

For.of
List of Widgets loops through each of these Widgets

Example:

File(
	path:"for_of",
	child: For.of([
		Command('say 1'),
		Command('say 2'),
		Command('say 3')
	]),
)
// results in:
say 1
say 2
say 3

# CommandList

But there is a more efficient way to list raw Minecraft commands. The CommandList Widget allows you to input a List of Commands, a List of Strings representing a command or a multiline string.

constructor
List a list of commands or strings

This has basically the same function as For.of just for commands.

CommandList([
		Command('say 1'),
		Command('say 2'),
		Command('say 3')
]),

But there is also a subset which accepts one big multiline string:

CommandList.str
String list of commands each on a seperate line
CommandList.str("""
	/say 1
	/say 2
	/say 3
"""),

The slashes are automatically filtered out.

# CommandBuilder

A widget which lets you construct complex commands with optional parts, multiple subcommands and different types of data in a declarative way. Mainly used inside of wrapper widgets for minecrafts commands.

Construct a builder with an initial command:

final builder = CommandBuilder('damage $target $amount')

Then you can call various methods appending an argument conditionally(if the input is null, nothing gets appended). All methods have in common the argument prefix for adding something infront of the passed type, the argument suffix, added after and a list also which is also appended seperated by spaces.

The following methods/types are supported: .string, .number, .entity and .location.

builder.string(damageType)
	.when(
		location != null,
		then: 'at $location',
		otherwise: by != null ? 'by $by' : null,
	)
	.string(cause?.toString(), prefix: 'from ');

.when is used when there are two possible ways the command could generate. When the condition is evaluated as true, then is used, else otherwise. Both could also be null, thus nothing being appended.

In case you want to build strings/commands yourself, take a look at the source code for Damage, Clone, Data, Effect, Ride, FillBiome ... (more will follow) to see CommandBuilder in use.

# Group

The group groups actions similar to for but has an option to prefix each action and encapsulate the content in a new file.
constructor
prefix a prefix type of String
children the underlying widgets
[suffix] a String that should be added at the end
[filename] the targeted filename(will be combined with an unique id)
[groupMin] the minimum amount of children to encapsulate(default = 3, set to -1 to disable)

If the children length is larger or equal groupMin a new file is created in /objD/(the working directory for objectiveD) and executed.

Group(
	prefix: "execute as @e run "
	children: [
		Command('say 1'),
		Command('say 2'),
		Command('say 3')
	],
	filename: "asgroup",
	groupMin: 2
),

# Gson

Minecrafts Data is stored in the nbt format. Each property in therefore encoded into key-value pairs(json format). Additionally Minecraft introduced gson, which adds types. The gson (opens new window) package handles the encoding and decoding of Minecrafts format in objD and provides dart wrappers for the specific types.

# Usage

To decode you can use

gson.decode("{...}");

and to encode you can use

gson.encode({...});

That means, when you need the gson string somewhere, you can input a Map or a List in to encode. For most of the usecases the integrated Widgets to this automatically(Entity,Data,...) and generate valid gson out of the box.

# Types

Additionally to the dart types String,int, double, Map and List gson adds some specific types:

Type Example Result
String "a" "a"
int 0 0
Map {"a":"a"} {a:"a"}
List ["a"] ["a"]
Byte Byte(20) 20b
Boolean true 1b
Float Float(90) 90f
Double Double(0.75) 0.75d
Short Short(10) 10s
Long Long(10000) 10000l

TIP

For more information take a look at the Package at pub.dev/packages/gson (opens new window) or visit the example (opens new window)

# Entity

constructor arguments are optional
selector the entity selector(e.g p,s,e or r)
limit number of matched entities
tags a List of Strings or Tags that the entity should have
scores a List of Score matches that the entity should match
team a Team the entity has to be part of
type EntityType, id of the entity
nbt a Map of required nbt properties
strNbt option to override the nbt map with a String to support expressions like 1b
area A Area where the entity should be
distance Range to the entity
level Range of experience levels
gamemode Gamemode type(e.g Gamemode.creative, Gamemode.survival)
horizontalRotation Range of the horizontal facing direction
verticalRotation Range of the vertical facing direction
isRotated a Rotation Object for testing a specific rotation
playerName a String if you prefer to use a playername instead of arguments
predicate a String that refers to a Predicate in the Datapack
Methods
sort adds a sort attribute of type Sort
storeResult Command, path, scale,datatype, useSuccess

storeResult stores a result or success of a command in the nbt path of an entity. Example:

Entity.Selected().storeResult(
	Command('say hello'),
	path: "Invisisble",
	scale: 1,
	datatype: "byte"
	useSuccess:true
)

⇒ execute store success entity  Invisisble byte 1 run say hello

# RestActions

RestActions behave like normal Methods/Widgets when used in the Widget tree. With it you can for Example use:

method function
kill kills the entity
raycast sends out a raycast(take a look at the raycast docs)
tp/teleport teleports the entity to a certain location or another entity(ref teleport docs)
give gives an item to a player(ref Give docs)
replaceitem replaces a certain slot with an Item(ref ReplaceItem docs)
particle shows particles at the entitys position(ref Particle docs)
crash crashes a players client
clear clears the players inventory(ref Clear docs)
tellraw prints a message in the players chat(ref Tellraw docs)
dataMerge merges nbt data into the entity
dataGet uses execute to get nbt data from the entity
dataRemove removes the nbt data of a specific path
dataModify modifies the nbt data of the entity(ref Data Widget!)
execute uses the execute on the entity(as)
exec short for execute
executeStrait uses the execute on the entity(as) with StraitWidget
execStrait short for executeStrait
asat uses execute with the entity and their location
asatStrait asat with Strait
as uses execute as the entity
asStrait as with Strait
at uses execute with the entities location
atStrait at with Strait
addTag adds a Tag(String) to the entity(ref Tag docs)
addTags adds a List of Tags(String) to the entity
removeTag removes a certain Tag(String)
removeTags removes a List of Tags(String)
joinTeam entity joins the given team
leaveTeam entity leaves the current team
forEach executes for each given Entity that fulfills the selector a Function with a List of Widgets(see example)

Example:

Entity.Self().kill()

⇒ kill 

However, you can as well use these Methods together with StraitWidget and add the .queue() method to let objd automatically add the generated Widget to the Widget list. This makes the code more readable.

Example:

StraitWidget(
	(List<Widget> widgets){
		Entity.Self().kill().queue()
		widget.add(...)
	}
)

⇒ kill ...

# Sort

Sort
Sort.random
Sort.furthest
Sort.nearest
Sort.albitrary

# Range

The Range class defines a range of values(e.g 3..10 in vanilla)

Range
[to] Number for the maximum range
[from] Number for the minimal range
Range(0,10)

Use Range.to to define less than and Range.from to define greater than.

Use Range.exact to get the exact Range(e.g 4)

Range.exact(4)

# EntityType

EntityType
String String representation of the type

There is also an EntityType for every type_id in minecraft with Entities.[type_id]

Say(
	Entity(
		selector: "e",
		limit: 1,
		tags:["first","second"],
		scores:[Score(score1).matches(10)],
		team: Team("my_team"),
		type: Entities.armor_stand,
		distance: Range.to(2),
		area: Area.fromLocations(
			// use null for a unlimited selection
			Location.glob(x: -10,y: null,z: -10),
			Location.glob(x: 10, y: null, z: 10)
		),
		level: Range.from(1),
		gamemode: Gamemode.creative,
		horizontalRotation: Range.from(1),
		verticalRotation: Range(20, 80),
	).sort(Sort.random)
)

⇒ say [limit=1,tag=first,tag=second,scores:{test=10},team=my_team,type=armor_stand,distance=..2,x=-10,z=-10,dx=20,dz=20,level=1..,gamemode=creative,y_rotation=1..,x_rotation=20..80,sort=random]
specific constructors
Entity.Selected(...) creates an entity with @s
Entity.Self(...) Entity.Selected, but shorter
Entity.Player(...) creates an entity with @p
Entity.PlayerName(String) creates an entity with an implicit name
Entity.All(...) creates an entity with @a
Entity.Random(...) creates an entity with @r
Entity.Select(Selector) takes in a Selector to select the Entity
Entity.Marker(...) selects entities of type Marker: @e[type=minecraft:marker]
Entity.clone(Entity) creates a new instance of an already existing Entity object

# Entity.not

With the not function you can negate specific arguments. It takes in the same options as Entity().

Example:

Say(Entity().not(tags:["mytag"],nbt:{"istrue":1}))
⇒ say [tag=!mytag,nbt=!{"istrue":1}]

# Entity.copyWith

Creates a new Entity based on the existing one and applies new arguments. (same as constructors)

Example:

Entity ent1 = Entity(type:Entities.sheep)
Entity ent2 = ent1.copyWith(distance:Range.to(1))

# Entity.setValues

Modifies the properties of the existing Entity and applies new arguments(same as constructors)

Example:

Entity ent1 = Entity(type:Entities.sheep)
ent1.setValues(distance:Range.to(1))[type=sheep,distance=..1]

# Tag

A tag saves a boolean value with an entity inside the game.

constructor
String the name of the tag
entity the entity you that want to assign a tag to
value the boolean value(default true)

As a clearer way, you can also use Tag.add(...) or Tag.remove(...) making your code more readable.

Example:

Tag("firstTag",entity:Entity.Player(),value: true)
⇒ tag  add firstTag

There is also the add or remove method for changing a variable:

Tag mytag = Tag("firstTag",entity:Entity.Player())
// in generate
mytag.add(),
mytag.remove()

⇒ tag  add firstTag
⇒ tag  remove firstTag

Also consider the addTag method on an entity.

# toggle

With the toggle method you can toggle the value(invert the tag). This is done with a temporary tag:

Tag("mytag",entity:Entity.Selected()).toggle()
⇒ execute if entity [tag=mytag] run tag  add objd_temp
⇒ execute if entity [tag=objd_temp] run tag  remove mytag
⇒ execute if entity [tag=!objd_temp] run tag  add mytag
⇒ tag  remove objd_temp

# removeIfExists

The removeIfExists method removes the tag and may execute some action before if the tag exists.

Tag("mytag",entity:Entity.Selected()).removeIfExists(
	then: Say("removed")
) // optional argument
⇒ execute if entity [tag=mytag] run say removed
⇒ execute if entity [tag=mytag] run tag  remove mytag

# Operators

You can use Operators to use Conditions and Assignments of new values faster.

** >> **

Assignes a new boolean value to the Tag(removes or adds the tag).

	Tag("test") >> true/tag  add test

** & **

Checks if the Tag is a certain value and returns a Condition to use in If.

	If(
		Tag("test") & true,
		then: [
			...
		]
	)/execute if entity [tag=test] run ...

# Prefixes

Often you find yourself giving all tags a prefix espacially for your project. This can get very repetitive and anoying, so objD has this prefix built in. Just assign the wanted prefix to the Tag.prefix constant and most tags(initiated with Tag, Entity and Summon) will be converted:

Tag.prefix = "stevertus_"

# Scoreboard

A scoreboard objective holds values, kind a like a Variable inside Minecraft. The Scoreboard class just handles adding or removing objectives. The value assignment is handled by the Score class.

constructor
String name of the objective(required)
type the objective type (default = dummy)
display TextComponent that displays the name
addIntoLoad bool whether the scoreboard should be added into your load file(default = true)

objD automatically keeps a list of all scoreboards and inserts them into the given load file, ignoring doubled names. Example:

Scoreboard(
	"death_count",
	type: "deathCount",
	display: TextComponent("This is how many deaths you have:"),
	addIntoLoad: true
)
Scoreboard("death_count")

// load.mcfunction:
/scoreboard objectives add death_count deathCount [{"text":"This is how many deaths you have:"}]

So the second scoreboard was not added because one "death_count" already existed.

The Scoreboard.add constructor does exactly the same but puts the result without checking in the current file.

Scoreboard.remove removes an objective by its name again.

With Scoreboard.setdisplay you can display the values:

Scoreboard.setdisplay
String name of the objective(required)
display String for display location (default = sidebar)

Scoreboard.modify can change the rendertype to use hearts after its creation

Scoreboard.modify
String name of the objective(required)
useHearts bool (default = false)

# Prefixes

Often you find yourself giving all scoreboards a prefix espacially for your project. This can get very repetitive and anoying, so objD has this prefix built in. Just assign the wanted prefix to the Scoreboard.prefix constant and all your scores will be converted:

Scoreboard.prefix = "stevertus_"

# Accessing the Scores

A Scoreboard implements the operator []. With this operator you can retrieve a score quickly from its scoreboard by passing either an Entity or a String representing a player name(or fake name):

var board = Scoreboard("test");
...
var score1 = board[Entity.Self()];
var score2 = board["#some_constant"];

For quick access to the most common ones you can also use board.self, board.all or board.player:

var score3 = board.player;

# Score

The score class is the basis for setting values, calculating with scores and checking the values. It implements one base class with no functionality and several methods to do actions:

constructor
Entity the entity within the scoreboard
String the name of the objective
addNew bool whether it should add the scoreboard itself if it does not exist(default = true)
type type of generated Scoreboard(default = dummy, only if addNew)

With the addNew property it is not required to add a scoreboard before!

# Calculations

These methods can be used to set or calculate the value:

name arguments
set int
reset
add int
subtract int
The following compare another Score
setEqual Score
swapWith Score
setToSmallest Score
setToBiggest Score
addScore Score
subtractScore Score
multiplyByScore Score
divideByScore Score
modulo Score
setToData Data
setToResult Command,useSuccess(bool)
setToWidget Widget,useSuccess(bool) (⇒ CAUTION make sure your Widget just generates one Command)
setToCondition Condition,useSuccess(bool)
findSmallest List<Score>,min (⇒ finds the smallest value in a list of scores)
findBiggest List<Score>,max (⇒ finds the biggest value in a list of scores)

All of these methods return a new instance of Score with the calculations applied. So you can also chain single calculations or use multiple on one base Score.

Examples:

// defining scores variables inside the widget
Score base = Score(Entity.Selected(),"score",addNew: true)
Score another = Score(Entity.Selected(),"score2")
// ... in the generate method:
base.set(5).add(3).subtract(10).reset()
⇒ scoreboard players set  score 5
⇒ scoreboard players add  score 3
⇒ scoreboard players remove  score 10
⇒ scoreboard players reset  score

base.setEqual(another).swapWith(another).setToBiggest(another)
⇒ scoreboard players operation  score =  score2
⇒ scoreboard players operation  score ><  score2
⇒ scoreboard players operation  score >  score2

another.addScore(base).divideByScore(base).modulo(base)
⇒ scoreboard players operation  score2 +=  score
⇒ scoreboard players operation  score2 /=  score
⇒ scoreboard players operation  score2 %=  score



base.setToData(Data.get(Location("~ ~ ~"),"Items[0].Count"))
⇒ execute store result score  score run data get block ~ ~ ~ Items[0].Count 1

// using success instead of result
base.setToResult(Command("say hi"),useSuccess:true)
⇒ execute store success score  score run say hi

# Conditions

These methods can be used for example with if to match values:

name arguments example Result
matches int @s score matches 5
matchesRange Range @s score matches 0..20
isEqual Score @s score = @s score2
isSmaller Score @s score < @s score2
isSmallerOrEqual Score @s score <= @s score2
isBigger Score @s score > @s score2
isBiggerOrEqual Score @s score >= @s score2

# Operators

Operators are a way to make the common used methods easier and more accessible. You can use the operators(+,-,/,*...) like you would with numbers or strings. The Score Widget detects the type automatically and returns the matching methods.

Operator available Types equivilant to
>> int, Score, Data.get, Condition score.set(int)
+ int, Score score.add(int)
- int, Score score.substract(int)
% int, Score score.modulo(score2)
/ int, Score score.divideByScore(score2)
* int, Score score.multiplyByScore(score2)
Conditions used in If
& int,Score,Range score.matches(int)
> int, Score score.isBigger(score2)
< int, Score score.isSmaller(score2)
>= int, Score score.isBiggerOrEqual(score2)
<= int, Score score.isSmallerOrEqual(score2)

# Constant Score

Do you need constant values with scores? objD got you covered with Score.con:

Score.con
int a constant number
addNew bool whether it should add objd_consts itself if it does not exist(default = true)

This will automatically create a scoreboard called objd_consts and set the value to the fake entity #[value] Example:

Score.con(5)
⇒ scoreboard players set #5 objd_consts 5

# Selected Score

Often times you want the score of a selected Entity(@s). Score.fromSelected is the same as Score but has a predefined entity. Example:

Score.fromSelected("objective").set(3)
⇒ scoreboard players set  objective 3

The score class is the basis for setting values, calculating with scores and checking the values. It implements one base class with no functionality and several methods to do actions:

constructor
Entity the entity within the scoreboard
String the name of the objective
addNew bool whether it should add the scoreboard itself if it does not exist(default = true)

With the addNew property it is not required to add a scoreboard before!

# Calculations

These methods can be used to set or calculate the value:

name arguments
set int
reset
add int
subtract int
The following compare another Score
setEqual Score
swapWith Score
setToSmallest Score
setToBiggest Score
addScore Score
subtractScore Score
multiplyByScore Score
divideByScore Score
modulo Score
setToData Data
setToResult Command,useSuccess(bool)
findSmallest List<Score>,min (⇒ finds the smallest value in a list of scores)
findBiggest List<Score>,max (⇒ finds the biggest value in a list of scores)

All of these methods return a new instance of Score with the calculations applied. So you can also chain single calculations or use multiple on one base Score.

Examples:

// defining scores variables inside the widget
Score base = Score(Entity.Selected(),"score",addNew: true)
Score another = Score(Entity.Selected(),"score2")
// ... in the generate method:
base.set(5).add(3).subtract(10).reset()
⇒ scoreboard players set  score 5
⇒ scoreboard players add  score 3
⇒ scoreboard players remove  score 10
⇒ scoreboard players reset  score

base.setEqual(another).swapWith(another).setToBiggest(another)
⇒ scoreboard players operation  score =  score2
⇒ scoreboard players operation  score ><  score2
⇒ scoreboard players operation  score >  score2

another.addScore(base).divideByScore(base).modulo(base)
⇒ scoreboard players operation  score2 +=  score
⇒ scoreboard players operation  score2 /=  score
⇒ scoreboard players operation  score2 %=  score



base.setToData(Data.get(Location("~ ~ ~"),"Items[0].Count"))
⇒ execute store result score  score run data get block ~ ~ ~ Items[0].Count 1

// using success instead of result
base.setToResult(Command("say hi"),useSuccess:true)
⇒ execute store success score  score run say hi

# Conditions

These methods can be used for example with if to match values:

name arguments example Result
matches int @s score matches 5
matchesRange Range @s score matches 0..20
isEqual Score @s score = @s score2
isSmaller Score @s score < @s score2
isSmallerOrEqual Score @s score <= @s score2
isBigger Score @s score > @s score2
isBiggerOrEqual Score @s score >= @s score2

# Constant Score

Do you need constant values with scores? objD got you covered with Score.con:

Score.con
int a constant number
addNew bool whether it should add objd_consts itself if it does not exist(default = true)

This will automatically create a scoreboard called objd_consts and set the value to the fake entity #[value] Example:

Score.con(5)
⇒ scoreboard players set #5 objd_consts 5

# Selected Score

Often times you want the score of a selected Entity(@s). Score.fromSelected is the same as Score but has a predefined entity. Example:

Score.fromSelected("objective").set(3)
⇒ scoreboard players set  objective 3

# Block

There is also a util class called Block which provides a wrapper for all available blocks in Minecraft. Usage:

Block([minecraft_block_id]) // as string or
Blocks.[minecraft_block_id]

All ids can be found here (opens new window). But you can also insert a block by its string:

constructor
String the minecraft block id

Example:

SetBlock(
	Blocks.stone,
	location: Location.here()
)

You can also use Blocks.all to get a List of all the block from the latest minecraft version and Blocks.snapshot to get the new blocks in the next version.

# Block.nbt

This is a special function that creates a block with the option to add nbt data and blockstates.

Block.nbt
Block or String the Block type
states a Map of Blockstates(e.g {"left":true}, optional)
nbt Nbt in form of a Dart Map(optional)
strNbt Nbt in a pure String format, allows for 1b etc(optional)

With this function you can fully configure your blocks.

# Biome

A wrapper for all biomes in minecraft. Usage:

Block([biome_id]) // as string or
Biomes.[biome_id]

You can also insert a custom biome by providing a string:

constructor
String the minecraft block id

Example:

If(
	Biomes.desert,
	then: [Say('In desert!')]
)

# Location

In the block example we already used a class called Location. This translates into Minecraft Coordinates.

constructor
String the minecraft coordinate string(e.g "~ ~ ~")
SetBlock(Blocks.stone,location: Location("~ 5 ~"))

There is also a shortcut for " ~ ~ ~ ":

Location.here Selects the current Position
Location.here()~ ~ ~

But the Location class also provides a wrapper for global coordinates:

Location.glob
x a double defining the absolute x coordinate
y a double defining the absolute y coordinate
z a double defining the absolute z coordinate
Location.glob(x: 5,y: 51.5,z: 784.20)5 51.5 784.2

And also for relative coordinates:

Location.rel
x a double defining the relative x coordinate
y a double defining the relative y coordinate
z a double defining the relative z coordinate
Location.rel(x: 5,y: 1.5,z: 0)~5 ~1.5 ~

And local coordinates(depends on the rotation of the head):

Location.local
x a double defining the local x coordinate
y a double defining the local y coordinate
z a double defining the local z coordinate
Location.local(x: 0,y: 1,z: 2.5)^ ^1 ^2.5

To clone a Location use Location.clone:

Location.clone
Location the source location
Location.clone(Location.rel(y:2))~ ~2 ~

There is also a method for a location:

methods
storeResult Command, path, scale, datatype, useSuccess

This stores a result or success of a command in the nbt path of a location. Example:

Location.here().storeResult(
	Command('say hello'),
	path: "Items[0].tag.command",
	useSuccess:true,
	scale: 1,
	datatype: "byte"
)

⇒ execute store success block ~ ~ ~ Items[0].tag.command 1 byte run say hello

# Area

The Area class provides a way to select a three dimensional space between some locations. Therefore it automatically builds the lowest and highest coordinates and calculates the distances.

constructor doubles
x1 one x corner
y1 one y corner
z1 one z corner
x2 second x corner
y2 second x corner
z2 second x corner

This is especially useful for if blocks, Fill and Clone.

Example:

Area(x1: 100, y1: -15.75, z1: 0, x2: 2, y1: 10, z2: -10)2 -15.75 -10 100 10 0

# Area.rel

Use Area.rel if you want to select the area relative to an unknown position.

# Area.fromLocations

But if you would also like local or relative coordinates, you can always pass the locations directly:

Area.fromLocations
Location location 1
Location location 2

# Area.fromRanges

There is as well a constructor to construct an Area within a defined Range of a Location(Works like fill or clone).

Area.fromRanges
x original location
y original location
z original location
dx distance to next location
dy distance to next location
dz distance to next location

Example: ˋˋˋdart Area.fromRanges( x: 10, y: 64, z: 10, dx: 10, dy: 10, dz: 10 ) ==> 10 64 10 20 74 20 ˋˋˋ

# Rotation

The Rotation class is very similar to Location but takes in just two directions for an entities rotation:

constructor
String the minecraft coordinate string(e.g "~ ~")
Rotation.glob
x int representing global x orientation
y int representing global y orientation
Rotation.rel
x int representing rotation relative to the current x orientation
y int representing rotation relative to the current y orientation

Example:

Rotation.rel(x: 90,y: 180)~90 ~180

Execute.rotated(Rotation.glob(x:0,y:90),children:[
	Command("tp @s ^ ^ ^10")
])
⇒ execute rotated 0 90 run command tp  ^ ^ ^10

# Predefined Values

The Rotation object has some common values. These mainly include all the directions(north, west, south, east):

Rotation.n ⇒ 180
Rotation.s ⇒ 0
Rotation.e ⇒ -90
Rotation.w ⇒ 90

You can also generate a Rotation object directly:

Rotation.north()180  0
Rotation.east(y: 10)-90 10
Rotation.south(dx: 45)45  0

Here you can also specify the y-value and an additional difference in x.

Tip: You can also use Rotation.checkNorth(), Rotation.checkEast()... to get a range testing specific rotations used in entities

# Get Direction

The getDirection method allows you to extract a direction from the x value of a Rotation. This can be used in Blockstates for example. All the direction are rounded to 90° steps.

Example:

var myrot = Rotation.global(x: 90, y: 56)
myrot.getDirection()"west"

# Data

The Data Widgets allows you to edit nbt data of Entities or Blocks.

constructor
dynamic The target Entity, Block or Storage which you want to modify
nbt A Dart Map containing new nbt data
strNbt option to override the nbt map with a String to support expressions like 1b
type A String defining the operation type(default=merge)

Example:

Data(
	Entity.Selected(),
	nbt: {
		"Invisible":1,
		"NoGravity":1
	}
)
⇒ data merge entity  {"Invisible":1,"NoGravity":1}

There are also subconstructors for each operation type(Data.merge, Data.get, Data.remove)

# DataModify

And the modify operation is also available, yet a bit more complex:

Data.modify
dynamic The target Entity OR Location which you want to modify
path the nbt path you want to modify
modify A DataModify object defining which parameters you want to modify

So this is split up into a seperate class:

There are five sub operations again: set, merge, prepend, append and insert. All follow this constructor rules:

DataModify
dynamic The source of the modification. Can be a Map, String, Number, Entity or Location
fromPath optional path for the Entity or Location source from where to read the data

So we can for Example use

Data.modify(
	Entity.Selected(),
	path: "my_Custom_Path",
	modify: DataModify.set(
		"hey" // or {"nbt":"here"} or 56
	),
)
⇒ data modify  my_Custom_Path set value "hey"

Or

Data.modify(
	Entity.Selected(),
	path: "my_Custom_Path2",
	modify: DataModify.insert(
		Entity.Selected(), // or Location... to get data from a block
		index: 2, // insert also needs an additional index
		fromPath: "my_Custom_Path"
	),
)
// this just copies one property to another
⇒ data modify  my_Custom_Path2 insert from entity  my_Custom_Path

For string subcommands 1.19.4 use the corresponding constructors setString, mergeString, prependString, appendString and insertString similar to above, but you can also add start and end indezies to cut of the string:

DataModify String
dynamic The source of the modification. Can be a Map, String, Number, Entity or Location
fromPath optional path for the Entity or Location source from where to read the data
start optional start index for substring
end optional end index for substring

# Data.copy

A handy shortcut for that is the Data.copy constructor, which just copies a property from one path to another:

Data.copy
dynamic The target Entity OR Location which you want to modify
path the nbt path you want to copy to
from The source Entity OR Block
fromPath The source nbt path
Data.copy(
	Entity.Selected(),
	path: "my_Custom_Path2",
	from: Location("~ ~-1 ~"),
	fromPath: "Items[0].tag.display.name"
)
⇒ data modify  my_Custom_Path2 set from block ~ ~-1 ~ Items[0].tag.display.name

# Data.fromScore

You can also convert a score directly to a nbt field with Data.fromScore:

Data.fromScore
dynamic The target Entity OR Location which you want to modify
path the nbt path you want to copy to
score The source Score
scale optional int (default = 1)
datatype a Java datatype for the score(default = byte)
Data.fromScore(
	Entity.Selected(),
	path: "my_Custom_Path",
	score: Score(Entity(),"myscore")
)
⇒ execute store result entity  my_Custom_Path 1 byte run scoreboard players get  myscore

# DataStorage

Since 1.15 you can store nbt data globally. To use this with objD include a DataStorage as a target.

Example:

Data.merge(
	DataStorage("example:store"),
	nbt: {"test":true}
)

Also take a look at the objD Storage API for easier accessibility.

# Item

The Item class represents an item in an inventory in Minecraft. It is used in the Give or Nbt Commands.

constructor
Item | Block | String the type of item(required, see example)
count Integer value for the amount of stacked items
slot The current Slot of the item(does not work for give)
damage the used durability of the item
hideFlags int from 1 to 63 describing which information to hide
model int describing which model varient should be used
name a TextComponent showing a name
lore a List of TextComponents giving extra information
nbt addional NBT as Dart Map

Example:

Give(Entity.Selected(),
	item: Item(
		Items.iron_axe, // OR Blocks.stone OR "whatever id"
		count: 5,
		name: TextComponent("My Item",color:Color.Black),
		lore: [
			TextComponent("My Description",color:Color.Blue),
		],
		damage: 40,
		model: 3390001,
		nbt: {
			"customNBT":1
		}
	)
)

⇒ give  minecraft:iron_axe{"customNBT":1,"Damage":40,"CustomModelData":3390001,"display":{"Name":"{\"text\":\"My Item\",\"color\":\"black\"}","Lore":["{\"text\":\"My Description\",\"color\":\"blue\"}"]}} 5

or very simple

Give(
	Entity.Selected(),
	item: Items.apple
)

⇒ give  minecraft:apple

Item.copyWith creates a copy of the current Item overriding specified properties.

Item.fromJson creates an Item based on nbt or json data.

Items is like Entities or Blocks a utility class to provide a list of all available items.

Item([minecraft_item_id]) creates a Item from a String
Items.[minecraft_item_id] there is also an value for each item in Minecraft

# HideFlags

To help you with the hideFlags value, there is the HideFlags method. It translates the human readable boolean values into an int.

HideFlags bools
enchant whether to show the enchantments
attributes whether to show the attributes
unbreakable whether to show the unbreakable tag
canDestroy whether to show the canDestroy tag
canPlaceOn whether to show the canPlaceOn tag
others whether to show other nbt information
dye whether to show other nbt information
armorTrims whether to show other nbt information

Example:

var flags = HideFlags(attributes:true, unbreakable: true, others: true); // = 38
...
Item(Items.apple,hideFlags: flags)

# Slot

The Slot object gives you certain utils to manipulate Inventories and Containers with the Item, Replaceitem or Data. Every Slot has a String(slot) like inventory.10 used in replaceitem and an id like 19 that is used with nbt data. objD should change between these values automatically for the specific usecase.

constructor
slot String for Replaceitem
id int for NBT

# Constants

More important are all the constants:

  • Slot.Hotbar[0-8]
  • Slot.Inventory[0-26]
  • Slot.Enderchest[0-26]
  • Slot.Container[0-53]
  • Slot.MainHand
  • Slot.OffHand
  • Slot.Head
  • Slot.Chest
  • Slot.Legs
  • Slot.Feet

Example:

ReplaceItem(
	Entity.All(),
	item:Items.golden_helmet,
	slot:Slot.Head
)
⇒ replaceitem entity  armor.head minecraft:golden_helmet

# Helpers

Together with this objD also introduces helpers to quickly find the desired slot.

Slot.inv takes in two numbers, like 2,6 The first number represents the row in the inventory, so the second row And the second number is the sixth slot of that row.

objD calculates the corresponding Slot. In this case inventory.14.

Notice: also the hotbar can be calculated with this. It is the 4th row

Slot.chest takes in numbers, like 5,6 and an optional boolean for using an enderchest And does exactly the same but with a container, like a chest.

Slot.drop takes in numbers, like 1,3 This calculates the rows and columns for a 3x3 Container like a Dropper or a Dispenser. Therefore just values from 1 to 3 are allowed.

Slot.craft used to mark a 3x3 space inside a conventional container. takes in two numbers, like Slot.inv or one number from 1-9. The start options marks the upper left.

Example:

ReplaceItem.block(
	Location.here(),
	item:Item(Items.beef),
	slot:Slot.chest(3,8)
)
⇒ replaceitem block ~  ~  ~  container.25 minecraft:beef

# Time

Object that represents time in minecraft. Usually translated into ticks (20ticks = 1 second).

You can directly construct the time from the integer number of ticks:

var t = Time(2400);

However this is not the easiest way to construct times. The simplest is to use number extensions explained below. For a handy interface which does the conversions automatically, use Time.duration:

Time.duration
ticks integer number of ticks(optional)
days number of ingame days(optional)
minutes number of minutes(optional)
seconds number of seconds(optional)

If you just want a integer number of seconds, minutes or days, you can also use constant constructors Time.seconds, Time.minutes, Time.days.

So we can write the same 1min 30s from above the following ways(also using fractional timesteps):

t = Time.seconds(90),
t = Time.duration(minutes: 1.5),
t = Time.duration(minutes: 1, seconds: 30),

In case of the Effect widget, it might also be useful to pass infinite time. This can be done with Time.infinite(). Otherwise when a finite time is expected, an error is thrown.

# Number Extensions & Operators

Yet an even more intuitive way is to use built in getters on the num type and operators.

On any number(int, double, ...) you can call .ticks, .seconds, .minutes and .days:

t = 2400.ticks,
t = 90.seconds,
t = 1.5.minutes,

Also all common math and comparison operators are available:

t = 1.minutes + 30.seconds 
t += 20.seconds
if(t > 10.seconds) {
	// do something conditional
} 

Using this style is highly recommended, as it leads to readable and understandable code.

# String Conversion

In commands when Time is used, string are generated dynamically.

When days can be expressed in 0.5 steps, the suffix d is generated.

When seconds can be expressed in 0.25 steps, the suffix s is generated.

So a word of warning, even when providing integer ticks, objD can decide to simplify the commands. For example Time(10) becomes 0.5s. This behaviour might change in the future, if there are serious concerns.

In case you want to have just the ticks, either use .ticks on a Time object or call toString with reduce set to false: Time(10).toString(reduce=false) => 10.

Last Updated: 1/6/2024, 11:31:42 PM