diff --git a/cmd/blocksetter/main.go b/cmd/blocksetter/main.go new file mode 100644 index 000000000..819d72922 --- /dev/null +++ b/cmd/blocksetter/main.go @@ -0,0 +1,395 @@ +package main + +import ( + "flag" + "fmt" + "go/ast" + "go/parser" + "go/token" + "io" + "log" + "os" + "path/filepath" + "sort" +) + +func main() { + out := flag.String("o", "", "output file for facing interface and methods") + flag.Parse() + + if len(flag.Args()) != 1 { + log.Fatalln("must pass one package to produce facing interface methods for") + } + dir := flag.Args()[0] + if !filepath.IsAbs(dir) { + abs, err := filepath.Abs(dir) + if err != nil { + log.Fatalln(err) + } + dir = abs + } + + fset := token.NewFileSet() + pkgs, err := parser.ParseDir(fset, dir, func(info os.FileInfo) bool { + name := info.Name() + return filepath.Ext(name) == ".go" && filepath.Base(name) != *out + }, parser.ParseComments) + if err != nil { + log.Fatalln(err) + } + if len(pkgs) != 1 { + log.Fatalln("expected exactly one package in directory") + } + + f, err := os.OpenFile(*out, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_APPEND, 0644) + if err != nil { + log.Fatalln(err) + } + for _, pkg := range pkgs { + files := make([]*ast.File, 0, len(pkg.Files)) + for _, file := range pkg.Files { + files = append(files, file) + } + procPackage(pkg.Name, files, f) + } + _ = f.Close() +} + +func procPackage(pkgName string, files []*ast.File, w io.Writer) { + b := &builder{ + pkgName: pkgName, + files: files, + fields: make(map[string][]*ast.Field), + aliases: make(map[string]string), + handled: map[string]struct{}{}, + funcs: map[string]struct{}{}, + } + b.readStructFields() + b.readFuncs() + b.resolveBlocks() + b.sortNames() + + b.writePackage(w) + b.writeInterfaces(w) + b.writeMethods(w) +} + +type builder struct { + pkgName string + files []*ast.File + fields map[string][]*ast.Field + funcs map[string]struct{} + aliases map[string]string + handled map[string]struct{} + facingBlocks []string + axisBlocks []string + colourBlocks []string +} + +func (b *builder) sortNames() { + sort.Strings(b.facingBlocks) + sort.Strings(b.axisBlocks) + sort.Strings(b.colourBlocks) +} + +func (b *builder) writePackage(w io.Writer) { + if _, err := fmt.Fprintf(w, "// Code generated by cmd/blocksetter; DO NOT EDIT.\n\npackage %v\n\n", b.pkgName); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "import ("); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "\t\"github.com/df-mc/dragonfly/server/block/cube\""); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "\t\"github.com/df-mc/dragonfly/server/item\""); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "\t\"github.com/df-mc/dragonfly/server/world\""); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, ")"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w); err != nil { + log.Fatalln(err) + } +} + +func (b *builder) writeInterfaces(w io.Writer) { + if _, err := fmt.Fprintln(w, "// HasFacing represents a block with a horizontal facing direction."); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "type HasFacing interface {"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "\tworld.Block"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "\tWithFacing(facing cube.Direction) world.Block"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "}"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "// HasAxis represents a block with an axis."); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "type HasAxis interface {"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "\tworld.Block"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "\tWithAxis(axis cube.Axis) world.Block"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "}"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "// HasColour represents a block with a colour."); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "type HasColour interface {"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "\tworld.Block"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "\tWithColour(colour item.Colour) world.Block"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "}"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w); err != nil { + log.Fatalln(err) + } +} + +func (b *builder) writeMethods(w io.Writer) { + for _, name := range b.facingBlocks { + if _, err := fmt.Fprintln(w, "// WithFacing returns the block with the facing set to facing."); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintf(w, "func (b %v) WithFacing(facing cube.Direction) world.Block {\n", name); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "\tb.Facing = facing"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "\treturn b"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "}"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w); err != nil { + log.Fatalln(err) + } + } + for _, name := range b.axisBlocks { + if _, err := fmt.Fprintln(w, "// WithAxis returns the block with the axis set to axis."); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintf(w, "func (b %v) WithAxis(axis cube.Axis) world.Block {\n", name); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "\tb.Axis = axis"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "\treturn b"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "}"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w); err != nil { + log.Fatalln(err) + } + } + for _, name := range b.colourBlocks { + if _, err := fmt.Fprintln(w, "// WithColour returns the block with the colour set to colour."); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintf(w, "func (b %v) WithColour(colour item.Colour) world.Block {\n", name); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "\tb.Colour = colour"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "\treturn b"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w, "}"); err != nil { + log.Fatalln(err) + } + if _, err := fmt.Fprintln(w); err != nil { + log.Fatalln(err) + } + } +} + +func (b *builder) resolveBlocks() { + for structName, fields := range b.fields { + if _, ok := b.funcs[structName]; !ok { + continue + } + if hasFacingDirectionField(fields) { + b.facingBlocks = append(b.facingBlocks, structName) + } + if hasAxisField(fields) { + b.axisBlocks = append(b.axisBlocks, structName) + } + if hasColourField(fields) { + b.colourBlocks = append(b.colourBlocks, structName) + } + } +} + +func hasFacingDirectionField(fields []*ast.Field) bool { + for _, field := range fields { + if len(field.Names) == 0 { + continue + } + for _, name := range field.Names { + if name.Name != "Facing" { + continue + } + if s, ok := field.Type.(*ast.SelectorExpr); ok { + if x, ok := s.X.(*ast.Ident); ok && x.Name == "cube" && s.Sel.Name == "Direction" { + return true + } + } + } + } + return false +} + +func hasAxisField(fields []*ast.Field) bool { + for _, field := range fields { + if len(field.Names) == 0 { + continue + } + for _, name := range field.Names { + if name.Name != "Axis" { + continue + } + if s, ok := field.Type.(*ast.SelectorExpr); ok { + if x, ok := s.X.(*ast.Ident); ok && x.Name == "cube" && s.Sel.Name == "Axis" { + return true + } + } + } + } + return false +} + +func hasColourField(fields []*ast.Field) bool { + for _, field := range fields { + if len(field.Names) == 0 { + continue + } + for _, name := range field.Names { + if name.Name != "Colour" { + continue + } + if s, ok := field.Type.(*ast.SelectorExpr); ok { + if x, ok := s.X.(*ast.Ident); ok && x.Name == "item" && s.Sel.Name == "Colour" { + return true + } + } + } + } + return false +} + +func (b *builder) readFuncs() { + for _, f := range b.files { + ast.Inspect(f, b.readFuncDecls) + } +} + +func (b *builder) readFuncDecls(node ast.Node) bool { + fun, ok := node.(*ast.FuncDecl) + if !ok || fun.Name.Name != "EncodeBlock" || fun.Recv == nil { + return true + } + if ident, ok := fun.Recv.List[0].Type.(*ast.Ident); ok { + b.funcs[ident.Name] = struct{}{} + } + return true +} + +func (b *builder) readStructFields() { + for _, f := range b.files { + ast.Inspect(f, b.readStructs) + } + b.resolveEmbedded() + b.resolveAliases() +} + +func (b *builder) resolveAliases() { + for name, alias := range b.aliases { + b.fields[name] = b.findFields(alias) + } +} + +func (b *builder) findFields(structName string) []*ast.Field { + for { + if fields, ok := b.fields[structName]; ok { + return fields + } + if nested, ok := b.aliases[structName]; ok { + structName = nested + continue + } + return nil + } +} + +func (b *builder) resolveEmbedded() { + for name, fields := range b.fields { + if _, ok := b.handled[name]; ok { + continue + } + newFields := make([]*ast.Field, 0, len(fields)) + for _, f := range fields { + if len(f.Names) == 0 { + if ident, ok := f.Type.(*ast.Ident); ok { + for _, af := range b.findFields(ident.Name) { + if len(af.Names) == 0 { + b.resolveEmbedded() + return + } + } + newFields = append(newFields, b.findFields(ident.Name)...) + } + } else { + newFields = append(newFields, f) + } + } + b.handled[name] = struct{}{} + b.fields[name] = newFields + } +} + +func (b *builder) readStructs(node ast.Node) bool { + s, ok := node.(*ast.TypeSpec) + if !ok { + return true + } + switch t := s.Type.(type) { + case *ast.StructType: + b.fields[s.Name.Name] = t.Fields.List + case *ast.Ident: + b.aliases[s.Name.Name] = t.Name + } + return true +} diff --git a/server/block/register.go b/server/block/register.go index 976374f9f..42e49ebb6 100644 --- a/server/block/register.go +++ b/server/block/register.go @@ -6,6 +6,7 @@ import ( ) //go:generate go run ../../cmd/blockhash -o hash.go . +//go:generate go run ../../cmd/blocksetter -o setter.go . // init registers all blocks implemented by Dragonfly. func init() { diff --git a/server/block/setter.go b/server/block/setter.go new file mode 100644 index 000000000..1ad165826 --- /dev/null +++ b/server/block/setter.go @@ -0,0 +1,315 @@ +// Code generated by cmd/blocksetter; DO NOT EDIT. + +package block + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/item" + "github.com/df-mc/dragonfly/server/world" +) + +// HasFacing represents a block with a horizontal facing direction. +type HasFacing interface { + world.Block + WithFacing(facing cube.Direction) world.Block +} + +// HasAxis represents a block with an axis. +type HasAxis interface { + world.Block + WithAxis(axis cube.Axis) world.Block +} + +// HasColour represents a block with a colour. +type HasColour interface { + world.Block + WithColour(colour item.Colour) world.Block +} + +// WithFacing returns the block with the facing set to facing. +func (b Anvil) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b Bed) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b BlastFurnace) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b Campfire) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b Chest) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b CocoaBean) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b CopperDoor) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b CopperGolemStatue) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b CopperTrapdoor) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b DecoratedPot) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b EnderChest) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b Furnace) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b GlazedTerracotta) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b Grindstone) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b Ladder) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b Lectern) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b LitPumpkin) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b Loom) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b PinkPetals) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b Pumpkin) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b Smoker) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b Stairs) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b Stonecutter) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b WoodDoor) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b WoodFenceGate) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithFacing returns the block with the facing set to facing. +func (b WoodTrapdoor) WithFacing(facing cube.Direction) world.Block { + b.Facing = facing + return b +} + +// WithAxis returns the block with the axis set to axis. +func (b Basalt) WithAxis(axis cube.Axis) world.Block { + b.Axis = axis + return b +} + +// WithAxis returns the block with the axis set to axis. +func (b Bone) WithAxis(axis cube.Axis) world.Block { + b.Axis = axis + return b +} + +// WithAxis returns the block with the axis set to axis. +func (b CopperChain) WithAxis(axis cube.Axis) world.Block { + b.Axis = axis + return b +} + +// WithAxis returns the block with the axis set to axis. +func (b Deepslate) WithAxis(axis cube.Axis) world.Block { + b.Axis = axis + return b +} + +// WithAxis returns the block with the axis set to axis. +func (b Froglight) WithAxis(axis cube.Axis) world.Block { + b.Axis = axis + return b +} + +// WithAxis returns the block with the axis set to axis. +func (b HayBale) WithAxis(axis cube.Axis) world.Block { + b.Axis = axis + return b +} + +// WithAxis returns the block with the axis set to axis. +func (b IronChain) WithAxis(axis cube.Axis) world.Block { + b.Axis = axis + return b +} + +// WithAxis returns the block with the axis set to axis. +func (b Log) WithAxis(axis cube.Axis) world.Block { + b.Axis = axis + return b +} + +// WithAxis returns the block with the axis set to axis. +func (b MuddyMangroveRoots) WithAxis(axis cube.Axis) world.Block { + b.Axis = axis + return b +} + +// WithAxis returns the block with the axis set to axis. +func (b PurpurPillar) WithAxis(axis cube.Axis) world.Block { + b.Axis = axis + return b +} + +// WithAxis returns the block with the axis set to axis. +func (b QuartzPillar) WithAxis(axis cube.Axis) world.Block { + b.Axis = axis + return b +} + +// WithAxis returns the block with the axis set to axis. +func (b Wood) WithAxis(axis cube.Axis) world.Block { + b.Axis = axis + return b +} + +// WithColour returns the block with the colour set to colour. +func (b Banner) WithColour(colour item.Colour) world.Block { + b.Colour = colour + return b +} + +// WithColour returns the block with the colour set to colour. +func (b Bed) WithColour(colour item.Colour) world.Block { + b.Colour = colour + return b +} + +// WithColour returns the block with the colour set to colour. +func (b Carpet) WithColour(colour item.Colour) world.Block { + b.Colour = colour + return b +} + +// WithColour returns the block with the colour set to colour. +func (b Concrete) WithColour(colour item.Colour) world.Block { + b.Colour = colour + return b +} + +// WithColour returns the block with the colour set to colour. +func (b ConcretePowder) WithColour(colour item.Colour) world.Block { + b.Colour = colour + return b +} + +// WithColour returns the block with the colour set to colour. +func (b GlazedTerracotta) WithColour(colour item.Colour) world.Block { + b.Colour = colour + return b +} + +// WithColour returns the block with the colour set to colour. +func (b StainedGlass) WithColour(colour item.Colour) world.Block { + b.Colour = colour + return b +} + +// WithColour returns the block with the colour set to colour. +func (b StainedGlassPane) WithColour(colour item.Colour) world.Block { + b.Colour = colour + return b +} + +// WithColour returns the block with the colour set to colour. +func (b StainedTerracotta) WithColour(colour item.Colour) world.Block { + b.Colour = colour + return b +} + +// WithColour returns the block with the colour set to colour. +func (b Wool) WithColour(colour item.Colour) world.Block { + b.Colour = colour + return b +}