Ike
Ike
Explore posts from servers
AAdmincraft
Created by Ike on 4/19/2025 in #questions
How does my configuration language look?
Yeah, I definitely took from pkl a bit! Main difference is that this doesn't translate itself to different formats, that way I can do much nicer error checking, error reporting and parsing
14 replies
AAdmincraft
Created by Ike on 4/19/2025 in #questions
How does my configuration language look?
Most of this is for more advanced cases where you have more complex configs or series of configs
14 replies
AAdmincraft
Created by Ike on 4/19/2025 in #questions
How does my configuration language look?
Thank you! You are also not forced to use all of this, you can just stick to the basic config format and it will work just fine :D
14 replies
AAdmincraft
Created by Ike on 4/19/2025 in #questions
How does my configuration language look?
Also, things like multiline strings exist!
test: """
Hello, world!
"""
test: """
Hello, world!
"""
I may have missed some syntax features, if you want to know more just ask!
14 replies
AAdmincraft
Created by Ike on 4/19/2025 in #questions
How does my configuration language look?
Miscellaneous features Importing
import "./templates.hxc" as templates

{
test: templates:someFormat("Hi!"),
}
import "./templates.hxc" as templates

{
test: templates:someFormat("Hi!"),
}
Interpolation with config values
{
chat: {
prefix: "[CHAT]",
formats: {
everyone: "{../prefix} ${event.player.name} >> ${event.message}" // We use ../<name> to access a value or section from the parent, you can chain these: ../../../<name> to go up 3 times
}
}
}
{
chat: {
prefix: "[CHAT]",
formats: {
everyone: "{../prefix} ${event.player.name} >> ${event.message}" // We use ../<name> to access a value or section from the parent, you can chain these: ../../../<name> to go up 3 times
}
}
}
Interpolation with config values AND fallbacks
{
chat: {
prefix: "[CHAT]",
formats: {
everyone: "{../prefix} ${event?.player?.name ?? "Unknown"} >> ${event.message}" // If you want to add defaults, you need to use <name>?.<name> to access something which may not exist, and <name> ?? <default> to specify a default
}
}
}
{
chat: {
prefix: "[CHAT]",
formats: {
everyone: "{../prefix} ${event?.player?.name ?? "Unknown"} >> ${event.message}" // If you want to add defaults, you need to use <name>?.<name> to access something which may not exist, and <name> ?? <default> to specify a default
}
}
}
Arrays
import "./customItems.hxc" as customItems

{
rewards: [
item{"minecraft:diamond", 15},
customItems:specialDiamond(1),
],
}
import "./customItems.hxc" as customItems

{
rewards: [
item{"minecraft:diamond", 15},
customItems:specialDiamond(1),
],
}
Escaping interpolation
{
value: 15,
escaped: "You will get {value} items! \{o.o\}" // You will get 15 items! {o.o}
}
{
value: 15,
escaped: "You will get {value} items! \{o.o\}" // You will get 15 items! {o.o}
}
Overriding
import "./test.hxc" as test

{
/* Multiline comment
templateTest() -> { name: "Frank", age: 52 }
*/

value: test:templateTest { // You can omit the () if there are no arguments
name: "Jim" // We can override template vaues like this
},
}
import "./test.hxc" as test

{
/* Multiline comment
templateTest() -> { name: "Frank", age: 52 }
*/

value: test:templateTest { // You can omit the () if there are no arguments
name: "Jim" // We can override template vaues like this
},
}
Repeating
import "./test.hxc" as test

{
mapped: map(info in ${servers.info}) {
name: ${info.name},
ip: ${info.ip}
},

repeated: repeat(15) {
hello: "World!"
},

repeatedTemplate: repeat(15) { test:someTemplate(15) }
}
import "./test.hxc" as test

