diff --git a/server/command/command.go b/server/command/command.go index ff2c607..08873ab 100644 --- a/server/command/command.go +++ b/server/command/command.go @@ -1,86 +1,10 @@ package command import ( - "io" - "unsafe" - - pk "github.com/Tnze/go-mc/net/packet" + "github.com/Tnze/go-mc/chat" + "strings" ) -type CmdSet struct { - root int32 - nodes []Node -} - -func NewCmdSet() *CmdSet { - return &CmdSet{ - root: 0, - nodes: []Node{ - { - flags: TypeRoot, - parser: nil, - }, - }, - } -} - -func (c *CmdSet) AddNode(n Node) int32 { - c.nodes = append(c.nodes, n) - return int32(len(c.nodes) - 1) -} - -func (c *CmdSet) WriteTo(w io.Writer) (int64, error) { - return pk.Tuple{ - pk.Array(c.nodes), - pk.VarInt(c.root), - }.WriteTo(w) -} - -func (c *CmdSet) Execute(cmd string) error { - for _, child := range c.nodes[c.root].children { - if node := c.nodes[child]; node.flags&0x03 == TypeLiteral { - - } - } -} - -type Node struct { - flags byte - children []int32 - redirect int32 - name string - parser Parser - suggestions string -} - -func (n *Node) WriteTo(w io.Writer) (int64, error) { - return pk.Tuple{ - pk.Byte(n.flags), - pk.Array(*(*[]pk.VarInt)(unsafe.Pointer(&n.children))), - pk.Opt{ // Redirect - Has: n.flags&HasRedirect != 0, - Field: pk.VarInt(n.redirect), - }, - pk.Opt{ // Name - Has: n.flags&0x03 == TypeLiteral || n.flags&0x03 == TypeArgument, - Field: pk.String(n.name), - }, - pk.Opt{ // Parser & Properties - Has: n.flags&0x03 == TypeArgument, - Field: n.parser, - }, - pk.Opt{ - Has: n.flags&HasSuggestionsType != 0, - Field: pk.Identifier(n.suggestions), - }, - }.WriteTo(w) -} - -type Parser interface { - pk.FieldEncoder - Parse(cmd string) (token string, value interface{}, err error) -} - const ( TypeRoot byte = iota TypeLiteral @@ -93,3 +17,207 @@ const ( HasRedirect HasSuggestionsType ) + +type Node interface { + Then(nodes ...Node) Node + Runnable() HandleFunc + Parse(cmd string, store map[string]interface{}) (token string, next Node, err error) +} + +type Root struct { + children map[string]Node +} + +type HandleFunc func(args map[string]interface{}) + +func NewGraph() *Root { + return &Root{children: make(map[string]Node)} +} + +func (r *Root) Then(nodes ...Node) Node { + for _, node := range nodes { + l, ok := node.(*Literal) + if !ok { + panic("you could only add Literal node to Root") + } + if _, ok := r.children[l.Name]; ok { + panic("the Literal(" + l.Name + ") is already exist") + } + r.children[l.Name] = node + } + return r +} + +func Lite(literal string) *Literal { + return &Literal{Name: literal} +} + +func Arg(name string, parser Parser) *Argument { + return &Argument{Name: name, Parser: parser} +} + +func (r *Root) Runnable() HandleFunc { + return nil +} + +func (r *Root) Parse(cmd string, _ map[string]interface{}) (token string, next Node, err error) { + token, value, err := StringParser(0).Parse(cmd) + if err != nil { + return "", nil, err + } + child, ok := r.children[value.(string)] + if !ok { + return "", nil, Error{chat.TranslateMsg("command.unknown.command")} + } + return "", child, nil +} + +func (r *Root) Run(cmd string) error { + var node Node = r + args := make(map[string]interface{}) + for { + token, next, err := node.Parse(cmd, args) + if err != nil { + return err + } + cmd = strings.TrimSpace(strings.TrimPrefix(cmd, token)) + if next == nil || len(cmd) == 0 { + f := node.Runnable() + if f == nil { + return Error{chat.TranslateMsg("command.unknown.command")} + } + f(args) + return nil + } + node = next + } +} + +type Children struct { + Lite map[string]*Literal + Args *Argument +} + +func (c *Children) add(nodes []Node) { + for _, node := range nodes { + switch node.(type) { + case *Literal: + if c.Args != nil { + panic("this Node already has an Argument Node children, cannot Add Literal Node") + } + if c.Lite == nil { + c.Lite = make(map[string]*Literal) + } + child := node.(*Literal) + c.Lite[child.Name] = child + case *Argument: + if c.Lite != nil { + panic("this Node already has Literal Node children, cannot Add Argument Node") + } + if c.Args != nil { + panic("this Node already set an Argument Node child, cannot be duplicate") + } + c.Args = node.(*Argument) + } + } + return +} + +type Handler struct { + Handler HandleFunc +} + +func (h *Handler) Handle(f HandleFunc) { + if h.Handler != nil { + panic("handler func set twice") + } + h.Handler = f +} + +func (h *Handler) Runnable() HandleFunc { + return h.Handler +} + +type Literal struct { + Name string + Children + Handler +} + +func (l *Literal) Then(nodes ...Node) Node { + l.add(nodes) + return l +} + +func (l *Literal) Parse(cmd string, store map[string]interface{}) (token string, next Node, err error) { + cmd = strings.TrimPrefix(cmd, l.Name) + store[l.Name] = nil + if l.Children.Lite != nil { + token, value, err := StringParser(0).Parse(strings.TrimSpace(cmd)) + if err != nil { + return token, nil, err + } + if next, ok := l.Children.Lite[value.(string)]; ok { + return l.Name, next, nil + } + return "", nil, Error{chat.TranslateMsg("command.unknown.command")} + } else if l.Children.Args != nil { + return l.Name, l.Children.Args, nil + } else { + return l.Name, nil, nil + } +} + +func (l *Literal) Handle(f HandleFunc) Node { + l.Handler.Handle(f) + return l +} + +type Argument struct { + Name string + Children + Parser Parser + Handler +} + +func (a *Argument) Parse(cmd string, store map[string]interface{}) (token string, next Node, err error) { + token, value, err := a.Parser.Parse(cmd) + if err != nil { + return "", nil, err + } + store[a.Name] = value + cmd = strings.TrimSpace(strings.TrimPrefix(cmd, token)) + + if a.Children.Lite != nil { + _, value, err := StringParser(0).Parse(strings.TrimSpace(cmd)) + if err != nil { + return token, nil, err + } + if next, ok := a.Children.Lite[value.(string)]; ok { + return token, next, nil + } + return "", nil, Error{chat.TranslateMsg("command.unknown.command")} + } else if a.Children.Args != nil { + return token, a.Children.Args, nil + } else { + return token, nil, nil + } +} + +func (a *Argument) Then(nodes ...Node) Node { + a.add(nodes) + return a +} + +func (a *Argument) Handle(f HandleFunc) Node { + a.Handler.Handle(f) + return a +} + +type Error struct { + msg chat.Message +} + +func (e Error) Error() string { + return e.msg.String() +} diff --git a/server/command/command_test.go b/server/command/command_test.go new file mode 100644 index 0000000..d1a35df --- /dev/null +++ b/server/command/command_test.go @@ -0,0 +1,52 @@ +package command + +import "testing" + +func TestRoot_Run(t *testing.T) { + g := NewGraph().Then( + Lite("whitelist").Then( + Lite("add").Then( + Arg("targets", StringParser(0)). + Handle(func(args map[string]interface{}) { + t.Logf("whitelist add: %v", args) + })), + Lite("remove").Then( + Arg("targets", StringParser(0)). + Handle(func(args map[string]interface{}) { + t.Logf("whitelist remove: %v", args) + })), + Lite("on").Handle(func(args map[string]interface{}) { + t.Logf("whitelist on: %v", args) + }), + Lite("off").Handle(func(args map[string]interface{}) { + t.Logf("whitelist off: %v", args) + }), + ), + ).(*Root) + + targetA := Arg("targetA", StringParser(0)) + targetB := Arg("targetB", StringParser(0)) + targetB.Handle(func(args map[string]interface{}) { + t.Logf("tp A B parsed: %v", args) + }) + tp := Lite("tp").Then(targetA.Then( + Lite("form").Then(targetB), + Lite("to").Then(targetB), + )) + g.Then(tp) + + err := g.Run("tp Tnze to Xi_Xi_Mi") + if err != nil { + t.Fatal(err) + } + + //g2 := NewGraph() + //g2.Then( + // g.Lite("whitelist").Then( + // // using reflect to generate all arguments nodes + // g.Lite("add").Then(g.Func(func(player GameProfile) { + // + // })), + // ), + //) +}