Skip to content

Commit 2451322

Browse files
authored
Merge pull request #12 from MinecraftMetascript/feat/worldgen/noise
Feat: Implement Noise
2 parents f21eaee + b9b126c commit 2451322

29 files changed

+2632
-1211
lines changed

cmd/build.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,21 @@ var buildCmd = &cobra.Command{
4747
stat, err := fs.Stat(os.DirFS("."), inFile)
4848

4949
if err != nil {
50-
log.Println("Error building project.", err)
50+
log.Println("Error building project", err)
5151
return
5252
}
53+
5354
if stat.IsDir() {
5455
log.Println("Project is a directory")
5556
} else {
5657
if content, err := os.ReadFile(inFile); err != nil {
57-
log.Println("Error building project:")
58+
log.Println("Error reading project:", err)
5859
} else {
5960
f := project.AddFile(inFile, string(content))
60-
f.Parse()
61+
err = f.Parse()
62+
if err != nil {
63+
log.Println("Error parsing project:", err)
64+
}
6165
}
6266
}
6367
r, err := json.MarshalIndent(project.BuildFsLike(outFile), "", " ")

examples/overworld-noise-settings/surface-rules/badlands.mms

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace MySpace {
1+
namespace minecraft {
22
Surface {
33
InBadlands = Biome(
44
minecraft:badlands,
@@ -14,9 +14,9 @@ namespace MySpace {
1414
If (StoneDepth(Floor)) [
1515
If (
1616
Or (
17-
Noise(minecraft:surface).Min(-0.909).Max(-0.5454)
18-
Noise(minecraft:surface).Min(-0.1818).Max(0.1818)
19-
Noise(minecraft:surface).Min(0.5454).Max(0.909)
17+
NoiseThreshold(minecraft:surface).Min(-0.909).Max(-0.5454)
18+
NoiseThreshold(minecraft:surface).Min(-0.1818).Max(0.1818)
19+
NoiseThreshold(minecraft:surface).Min(0.5454).Max(0.909)
2020
)
2121
) Block(minecraft:terracotta)
2222
Bandlands()

grammar/MinecraftMetascript.g4

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
grammar MinecraftMetascript;
22

3-
import Surface, Core_Lang;
3+
import Surface, Noise, Core_Lang;
44

5-
script: (namespace NL*)*;
5+
script: NL* (namespace NL*)*;
66

77
namespaceDeclaration: 'namespace' Identifier;
88
namespace: namespaceDeclaration NL* '{' NL* (contentBlocks NL*)* NL* '}';
99

10-
contentBlocks: surface;
10+
contentBlocks: surfaceBlock | noiseBlock;

grammar/Noise.g4

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
grammar Noise;
2+
import Core_Lang;
3+
4+
noiseBlock: 'Noise' NL* '{' NL* (noiseDeclaration NL*)* NL* '}';
5+
6+
noiseDeclaration: Identifier '=' noise;
7+
8+
noise: 'Noise' '(' NL* Int NL* ')' NL* (noise_Builder NL*)*;
9+
10+
noise_Builder: noise_Builder_Amplitudes;
11+
noise_Builder_Amplitudes: '.Amplitudes' '(' (number ',')* number ')';

grammar/Surface.g4

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
grammar Surface;
22
import Core_Lang;
33

4-
surface: 'Surface' NL* '{' NL* (surfaceStatement NL*)* NL* '}';
4+
surfaceBlock: 'Surface' NL* '{' NL* (surfaceStatement NL*)* NL* '}';
55
surfaceStatement: verticalAnchorDeclaration | surfaceConditionDeclaration | surfaceRuleDeclaration;
66

77
verticalAnchor: ('~'? Int) | Identifier;
@@ -20,7 +20,7 @@ surfaceCondition:
2020
| surfaceCondition_Hole
2121
| surfaceCondition_Steep
2222
| surfaceCondition_Freezing
23-
| surfaceCondition_Noise
23+
| surfaceCondition_NoiseThreshold
2424
| surfaceCondition_StoneDepth
2525
| surfaceCondition_AboveWater
2626
| surfaceCondition_YAbove
@@ -47,10 +47,10 @@ surfaceCondition_Freezing: 'Freezing' '(' ')';
4747

4848

4949
// These are split out to make them easier to differentiate in the go code
50-
surfaceCondition_NoiseBuilder_Min: '.Min(' number ')';
51-
surfaceCondition_NoiseBuilder_Max: '.Max(' number ')';
52-
surfaceCondition_NoiseBuilder: surfaceCondition_NoiseBuilder_Max | surfaceCondition_NoiseBuilder_Min;
53-
surfaceCondition_Noise: 'Noise' '(' resourceReference ')' NL* (surfaceCondition_NoiseBuilder NL*)*;
50+
surfaceCondition_NoiseThresholdBuilder_Min: '.Min(' number ')';
51+
surfaceCondition_NoiseThresholdBuilder_Max: '.Max(' number ')';
52+
surfaceCondition_NoiseThresholdBuilder: surfaceCondition_NoiseThresholdBuilder_Max | surfaceCondition_NoiseThresholdBuilder_Min;
53+
surfaceCondition_NoiseThreshold: 'NoiseThreshold' '(' resourceReference ')' NL* (surfaceCondition_NoiseThresholdBuilder NL*)*;
5454

5555
StoneDepthMode: 'Floor' | 'Ceiling';
5656
surfaceCondition_StoneDepth:

lang/Project.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/minecraftmetascript/mms/lib"
88
)
99
import _ "github.com/minecraftmetascript/mms/lang/constructs/worldgen/surface_rules"
10+
import _ "github.com/minecraftmetascript/mms/lang/constructs/worldgen/noise"
1011

1112
type Project struct {
1213
Files map[string]*File
@@ -18,7 +19,9 @@ func (p *Project) Diagnostics() []traversal.Diagnostic {
1819
for _, file := range p.Files {
1920
diagnostics = append(diagnostics, file.Diagnostics...)
2021
}
21-
return diagnostics
22+
23+
// Some things can create duplicate diagnostics, so we can deduplicate
24+
return lib.Unique(diagnostics)
2225
}
2326

2427
func NewProject() *Project {

lang/builder_chain/BuilderChain.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,33 @@ type BuilderChain[Target any] struct {
1717

1818
// Invoke looks up and calls the function registered for context type C.
1919
// Returns (nil, false) if no function is registered for that type.
20-
func Invoke[C antlr.ParserRuleContext, Target any](bc *BuilderChain[Target], ctx C, target *Target, scope *traversal.Scope, namespace string) bool {
21-
t := reflect.TypeOf((*C)(nil)).Elem()
22-
fn, ok := bc.fns[t]
20+
func Invoke[Target any](bc *BuilderChain[Target], ctx antlr.ParserRuleContext, target *Target, scope *traversal.Scope, namespace string) bool {
21+
ctxType := reflect.TypeOf(ctx)
22+
if ctxType.Kind() == reflect.Ptr {
23+
ctxType = ctxType.Elem()
24+
}
25+
26+
fn, ok := bc.fns[ctxType]
2327
if !ok {
2428
return false
2529
}
26-
if bc.seen[t] {
30+
if bc.seen[ctxType] {
2731
scope.DiagnoseSemanticWarning(
2832
fmt.Sprintf("Ignoring duplicate builder call %s", grammar.MinecraftMetascriptParserStaticData.RuleNames[ctx.GetRuleIndex()]),
2933
ctx,
3034
)
3135
return false
3236
} else {
3337
fn(ctx, target, scope, namespace)
34-
bc.seen[t] = true
38+
bc.seen[ctxType] = true
3539
}
3640
return true
3741
}
3842

3943
func Require[Target any](bc *BuilderChain[Target], ctx antlr.ParserRuleContext, scope *traversal.Scope, required reflect.Type, label string) {
44+
if required.Kind() == reflect.Ptr {
45+
required = required.Elem()
46+
}
4047
if !bc.seen[required] {
4148
scope.DiagnoseSemanticError(
4249
fmt.Sprintf("Missing required builder call %s", label),
@@ -53,8 +60,12 @@ type Registration[Target any] struct {
5360

5461
// Build creates a type-safe Registration for a context type C.
5562
func Build[C antlr.ParserRuleContext, Target any](fn func(ctx C, target *Target, scope *traversal.Scope, namespace string)) Registration[Target] {
63+
ctxType := reflect.TypeFor[C]()
64+
if ctxType.Kind() == reflect.Ptr {
65+
ctxType = ctxType.Elem()
66+
}
5667
return Registration[Target]{
57-
t: reflect.TypeOf((*C)(nil)).Elem(),
68+
t: ctxType,
5869
fn: func(ctx any, target *Target, scope *traversal.Scope, namespace string) {
5970
fn(ctx.(C), target, scope, namespace)
6071
},
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package noise
2+
3+
import (
4+
"encoding/json"
5+
"reflect"
6+
"strconv"
7+
8+
"github.com/antlr4-go/antlr/v4"
9+
"github.com/minecraftmetascript/mms/lang/builder_chain"
10+
"github.com/minecraftmetascript/mms/lang/grammar"
11+
"github.com/minecraftmetascript/mms/lang/traversal"
12+
"github.com/minecraftmetascript/mms/lib"
13+
)
14+
15+
func init() {
16+
traversal.ConstructRegistry.Register(
17+
reflect.TypeFor[*grammar.NoiseDeclarationContext](),
18+
func(ctx antlr.ParserRuleContext, currentNamespace string, scope *traversal.Scope) traversal.Construct {
19+
declaration := ctx.(*grammar.NoiseDeclarationContext)
20+
s := traversal.ProcessDeclaration(declaration, declaration.Noise(), scope, currentNamespace, "Noise")
21+
if s == nil {
22+
return nil
23+
}
24+
return s.GetValue()
25+
},
26+
)
27+
traversal.ConstructRegistry.Register(
28+
reflect.TypeFor[*grammar.NoiseContext](),
29+
func(ctx antlr.ParserRuleContext, currentNamespace string, scope *traversal.Scope) traversal.Construct {
30+
noise := ctx.(*grammar.NoiseContext)
31+
out := &Noise{
32+
Amplitudes: make([]float64, 0),
33+
}
34+
builder_chain.Builder_GetInt(
35+
noise, func(i int) { out.FirstOctave = i }, scope, "FirstOctave",
36+
)
37+
38+
for _, r := range noise.AllNoise_Builder() {
39+
if amplitudes := r.Noise_Builder_Amplitudes(); amplitudes != nil {
40+
for _, amplitude := range amplitudes.AllNumber() {
41+
if amplitude != nil {
42+
val, err := strconv.ParseFloat(amplitude.GetText(), 64)
43+
if err != nil {
44+
scope.DiagnoseSemanticError(
45+
"Invalid amplitude value",
46+
amplitude,
47+
)
48+
} else {
49+
out.Amplitudes = append(out.Amplitudes, val)
50+
}
51+
}
52+
}
53+
} else {
54+
scope.DiagnoseSemanticError("Missing amplitudes value", r)
55+
}
56+
}
57+
58+
return out
59+
},
60+
)
61+
}
62+
63+
type Noise struct {
64+
traversal.Construct
65+
FirstOctave int
66+
Amplitudes []float64
67+
}
68+
69+
func (n Noise) MarshalJSON() ([]byte, error) {
70+
return json.Marshal(struct {
71+
FirstOctave int `json:"firstOctave"`
72+
Amplitudes []float64 `json:"amplitudes"`
73+
}{
74+
FirstOctave: n.FirstOctave,
75+
Amplitudes: n.Amplitudes,
76+
})
77+
}
78+
79+
func (n Noise) ExportSymbol(symbol traversal.Symbol, rootDir *lib.FileTreeLike) error {
80+
data, err := json.MarshalIndent(n, "", " ")
81+
if err != nil {
82+
return err
83+
}
84+
rootDir.
85+
MkDir(symbol.GetReference().GetNamespace(), nil).
86+
MkDir("worldgen", nil).
87+
MkDir("noise", nil).
88+
MkFile(symbol.GetReference().GetName()+".json", string(data), nil)
89+
return nil
90+
91+
}

lang/constructs/worldgen/surface_rules/Condition_AboveWater.go

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,36 +13,40 @@ import (
1313
)
1414

1515
func init() {
16-
waterBuildChain := builder_chain.NewBuilderChain[AboveWaterCondition](
17-
builder_chain.Build(
18-
func(ctx *grammar.SharedBuilder_OffsetContext, out *AboveWaterCondition, scope *traversal.Scope, _ string) {
19-
builder_chain.Builder_GetInt(ctx, func(v int) { out.Offset = v }, scope, "Offset")
20-
},
21-
),
22-
builder_chain.Build(
23-
func(ctx *grammar.SharedBuilder_AddContext, out *AboveWaterCondition, _ *traversal.Scope, _ string) {
24-
builder_chain.SharedBuilder_Add(ctx, func(v bool) { out.Add = v })
25-
},
26-
),
27-
builder_chain.Build(
28-
func(ctx *grammar.SharedBuilder_MulContext, out *AboveWaterCondition, scope *traversal.Scope, _ string) {
29-
builder_chain.Builder_GetFloat(
30-
ctx,
31-
func(v float64) { out.DepthMultiplier = v },
32-
scope,
33-
"Multiplier",
34-
)
35-
},
36-
),
37-
)
38-
3916
traversal.ConstructRegistry.Register(
4017
reflect.TypeFor[grammar.SurfaceCondition_AboveWaterContext](),
4118
func(ctx antlr.ParserRuleContext, namespace string, scope *traversal.Scope) traversal.Construct {
19+
waterBuildChain := builder_chain.NewBuilderChain[AboveWaterCondition](
20+
builder_chain.Build(
21+
func(ctx *grammar.SharedBuilder_OffsetContext, out *AboveWaterCondition, scope *traversal.Scope, _ string) {
22+
builder_chain.Builder_GetInt(ctx, func(v int) { out.Offset = v }, scope, "Offset")
23+
},
24+
),
25+
builder_chain.Build(
26+
func(ctx *grammar.SharedBuilder_AddContext, out *AboveWaterCondition, _ *traversal.Scope, _ string) {
27+
builder_chain.SharedBuilder_Add(ctx, func(v bool) { out.Add = v })
28+
},
29+
),
30+
builder_chain.Build(
31+
func(ctx *grammar.SharedBuilder_MulContext, out *AboveWaterCondition, scope *traversal.Scope, _ string) {
32+
builder_chain.Builder_GetFloat(
33+
ctx,
34+
func(v float64) { out.DepthMultiplier = v },
35+
scope,
36+
"Multiplier",
37+
)
38+
},
39+
),
40+
)
41+
4242
aboveWater := ctx.(*grammar.SurfaceCondition_AboveWaterContext)
4343
out := &AboveWaterCondition{}
4444
for _, r := range aboveWater.AllSurfaceCondition_AboveWaterBuilder() {
45-
builder_chain.Invoke(waterBuildChain, r, out, scope, namespace)
45+
child := r.GetChild(0)
46+
if child == nil {
47+
continue
48+
}
49+
builder_chain.Invoke(waterBuildChain, child.(antlr.ParserRuleContext), out, scope, namespace)
4650
}
4751

4852
return out

0 commit comments

Comments
 (0)