{
mapped: map(info in ${servers.info}) {
name: ${info.name},
ip: ${info.ip}
},

repeated: repeat(15) {
hello: "World!"
},

repeatedTemplate: repeat(15) { test:someTemplate(15) }
}
Schemas exist, they are defined as .hxcs (Helix Config Schema), and they look like this:
{
name: text,
value: number,
color: rgba
}
{
name: text,
value: number,
color: rgba
}
As far as I remember, this should be everything! I would really value some feedback from people who know more than me, so thank you! *PS: This will be fully open-sourced along with a loader-independent library, which means this won't be tied strictly to my loader!
14 replies
AAdmincraft
Created by Ike on 4/19/2025 in #questions
How does my configuration language look?
We got types, we got a nicer format, we got dynamic data, let's now solve: - Reusability - Error messages Templates
{
server(address ip, text name) -> {
ownerName: ${server.current.name},
ip, // Instead of doing ip: ip, we can just do ip,
name, // Same here
},

servers: {
alpha: server(address{"127.0.0.1"}, "alpha"),
beta: server(address{"10.0.0.2"}, "beta"),
}
}
{
server(address ip, text name) -> {
ownerName: ${server.current.name},
ip, // Instead of doing ip: ip, we can just do ip,
name, // Same here
},

servers: {
alpha: server(address{"127.0.0.1"}, "alpha"),
beta: server(address{"10.0.0.2"}, "beta"),
}
}
You can also scope them:
{
templates -> {
test(amount) -> { // Notice how we didn't specify what "amount" is, most of the times we don't need to unless we want to limit it
result: amount * 2,
}

others -> {
square(number amount) -> amount * amount // expressions work, too!
}
},

value: templates:test(15),
squared: templates:others/square(5) // 25
}
{
templates -> {
test(amount) -> { // Notice how we didn't specify what "amount" is, most of the times we don't need to unless we want to limit it
result: amount * 2,
}

others -> {
square(number amount) -> amount * amount // expressions work, too!
}
},

value: templates:test(15),
squared: templates:others/square(5) // 25
}
And we can make custom postfixes for different values:
time <- {
ticks(x) <- x * 50ms
},

counter 15 time:ticks
time <- {
ticks(x) <- x * 50ms
},

counter 15 time:ticks
Error messages
@ In File /plugins/test/config.hxc
5. | ...
6. | announcement: {
7. | message: "Remember to have fun!",
8. | every: 15 meters,
^ ^ Invalid value, did you mean "15 minutes"?
| Expected: Duration
| Got : Distance
| Docs: https://localhost/types/core/Duration
9. | to: "all-players",
10. | }
11. | ...
@ In File /plugins/test/config.hxc
5. | ...
6. | announcement: {
7. | message: "Remember to have fun!",
8. | every: 15 meters,
^ ^ Invalid value, did you mean "15 minutes"?
| Expected: Duration
| Got : Distance
| Docs: https://localhost/types/core/Duration
9. | to: "all-players",
10. | }
11. | ...
These error messages are certainly nicer (and yes, a sidenote, the loader itself provides offline docs), they give you context, and often times help you in fixing the issue. When they can't, they'll just resort to telling you why it's wrong. These messages are done automatically, and are always in the same format across plugins.
14 replies
AAdmincraft
Created by Ike on 4/19/2025 in #questions
How does my configuration language look?
My solution It is based off of JSON5, with some key changes we'll go over soon. For now, this is the most basic config:
{}
{}
Pretty simple, right? Let's add some data
{
messages: {
welcome: "Hello"
}
}
{
messages: {
welcome: "Hello"
}
}
so far so good, everything looks exactly like json for now, let's see how we handle external data
{
messages: {
welcome: "Hello!",
players: {
chat: "${event.player.name} >> ${event.message}"
}
}
}
{
messages: {
welcome: "Hello!",
players: {
chat: "${event.player.name} >> ${event.message}"
}
}
}
WOAH! What's going on here? We have two new features: - String interpolation, defined by {...}, allows us to put values inside of strings (and yes, this means other config values!) - External string interpolation, an enhancement of string interpolation which is written like ${...}, it does the same as string interpolation however using plugin data, in this case the plugin directly passed in the event data. Let's keep it moving, see what types we have.
{
text: "Hello!",

durations: 15 minutes, 12 seconds, 13 ticks,

fixedTimes: 00:00 AM,
a24HourClock: 15:17, // 3:17 PM

decimals: 3.1415,
withLeadingDot: .1415,
orTrailing: 3.,

positiveInfinity: +inf,
negativeInfinity: -inf,

longNumbers: 15_000_000,
negativeNumbers: -42,
andScientificNotation: 1e06, // other examples: 5e+10 123.56e-34,

binary: 0b01000001 // A
octal: 0o01234567,
hexadecimal: 0xDEADBEEF,

colors: #FF0000,
namedColors: #red,
shortColors: #F00,
transparentColors: #00FF007F // 50% transparent blue!
rgbColors: rgb{255, 255, 255},
namedRgb: rgb{
red: 255,
green: 255,
blue
},
withTransparency: rgba{255, 255, 255, 127},

position: pos{world{"spawn"}, 27, 123.5, 19.5},

running: true,

ip: address{"127.0.0.1"},
url: address{"https://www.youtube.com/watch?v=dQw4w9WgXcQ%22}
}
{
text: "Hello!",

durations: 15 minutes, 12 seconds, 13 ticks,

fixedTimes: 00:00 AM,
a24HourClock: 15:17, // 3:17 PM

decimals: 3.1415,
withLeadingDot: .1415,
orTrailing: 3.,

positiveInfinity: +inf,
negativeInfinity: -inf,

longNumbers: 15_000_000,
negativeNumbers: -42,
andScientificNotation: 1e06, // other examples: 5e+10 123.56e-34,

binary: 0b01000001 // A
octal: 0o01234567,
hexadecimal: 0xDEADBEEF,

colors: #FF0000,
namedColors: #red,
shortColors: #F00,
transparentColors: #00FF007F // 50% transparent blue!
rgbColors: rgb{255, 255, 255},
namedRgb: rgb{
red: 255,
green: 255,
blue
},
withTransparency: rgba{255, 255, 255, 127},

position: pos{world{"spawn"}, 27, 123.5, 19.5},

running: true,

ip: address{"127.0.0.1"},
url: address{"https://www.youtube.com/watch?v=dQw4w9WgXcQ%22}
}
That's a lot of types! And they all boil down to:
number // Any type of number
text // Any type of text
bool // Yes / no values

// Things like "rgb" are used as named
number // Any type of number
text // Any type of text
bool // Yes / no values

// Things like "rgb" are used as named
14 replies
AAdmincraft
Created by Ike on 4/19/2025 in #questions
How does my configuration language look?
Why not JSON? JSON, while harder to type, solves some of our issues: - It is not indentation based. - It is easier to figure out what is a child of what. - It is still somewhat easy to read, depending on who you ask. For example:
{
"messages": {
"welcome": "Hello!",
"players": {
"chat": "%player_name% >> %message%"
}
}
}
{
"messages": {
"welcome": "Hello!",
"players": {
"chat": "%player_name% >> %message%"
}
}
}
However: - Typing could be more convenient, especially because of the repeated "" - Its types are similar in number to that of YAML, which is a step down from TOML. - We still can't reuse configuration. - Error messages still need to be defined by the plugin. - And most importantly, there are no comments! How has this been addressed so far? - JSON5 has cleared up the configuration by removing the repeated "" in keys, unless they contain characters such as - - JSON5 has added comments to JSON, // making it much nicer to explain configuration - JSON5 has also added more types or otherwise more ways to define them, such as hexacedimal numbers: 0xCAFEBABE What can we do? - We can adopt JSON5's changes to make JSON nicer to read, take the above example:
{
messages: {
welcome: "Hello!",
players: {
// Possible formats:
// %player_name% -> the player's display name
// %message% -> the message contents
chat: "%player_name% >> %message%"
}
}
}
{
messages: {
welcome: "Hello!",
players: {
// Possible formats:
// %player_name% -> the player's display name
// %message% -> the message contents
chat: "%player_name% >> %message%"
}
}
}
- We can expand the type system to include things like colors, durations and dates - And finally we can standardize error messages and reusability, as we said previously.
14 replies
AAdmincraft
Created by Ike on 4/19/2025 in #questions
How does my configuration language look?
Why not toml? TOML is a very easy and intuitive language, I mean, just look at this:
[messages]
welcome = "Hello!"
[messages]
welcome = "Hello!"
It also has a very, very nice list of possible data-types which just aren't present in YAML, so for now, this is a very welcome improvement. However: - Nested configurations can be hard to read, even when using indentation:
[messages]
welcome = "Hello!"

[messages.players]
chat = "%player_name% >> %message%"
[messages]
welcome = "Hello!"

[messages.players]
chat = "%player_name% >> %message%"
- It can often times be unclear as to which section a key is parented to. - Configurations are still very much static, handling plugin input still requires custom handling. - And just like YAML, duplicated or long configs can be hard to read. What can we do? - Make it clearer as to which section is which, as well as which option is under which section. - Standardize a way to dynamically pass in data. - Standardize a way to reuse configuration.
14 replies
AAdmincraft
Created by Ike on 4/19/2025 in #questions
How does my configuration language look?
Why not yaml/toml/json? All of the above languages approach the problem in different ways, and while all correct, sometimes they fall short for what is commonly needed when setting up a minecraft server. Why not yaml? YAML is a great language if you need a really simple configuration:
messages:
welcome: Hello!
messages:
welcome: Hello!
However: - It is indentation based, it does not accept TAB keys and will cause errors when a plugin inevitably loads the file. - Its error messages are just for the syntax, it is then up to the plugin itself to report errors in a more or less effective way. - It can get confusing on larger files, and often times repetition or otherwise very large files can lead to headaches. How has this been addressed so far? - Some plugins have adopted the use of import: <id> to import/merge template files or other sections. While this works, it is inconsistent and up to the plugin author if they'd like to introduce it. - Some plugins have made their error reporting nicer to look at, filled with info or otherwise try to indicate what went wrong in the most user-friendly way. Though this is not always the case. - Some editors just replace TAB with spaces in YAML files. Except the Pterodactyl editor, I always have issues with that. What can we do? - Standardize a way to reuse configuration formats in a sensible and structured way. - Standardize and prioritize informational and readable error reporting at a language level. - Remove the need for indentation-based configuration.
14 replies