]> git.lizzy.rs Git - hydra-dragonfire.git/commitdiff
Migrate to gopher-lua
authorElias Fleckenstein <eliasfleckenstein@web.de>
Sat, 28 May 2022 21:16:55 +0000 (23:16 +0200)
committerElias Fleckenstein <eliasfleckenstein@web.de>
Sat, 28 May 2022 21:16:55 +0000 (23:16 +0200)
18 files changed:
auth.go
builtin/vector.lua
callbacks.go [deleted file]
client.go
convert.go [deleted file]
example/dump-traffic.lua [new file with mode: 0755]
go.mod
go.sum
hydra.go
mkconvert.lua [deleted file]
parse_spec.lua [new file with mode: 0644]
poll.go
spec/client/pkt
spec/client/struct
tolua/generate.lua [new file with mode: 0755]
tolua/generated.go [new file with mode: 0644]
tolua/static.go [new file with mode: 0644]
types.go [deleted file]

diff --git a/auth.go b/auth.go
index bc8d24e09bdab7c58e4186bd8a988fd95c76b543..879ffcee6ecbd85060be09b88148739e71d7d435 100644 (file)
--- a/auth.go
+++ b/auth.go
@@ -2,8 +2,9 @@ package main
 
 import (
        "github.com/HimbeerserverDE/srp"
-       "github.com/Shopify/go-lua"
        "github.com/anon55555/mt"
+       "github.com/dragonfireclient/hydra/tolua"
+       "github.com/yuin/gopher-lua"
        "strings"
        "time"
 )
@@ -23,47 +24,50 @@ type Auth struct {
        username          string
        password          string
        language          string
+       version           string
        state             authState
        err               string
        srpBytesA, bytesA []byte
+       userdata          *lua.LUserData
 }
 
-func getAuth(l *lua.State) *Auth {
-       return lua.CheckUserData(l, 1, "hydra.auth").(*Auth)
+var authFuncs = map[string]lua.LGFunction{
+       "username": l_auth_username,
+       "password": l_auth_password,
+       "language": l_auth_language,
+       "version":  l_auth_version,
+       "state":    l_auth_state,
 }
 
-func (auth *Auth) create(client *Client) {
+func getAuth(l *lua.LState) *Auth {
+       return l.CheckUserData(1).Value.(*Auth)
+}
+
+func (auth *Auth) create(client *Client, l *lua.LState) {
+       if client.state != csNew {
+               panic("can't add auth component after connect")
+       }
+
        auth.client = client
        auth.language = "en_US"
+       auth.version = "hydra-dragonfire"
        auth.state = asInit
+       auth.userdata = l.NewUserData()
+       auth.userdata.Value = auth
+       l.SetMetatable(auth.userdata, l.GetTypeMetatable("hydra.auth"))
 }
 
-func (auth *Auth) push(l *lua.State) {
-       l.PushUserData(auth)
-
-       if lua.NewMetaTable(l, "hydra.auth") {
-               lua.NewLibrary(l, []lua.RegistryFunction{
-                       {Name: "username", Function: l_auth_username},
-                       {Name: "password", Function: l_auth_password},
-                       {Name: "language", Function: l_auth_language},
-                       {Name: "state", Function: l_auth_state},
-               })
-               l.SetField(-2, "__index")
-       }
-       l.SetMetaTable(-2)
+func (auth *Auth) tolua() lua.LValue {
+       return auth.userdata
 }
 
-func (auth *Auth) canConnect() (bool, string) {
+func (auth *Auth) connect() {
        if auth.username == "" {
-               return false, "missing username"
+               panic("missing username")
        }
 
-       return true, ""
-}
-
-func (auth *Auth) connect() {
        go func() {
-               for auth.state == asInit && auth.client.state == csConnected {
+               for auth.client.state == csConnected && auth.state == asInit {
                        auth.client.conn.SendCmd(&mt.ToSrvInit{
                                SerializeVer: 28,
                                MinProtoVer:  39,
@@ -75,10 +79,10 @@ func (auth *Auth) connect() {
        }()
 }
 
-func (auth *Auth) setError(err string) {
-       auth.state = asError
+func (auth *Auth) fail(err string) {
        auth.err = err
-       auth.client.conn.Close()
+       auth.state = asError
+       auth.client.disconnect()
 }
 
 func (auth *Auth) checkState(state authState, pkt *mt.Pkt) bool {
@@ -86,12 +90,12 @@ func (auth *Auth) checkState(state authState, pkt *mt.Pkt) bool {
                return true
        }
 
-       auth.setError("received " + pktToString(pkt) + " in invalid state")
+       auth.fail("received " + string(tolua.PktType(pkt)) + " in invalid state")
        return false
 }
 
-func (auth *Auth) handle(pkt *mt.Pkt, l *lua.State, idx int) {
-       if pkt == nil {
+func (auth *Auth) process(pkt *mt.Pkt) {
+       if auth.state == asError {
                return
        }
 
@@ -102,14 +106,14 @@ func (auth *Auth) handle(pkt *mt.Pkt, l *lua.State, idx int) {
                }
 
                if cmd.SerializeVer != 28 {
-                       auth.setError("unsupported serialize_ver")
+                       auth.fail("unsupported serialize version")
                        return
                }
 
                if cmd.AuthMethods == mt.FirstSRP {
                        salt, verifier, err := srp.NewClient([]byte(strings.ToLower(auth.username)), []byte(auth.password))
                        if err != nil {
-                               auth.setError(err.Error())
+                               auth.fail(err.Error())
                                return
                        }
 
@@ -123,7 +127,7 @@ func (auth *Auth) handle(pkt *mt.Pkt, l *lua.State, idx int) {
                        var err error
                        auth.srpBytesA, auth.bytesA, err = srp.InitiateHandshake()
                        if err != nil {
-                               auth.setError(err.Error())
+                               auth.fail(err.Error())
                                return
                        }
 
@@ -133,8 +137,8 @@ func (auth *Auth) handle(pkt *mt.Pkt, l *lua.State, idx int) {
                        })
                        auth.state = asRequested
                } else {
-                       auth.setError("invalid auth methods")
-                       return                  
+                       auth.fail("invalid auth methods")
+                       return
                }
 
        case *mt.ToCltSRPBytesSaltB:
@@ -144,7 +148,7 @@ func (auth *Auth) handle(pkt *mt.Pkt, l *lua.State, idx int) {
 
                srpBytesK, err := srp.CompleteHandshake(auth.srpBytesA, auth.bytesA, []byte(strings.ToLower(auth.username)), []byte(auth.password), cmd.Salt, cmd.B)
                if err != nil {
-                       auth.setError(err.Error())
+                       auth.fail(err.Error())
                        return
                }
 
@@ -153,7 +157,7 @@ func (auth *Auth) handle(pkt *mt.Pkt, l *lua.State, idx int) {
                auth.bytesA = []byte{}
 
                if M == nil {
-                       auth.setError("srp safety check fail")
+                       auth.fail("srp safety check fail")
                        return
                }
 
@@ -180,72 +184,60 @@ func (auth *Auth) handle(pkt *mt.Pkt, l *lua.State, idx int) {
                        Patch:    0,
                        Reserved: 0,
                        Formspec: 4,
-                       Version:  "hydra-dragonfire",
+                       Version:  auth.version,
                })
                auth.state = asActive
        }
 }
 
-func l_auth_username(l *lua.State) int {
-       auth := getAuth(l)
-
-       if l.IsString(2) {
-               if auth.client.state > csNew {
-                       panic("can't change username after connecting")
+func (auth *Auth) accessProperty(l *lua.LState, key string, ptr *string) int {
+       if str, ok := l.Get(2).(lua.LString); ok {
+               if auth.client.state != csNew {
+                       panic("can't change " + key + " after connecting")
                }
-               auth.username = lua.CheckString(l, 2)
+               *ptr = string(str)
                return 0
        } else {
-               l.PushString(auth.username)
+               l.Push(lua.LString(*ptr))
                return 1
        }
 }
 
-func l_auth_password(l *lua.State) int {
+func l_auth_username(l *lua.LState) int {
        auth := getAuth(l)
+       return auth.accessProperty(l, "username", &auth.username)
+}
 
-       if l.IsString(2) {
-               if auth.client.state > csNew {
-                       panic("can't change password after connecting")
-               }
-               auth.password = lua.CheckString(l, 2)
-               return 0
-       } else {
-               l.PushString(auth.password)
-               return 1
-       }
+func l_auth_password(l *lua.LState) int {
+       auth := getAuth(l)
+       return auth.accessProperty(l, "password", &auth.password)
 }
 
-func l_auth_language(l *lua.State) int {
+func l_auth_language(l *lua.LState) int {
        auth := getAuth(l)
+       return auth.accessProperty(l, "language", &auth.language)
+}
 
-       if l.IsString(2) {
-               if auth.client.state > csNew {
-                       panic("can't change language after connecting")
-               }
-               auth.language = lua.CheckString(l, 2)
-               return 0
-       } else {
-               l.PushString(auth.language)
-               return 1
-       }
+func l_auth_version(l *lua.LState) int {
+       auth := getAuth(l)
+       return auth.accessProperty(l, "version", &auth.version)
 }
 
-func l_auth_state(l *lua.State) int {
+func l_auth_state(l *lua.LState) int {
        auth := getAuth(l)
 
        switch auth.state {
        case asInit:
-               l.PushString("init")
+               l.Push(lua.LString("init"))
        case asRequested:
-               l.PushString("requested")
+               l.Push(lua.LString("requested"))
        case asVerified:
-               l.PushString("verified")
+               l.Push(lua.LString("verified"))
        case asActive:
-               l.PushString("active")
+               l.Push(lua.LString("active"))
        case asError:
-               l.PushString("error")
-               l.PushString(auth.err)
+               l.Push(lua.LString("error"))
+               l.Push(lua.LString(auth.err))
                return 2
        }
 
index 2fd926aef9ddef2fd9a4a1d501b690c7052eb393..95e1ebd3c8948bb17eada814426ea370a31043b6 100644 (file)
@@ -1,7 +1,7 @@
 --[[ builtin/vector.lua ]]--
 
 local function wrap(op, body_wrapper, ...)
-       return load("return function(a, b) " .. body_wrapper(op, ...) .. "end")()
+       return loadstring("return function(a, b) " .. body_wrapper(op, ...) .. "end")()
 end
 
 local function arith_mt(...)
diff --git a/callbacks.go b/callbacks.go
deleted file mode 100644 (file)
index e16aec7..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-package main
-
-import (
-       "github.com/Shopify/go-lua"
-       "github.com/anon55555/mt"
-)
-
-type Callbacks struct {
-       wildcard   bool
-       subscribed map[string]struct{}
-}
-
-func getCallbacks(l *lua.State) *Callbacks {
-       return lua.CheckUserData(l, 1, "hydra.callbacks").(*Callbacks)
-}
-
-func (handler *Callbacks) create(client *Client) {
-       handler.subscribed = map[string]struct{}{}
-}
-
-func (handler *Callbacks) push(l *lua.State) {
-       l.PushUserData(handler)
-
-       if lua.NewMetaTable(l, "hydra.callbacks") {
-               lua.NewLibrary(l, []lua.RegistryFunction{
-                       {Name: "wildcard", Function: l_callbacks_wildcard},
-                       {Name: "subscribe", Function: l_callbacks_subscribe},
-                       {Name: "unsubscribe", Function: l_callbacks_unsubscribe},
-               })
-               l.SetField(-2, "__index")
-       }
-       l.SetMetaTable(-2)
-}
-
-func (handler *Callbacks) canConnect() (bool, string) {
-       return true, ""
-}
-
-func (handler *Callbacks) connect() {
-}
-
-func (handler *Callbacks) handle(pkt *mt.Pkt, l *lua.State, idx int) {
-       if !handler.wildcard && pkt != nil {
-               if _, exists := handler.subscribed[pktToString(pkt)]; !exists {
-                       return
-               }
-       }
-
-       if !l.IsFunction(2) {
-               return
-       }
-
-       l.PushValue(2)      // callback
-       l.RawGetInt(1, idx) // arg 1: client
-       pktToLua(l, pkt)    // arg 2: pkt
-       l.Call(2, 0)
-}
-
-func l_callbacks_wildcard(l *lua.State) int {
-       handler := getCallbacks(l)
-       handler.wildcard = l.ToBoolean(2)
-       return 0
-}
-
-func l_callbacks_subscribe(l *lua.State) int {
-       handler := getCallbacks(l)
-
-       n := l.Top()
-       for i := 2; i <= n; i++ {
-               handler.subscribed[lua.CheckString(l, i)] = struct{}{}
-       }
-
-       return 0
-}
-
-func l_callbacks_unsubscribe(l *lua.State) int {
-       handler := getCallbacks(l)
-
-       n := l.Top()
-       for i := 2; i <= n; i++ {
-               delete(handler.subscribed, lua.CheckString(l, i))
-       }
-
-       return 0
-}
index a57e032afd3e1078636b72d03a26e8eceb2f0f45..a518c951d55395ee6e9c5ce854df757d893146f9 100644 (file)
--- a/client.go
+++ b/client.go
@@ -2,9 +2,11 @@ package main
 
 import (
        "errors"
-       "github.com/Shopify/go-lua"
        "github.com/anon55555/mt"
+       "github.com/dragonfireclient/hydra/tolua"
+       "github.com/yuin/gopher-lua"
        "net"
+       "sync"
 )
 
 type clientState uint8
@@ -15,141 +17,160 @@ const (
        csDisconnected
 )
 
-type Handler interface {
-       create(client *Client)
-       push(l *lua.State)
-       canConnect() (bool, string)
+type Component interface {
+       create(client *Client, l *lua.LState)
+       tolua() lua.LValue
        connect()
-       handle(pkt *mt.Pkt, l *lua.State, idx int)
+       process(pkt *mt.Pkt)
 }
 
 type Client struct {
-       address  string
-       state    clientState
-       handlers map[string]Handler
-       conn     mt.Peer
-       queue    chan *mt.Pkt
+       mu         sync.Mutex
+       address    string
+       state      clientState
+       conn       mt.Peer
+       queue      chan *mt.Pkt
+       wildcard   bool
+       subscribed map[string]struct{}
+       components map[string]Component
+       userdata   *lua.LUserData
 }
 
-func getClient(l *lua.State) *Client {
-       return lua.CheckUserData(l, 1, "hydra.client").(*Client)
+var clientFuncs = map[string]lua.LGFunction{
+       "address":     l_client_address,
+       "state":       l_client_state,
+       "connect":     l_client_connect,
+       "poll":        l_client_poll,
+       "disconnect":  l_client_disconnect,
+       "enable":      l_client_enable,
+       "subscribe":   l_client_subscribe,
+       "unsubscribe": l_client_unsubscribe,
+       "wildcard":    l_client_wildcard,
 }
 
-func l_client(l *lua.State) int {
-       client := &Client{
-               address:  lua.CheckString(l, 1),
-               state:    csNew,
-               handlers: map[string]Handler{},
-       }
+func getClient(l *lua.LState) *Client {
+       return l.CheckUserData(1).Value.(*Client)
+}
+
+func getClients(l *lua.LState) []*Client {
+       tbl := l.CheckTable(1)
+       n := tbl.MaxN()
 
-       l.PushUserData(client)
-
-       if lua.NewMetaTable(l, "hydra.client") {
-               lua.NewLibrary(l, []lua.RegistryFunction{
-                       {Name: "address", Function: l_client_address},
-                       {Name: "state", Function: l_client_state},
-                       {Name: "handler", Function: l_client_handler},
-                       {Name: "connect", Function: l_client_connect},
-                       {Name: "disconnect", Function: l_client_disconnect},
-               })
-               l.SetField(-2, "__index")
+       clients := make([]*Client, 0, n)
+       for i := 1; i <= n; i++ {
+               clients = append(clients, l.RawGetInt(tbl, i).(*lua.LUserData).Value.(*Client))
        }
-       l.SetMetaTable(-2)
 
-       return 1
+       return clients
 }
 
-func l_client_address(l *lua.State) int {
-       client := getClient(l)
-       l.PushString(client.address)
-       return 1
-}
+func getStrings(l *lua.LState) []string {
+       n := l.GetTop()
 
-func l_client_state(l *lua.State) int {
-       client := getClient(l)
-       switch client.state {
-       case csNew:
-               l.PushString("new")
-       case csConnected:
-               l.PushString("connected")
-       case csDisconnected:
-               l.PushString("disconnected")
+       strs := make([]string, 0, n-1)
+       for i := 2; i <= n; i++ {
+               strs = append(strs, l.CheckString(i))
        }
-       return 1
+
+       return strs
 }
 
-func l_client_handler(l *lua.State) int {
-       client := getClient(l)
-       name := lua.CheckString(l, 2)
+func (client *Client) disconnect() {
+       client.mu.Lock()
+       defer client.mu.Unlock()
 
-       handler, exists := client.handlers[name]
-       if !exists {
-               switch name {
-               case "callbacks":
-                       handler = &Callbacks{}
+       if client.state == csConnected {
+               client.conn.Close()
+       }
+}
 
-               case "auth":
-                       handler = &Auth{}
+func l_client(l *lua.LState) int {
+       client := &Client{}
 
-               default:
-                       return 0
-               }
+       client.address = l.CheckString(1)
+       client.state = csNew
+       client.wildcard = false
+       client.subscribed = map[string]struct{}{}
+       client.components = map[string]Component{}
+       client.userdata = l.NewUserData()
+       client.userdata.Value = client
+       l.SetMetatable(client.userdata, l.GetTypeMetatable("hydra.client"))
 
-               client.handlers[name] = handler
-               handler.create(client)
+       l.Push(client.userdata)
+       return 1
+}
+
+func l_client_index(l *lua.LState) int {
+       client := getClient(l)
+       key := l.CheckString(2)
+
+       if fun, exists := clientFuncs[key]; exists {
+               l.Push(l.NewFunction(fun))
+       } else if component, exists := client.components[key]; exists {
+               l.Push(component.tolua())
+       } else {
+               l.Push(lua.LNil)
        }
 
-       handler.push(l)
        return 1
 }
 
-func l_client_connect(l *lua.State) int {
+func l_client_address(l *lua.LState) int {
        client := getClient(l)
+       l.Push(lua.LString(client.address))
+       return 1
+}
 
-       if client.state != csNew {
-               l.PushBoolean(false)
-               l.PushString("invalid state")
-               return 2
+func l_client_state(l *lua.LState) int {
+       client := getClient(l)
+       switch client.state {
+       case csNew:
+               l.Push(lua.LString("new"))
+       case csConnected:
+               l.Push(lua.LString("connected"))
+       case csDisconnected:
+               l.Push(lua.LString("disconnected"))
        }
+       return 1
+}
 
-       for _, handler := range client.handlers {
-               ok, err := handler.canConnect()
+func l_client_connect(l *lua.LState) int {
+       client := getClient(l)
 
-               if !ok {
-                       l.PushBoolean(false)
-                       l.PushString(err)
-                       return 2
-               }
+       if client.state != csNew {
+               panic("can't reconnect")
        }
 
        addr, err := net.ResolveUDPAddr("udp", client.address)
        if err != nil {
-               l.PushBoolean(false)
-               l.PushString(err.Error())
-               return 2
+               panic(err)
        }
 
        conn, err := net.DialUDP("udp", nil, addr)
        if err != nil {
-               l.PushBoolean(false)
-               l.PushString(err.Error())
-               return 2
+               panic(err)
        }
 
        client.state = csConnected
        client.conn = mt.Connect(conn)
        client.queue = make(chan *mt.Pkt, 1024)
 
-       for _, handler := range client.handlers {
-               handler.connect()
-       }
-
        go func() {
                for {
                        pkt, err := client.conn.Recv()
 
                        if err == nil {
-                               client.queue <- &pkt
+                               client.mu.Lock()
+
+                               for _, component := range client.components {
+                                       component.process(&pkt)
+                               }
+
+                               if _, exists := client.subscribed[string(tolua.PktType(&pkt))]; exists || client.wildcard {
+                                       client.queue <- &pkt
+                               }
+
+                               client.mu.Unlock()
                        } else if errors.Is(err, net.ErrClosed) {
                                close(client.queue)
                                return
@@ -157,16 +178,96 @@ func l_client_connect(l *lua.State) int {
                }
        }()
 
-       l.PushBoolean(true)
-       return 1
+       client.mu.Lock()
+       for _, component := range client.components {
+               component.connect()
+       }
+       client.mu.Unlock()
+
+       return 0
+}
+
+func l_client_poll(l *lua.LState) int {
+       client := getClient(l)
+       _, pkt, timeout := doPoll(l, []*Client{client})
+
+       l.Push(tolua.Pkt(l, pkt))
+       l.Push(lua.LBool(timeout))
+       return 2
+}
+
+func l_client_disconnect(l *lua.LState) int {
+       client := getClient(l)
+       client.disconnect()
+       return 0
+}
+
+func l_client_enable(l *lua.LState) int {
+       client := getClient(l)
+       client.mu.Lock()
+       defer client.mu.Unlock()
+
+       for _, compname := range getStrings(l) {
+               if component, exists := client.components[compname]; !exists {
+                       switch compname {
+                       case "auth":
+                               component = &Auth{}
+                       default:
+                               panic("invalid component: " + compname)
+                       }
+
+                       client.components[compname] = component
+                       component.create(client, l)
+               }
+       }
+
+       return 0
+}
+
+func l_client_subscribe(l *lua.LState) int {
+       client := getClient(l)
+       client.mu.Lock()
+       defer client.mu.Unlock()
+
+       for _, pkt := range getStrings(l) {
+               client.subscribed[pkt] = struct{}{}
+       }
+
+       return 0
 }
 
-func l_client_disconnect(l *lua.State) int {
+func l_client_unsubscribe(l *lua.LState) int {
        client := getClient(l)
+       client.mu.Lock()
+       defer client.mu.Unlock()
+
+       for _, pkt := range getStrings(l) {
+               delete(client.subscribed, pkt)
+       }
+
+       return 0
+}
+
+func l_client_wildcard(l *lua.LState) int {
+       client := getClient(l)
+       client.wildcard = l.ToBool(2)
+       return 0
+}
+
+/*
+
+func l_client_send(l *lua.LState) int {
+       client := getClient(l)
+       pkt := fromlua.Pkt(l.CheckTable(2))
+
+       client.mu.Lock()
+       defer client.mu.Unlock()
 
        if client.state == csConnected {
-               client.conn.Close()
+               client.conn.Send(pkt)
        }
 
        return 0
 }
+
+*/
diff --git a/convert.go b/convert.go
deleted file mode 100644 (file)
index b53b6b9..0000000
+++ /dev/null
@@ -1,882 +0,0 @@
-// generated by mkconvert.lua, DO NOT EDIT
-package main
-
-import (
-       "github.com/Shopify/go-lua"
-       "github.com/anon55555/mt"
-)
-
-func luaPushAnimType(l *lua.State, val mt.AnimType) {
-       switch val {
-       case mt.NoAnim:
-               l.PushNil()
-       case mt.VerticalFrameAnim:
-               l.PushString("vertical_frame")
-       case mt.SpriteSheetAnim:
-               l.PushString("sprite_sheet")
-       }
-}
-
-func luaPushChatMsgType(l *lua.State, val mt.ChatMsgType) {
-       switch val {
-       case mt.RawMsg:
-               l.PushString("raw")
-       case mt.NormalMsg:
-               l.PushString("normal")
-       case mt.AnnounceMsg:
-               l.PushString("announce")
-       case mt.SysMsg:
-               l.PushString("sys")
-       }
-}
-
-func luaPushHotbarParam(l *lua.State, val mt.HotbarParam) {
-       switch val {
-       case mt.HotbarSize:
-               l.PushString("size")
-       case mt.HotbarImg:
-               l.PushString("img")
-       case mt.HotbarSelImg:
-               l.PushString("sel_img")
-       }
-}
-
-func luaPushHUDField(l *lua.State, val mt.HUDField) {
-       switch val {
-       case mt.HUDPos:
-               l.PushString("pos")
-       case mt.HUDName:
-               l.PushString("name")
-       case mt.HUDScale:
-               l.PushString("scale")
-       case mt.HUDText:
-               l.PushString("text")
-       case mt.HUDNumber:
-               l.PushString("number")
-       case mt.HUDItem:
-               l.PushString("item")
-       case mt.HUDDir:
-               l.PushString("dir")
-       case mt.HUDAlign:
-               l.PushString("align")
-       case mt.HUDOffset:
-               l.PushString("offset")
-       case mt.HUDWorldPos:
-               l.PushString("world_pos")
-       case mt.HUDSize:
-               l.PushString("size")
-       case mt.HUDZIndex:
-               l.PushString("z_index")
-       case mt.HUDText2:
-               l.PushString("text_2")
-       }
-}
-
-func luaPushHUDType(l *lua.State, val mt.HUDType) {
-       switch val {
-       case mt.ImgHUD:
-               l.PushString("img")
-       case mt.TextHUD:
-               l.PushString("text")
-       case mt.StatbarHUD:
-               l.PushString("statbar")
-       case mt.InvHUD:
-               l.PushString("inv")
-       case mt.WaypointHUD:
-               l.PushString("waypoint")
-       case mt.ImgWaypointHUD:
-               l.PushString("img_waypoint")
-       }
-}
-
-func luaPushKickReason(l *lua.State, val mt.KickReason) {
-       switch val {
-       case mt.WrongPasswd:
-               l.PushString("wrong_passwd")
-       case mt.UnexpectedData:
-               l.PushString("unexpected_data")
-       case mt.SrvIsSingleplayer:
-               l.PushString("srv_is_singleplayer")
-       case mt.UnsupportedVer:
-               l.PushString("unsupported_ver")
-       case mt.BadNameChars:
-               l.PushString("bad_name_chars")
-       case mt.BadName:
-               l.PushString("bad_name")
-       case mt.TooManyClts:
-               l.PushString("too_many_clts")
-       case mt.EmptyPasswd:
-               l.PushString("empty_passwd")
-       case mt.AlreadyConnected:
-               l.PushString("already_connected")
-       case mt.SrvErr:
-               l.PushString("srv_err")
-       case mt.Custom:
-               l.PushString("custom")
-       case mt.Shutdown:
-               l.PushString("shutdown")
-       case mt.Crash:
-               l.PushString("crash")
-       }
-}
-
-func luaPushModChanSig(l *lua.State, val mt.ModChanSig) {
-       switch val {
-       case mt.JoinOK:
-               l.PushString("join_ok")
-       case mt.JoinFail:
-               l.PushString("join_fail")
-       case mt.LeaveOK:
-               l.PushString("leave_ok")
-       case mt.LeaveFail:
-               l.PushString("leave_fail")
-       case mt.NotRegistered:
-               l.PushString("not_registered")
-       case mt.SetState:
-               l.PushString("set_state")
-       }
-}
-
-func luaPushPlayerListUpdateType(l *lua.State, val mt.PlayerListUpdateType) {
-       switch val {
-       case mt.InitPlayers:
-               l.PushString("init")
-       case mt.AddPlayers:
-               l.PushString("add")
-       case mt.RemovePlayers:
-               l.PushString("remove")
-       }
-}
-
-func luaPushSoundSrcType(l *lua.State, val mt.SoundSrcType) {
-       switch val {
-       case mt.NoSrc:
-               l.PushNil()
-       case mt.PosSrc:
-               l.PushString("pos")
-       case mt.AOSrc:
-               l.PushString("ao")
-       }
-}
-
-func luaPushAuthMethods(l *lua.State, val mt.AuthMethods) {
-       l.NewTable()
-       if val&mt.LegacyPasswd != 0 {
-               l.PushBoolean(true)
-               l.SetField(-2, "legacy_passwd")
-       }
-       if val&mt.SRP != 0 {
-               l.PushBoolean(true)
-               l.SetField(-2, "srp")
-       }
-       if val&mt.FirstSRP != 0 {
-               l.PushBoolean(true)
-               l.SetField(-2, "first_srp")
-       }
-}
-
-func luaPushCSMRestrictionFlags(l *lua.State, val mt.CSMRestrictionFlags) {
-       l.NewTable()
-       if val&mt.NoCSMs != 0 {
-               l.PushBoolean(true)
-               l.SetField(-2, "no_csms")
-       }
-       if val&mt.NoChatMsgs != 0 {
-               l.PushBoolean(true)
-               l.SetField(-2, "no_chat_msgs")
-       }
-       if val&mt.NoNodeDefs != 0 {
-               l.PushBoolean(true)
-               l.SetField(-2, "no_node_defs")
-       }
-       if val&mt.LimitMapRange != 0 {
-               l.PushBoolean(true)
-               l.SetField(-2, "limit_map_range")
-       }
-       if val&mt.NoPlayerList != 0 {
-               l.PushBoolean(true)
-               l.SetField(-2, "no_player_list")
-       }
-}
-
-func luaPushHUDFlags(l *lua.State, val mt.HUDFlags) {
-       l.NewTable()
-       if val&mt.ShowHotbar != 0 {
-               l.PushBoolean(true)
-               l.SetField(-2, "hotbar")
-       }
-       if val&mt.ShowHealthBar != 0 {
-               l.PushBoolean(true)
-               l.SetField(-2, "health_bar")
-       }
-       if val&mt.ShowCrosshair != 0 {
-               l.PushBoolean(true)
-               l.SetField(-2, "crosshair")
-       }
-       if val&mt.ShowWieldedItem != 0 {
-               l.PushBoolean(true)
-               l.SetField(-2, "wielded_item")
-       }
-       if val&mt.ShowBreathBar != 0 {
-               l.PushBoolean(true)
-               l.SetField(-2, "breath_bar")
-       }
-       if val&mt.ShowMinimap != 0 {
-               l.PushBoolean(true)
-               l.SetField(-2, "minimap")
-       }
-       if val&mt.ShowRadarMinimap != 0 {
-               l.PushBoolean(true)
-               l.SetField(-2, "radar_minimap")
-       }
-}
-
-func luaPushHUD(l *lua.State, val mt.HUD) {
-       l.NewTable()
-       luaPushVec2(l, [2]float64{float64(val.Align[0]), float64(val.Align[1])})
-       l.SetField(-2, "align")
-       l.PushInteger(int(val.Dir))
-       l.SetField(-2, "dir")
-       l.PushInteger(int(val.Item))
-       l.SetField(-2, "item")
-       l.PushString(string(val.Name))
-       l.SetField(-2, "name")
-       l.PushInteger(int(val.Number))
-       l.SetField(-2, "number")
-       luaPushVec2(l, [2]float64{float64(val.Offset[0]), float64(val.Offset[1])})
-       l.SetField(-2, "offset")
-       luaPushVec2(l, [2]float64{float64(val.Pos[0]), float64(val.Pos[1])})
-       l.SetField(-2, "pos")
-       luaPushVec2(l, [2]float64{float64(val.Scale[0]), float64(val.Scale[1])})
-       l.SetField(-2, "scale")
-       luaPushVec2(l, [2]float64{float64(val.Size[0]), float64(val.Size[1])})
-       l.SetField(-2, "size")
-       l.PushString(string(val.Text))
-       l.SetField(-2, "text")
-       l.PushString(string(val.Text2))
-       l.SetField(-2, "text_2")
-       luaPushHUDType(l, val.Type)
-       l.SetField(-2, "type")
-       luaPushVec3(l, [3]float64{float64(val.WorldPos[0]), float64(val.WorldPos[1]), float64(val.WorldPos[2])})
-       l.SetField(-2, "world_pos")
-       l.PushInteger(int(val.ZIndex))
-       l.SetField(-2, "z_index")
-}
-
-func luaPushNode(l *lua.State, val mt.Node) {
-       l.NewTable()
-       l.PushInteger(int(val.Param0))
-       l.SetField(-2, "param0")
-       l.PushInteger(int(val.Param1))
-       l.SetField(-2, "param1")
-       l.PushInteger(int(val.Param2))
-       l.SetField(-2, "param2")
-}
-
-func luaPushTileAnim(l *lua.State, val mt.TileAnim) {
-       l.NewTable()
-       luaPushVec2(l, [2]float64{float64(val.AspectRatio[0]), float64(val.AspectRatio[1])})
-       l.SetField(-2, "aspect_ratio")
-       l.PushNumber(float64(val.Duration))
-       l.SetField(-2, "duration")
-       luaPushVec2(l, [2]float64{float64(val.NFrames[0]), float64(val.NFrames[1])})
-       l.SetField(-2, "n_frames")
-       luaPushAnimType(l, val.Type)
-       l.SetField(-2, "type")
-}
-
-func pktToString(pkt *mt.Pkt) string {
-       switch pkt.Cmd.(type) {
-       case *mt.ToCltAcceptAuth:
-               return "accept_auth"
-       case *mt.ToCltAcceptSudoMode:
-               return "accept_sudo_mode"
-       case *mt.ToCltAddHUD:
-               return "add_hud"
-       case *mt.ToCltAddNode:
-               return "add_node"
-       case *mt.ToCltAddParticleSpawner:
-               return "add_particle_spawner"
-       case *mt.ToCltAddPlayerVel:
-               return "add_player_vel"
-       case *mt.ToCltAnnounceMedia:
-               return "announce_media"
-       case *mt.ToCltAOMsgs:
-               return "ao_msgs"
-       case *mt.ToCltAORmAdd:
-               return "ao_rm_add"
-       case *mt.ToCltBlkData:
-               return "blk_data"
-       case *mt.ToCltBreath:
-               return "breath"
-       case *mt.ToCltChangeHUD:
-               return "change_hud"
-       case *mt.ToCltChatMsg:
-               return "chat_msg"
-       case *mt.ToCltCloudParams:
-               return "cloud_params"
-       case *mt.ToCltCSMRestrictionFlags:
-               return "csm_restriction_flags"
-       case *mt.ToCltDeathScreen:
-               return "death_screen"
-       case *mt.ToCltDelParticleSpawner:
-               return "del_particle_spawner"
-       case *mt.ToCltDenySudoMode:
-               return "deny_sudo_mode"
-       case *mt.ToCltDetachedInv:
-               return "detached_inv"
-       case *mt.ToCltDisco:
-               return "disco"
-       case *mt.ToCltEyeOffset:
-               return "eye_offset"
-       case *mt.ToCltFadeSound:
-               return "fade_sound"
-       case *mt.ToCltFormspecPrepend:
-               return "formspec_prepend"
-       case *mt.ToCltFOV:
-               return "fov"
-       case *mt.ToCltHello:
-               return "hello"
-       case *mt.ToCltHP:
-               return "hp"
-       case *mt.ToCltHUDFlags:
-               return "hud_flags"
-       case *mt.ToCltInv:
-               return "inv"
-       case *mt.ToCltInvFormspec:
-               return "inv_formspec"
-       case *mt.ToCltItemDefs:
-               return "item_defs"
-       case *mt.ToCltKick:
-               return "kick"
-       case *mt.ToCltLegacyKick:
-               return "legacy_kick"
-       case *mt.ToCltLocalPlayerAnim:
-               return "local_player_anim"
-       case *mt.ToCltMedia:
-               return "media"
-       case *mt.ToCltMediaPush:
-               return "media_push"
-       case *mt.ToCltMinimapModes:
-               return "minimap_modes"
-       case *mt.ToCltModChanMsg:
-               return "mod_chan_msg"
-       case *mt.ToCltModChanSig:
-               return "mod_chan_sig"
-       case *mt.ToCltMoonParams:
-               return "moon_params"
-       case *mt.ToCltMovePlayer:
-               return "move_player"
-       case *mt.ToCltMovement:
-               return "movement"
-       case *mt.ToCltNodeDefs:
-               return "node_defs"
-       case *mt.ToCltNodeMetasChanged:
-               return "node_metas_changed"
-       case *mt.ToCltOverrideDayNightRatio:
-               return "override_day_night_ratio"
-       case *mt.ToCltPlaySound:
-               return "play_sound"
-       case *mt.ToCltPrivs:
-               return "privs"
-       case *mt.ToCltRemoveNode:
-               return "remove_node"
-       case *mt.ToCltRmHUD:
-               return "rm_hud"
-       case *mt.ToCltSetHotbarParam:
-               return "set_hotbar_param"
-       case *mt.ToCltShowFormspec:
-               return "show_formspec"
-       case *mt.ToCltSkyParams:
-               return "sky_params"
-       case *mt.ToCltSpawnParticle:
-               return "spawn_particle"
-       case *mt.ToCltSRPBytesSaltB:
-               return "srp_bytes_salt_b"
-       case *mt.ToCltStarParams:
-               return "star_params"
-       case *mt.ToCltStopSound:
-               return "stop_sound"
-       case *mt.ToCltSunParams:
-               return "sun_params"
-       case *mt.ToCltTimeOfDay:
-               return "time_of_day"
-       case *mt.ToCltUpdatePlayerList:
-               return "update_player_list"
-       }
-       panic("impossible")
-       return ""
-}
-
-func pktToLua(l *lua.State, pkt *mt.Pkt) {
-       if pkt == nil {
-               l.PushNil()
-               return
-       }
-       l.NewTable()
-       l.PushString(pktToString(pkt))
-       l.SetField(-2, "_type")
-       switch val := pkt.Cmd.(type) {
-       case *mt.ToCltAcceptAuth:
-               l.PushNumber(float64(val.MapSeed))
-               l.SetField(-2, "map_seed")
-               luaPushVec3(l, [3]float64{float64(val.PlayerPos[0]), float64(val.PlayerPos[1]), float64(val.PlayerPos[2])})
-               l.SetField(-2, "player_pos")
-               l.PushNumber(float64(val.SendInterval))
-               l.SetField(-2, "send_interval")
-               luaPushAuthMethods(l, val.SudoAuthMethods)
-               l.SetField(-2, "sudo_auth_methods")
-       case *mt.ToCltAddHUD:
-               luaPushHUD(l, val.HUD)
-               l.SetField(-2, "hud")
-               l.PushInteger(int(val.ID))
-               l.SetField(-2, "id")
-       case *mt.ToCltAddNode:
-               l.PushBoolean(bool(val.KeepMeta))
-               l.SetField(-2, "keep_meta")
-               luaPushNode(l, val.Node)
-               l.SetField(-2, "node")
-               luaPushVec3(l, [3]float64{float64(val.Pos[0]), float64(val.Pos[1]), float64(val.Pos[2])})
-               l.SetField(-2, "pos")
-       case *mt.ToCltAddParticleSpawner:
-               luaPushBox3(l, [2][3]float64{{float64(val.Acc[0][0]), float64(val.Acc[0][1]), float64(val.Acc[0][2])}, {float64(val.Acc[1][0]), float64(val.Acc[1][1]), float64(val.Acc[1][2])}})
-               l.SetField(-2, "acc")
-               l.PushInteger(int(val.Amount))
-               l.SetField(-2, "amount")
-               luaPushTileAnim(l, val.AnimParams)
-               l.SetField(-2, "anim_params")
-               l.PushBoolean(bool(val.AOCollision))
-               l.SetField(-2, "ao_collision")
-               l.PushBoolean(bool(val.Collide))
-               l.SetField(-2, "collide")
-               l.PushBoolean(bool(val.CollisionRm))
-               l.SetField(-2, "collision_rm")
-               l.PushNumber(float64(val.Duration))
-               l.SetField(-2, "duration")
-               luaPushBox1(l, [2]float64{float64(val.ExpirationTime[0]), float64(val.ExpirationTime[1])})
-               l.SetField(-2, "expiration_time")
-               l.PushInteger(int(val.Glow))
-               l.SetField(-2, "glow")
-               l.PushInteger(int(val.ID))
-               l.SetField(-2, "id")
-               l.PushInteger(int(val.NodeParam0))
-               l.SetField(-2, "node_param0")
-               l.PushInteger(int(val.NodeParam2))
-               l.SetField(-2, "node_param2")
-               l.PushInteger(int(val.NodeTile))
-               l.SetField(-2, "node_tile")
-               luaPushBox3(l, [2][3]float64{{float64(val.Pos[0][0]), float64(val.Pos[0][1]), float64(val.Pos[0][2])}, {float64(val.Pos[1][0]), float64(val.Pos[1][1]), float64(val.Pos[1][2])}})
-               l.SetField(-2, "pos")
-               luaPushBox1(l, [2]float64{float64(val.Size[0]), float64(val.Size[1])})
-               l.SetField(-2, "size")
-               l.PushString(string(val.Texture))
-               l.SetField(-2, "texture")
-               luaPushBox3(l, [2][3]float64{{float64(val.Vel[0][0]), float64(val.Vel[0][1]), float64(val.Vel[0][2])}, {float64(val.Vel[1][0]), float64(val.Vel[1][1]), float64(val.Vel[1][2])}})
-               l.SetField(-2, "vel")
-               l.PushBoolean(bool(val.Vertical))
-               l.SetField(-2, "vertical")
-       case *mt.ToCltAddPlayerVel:
-               luaPushVec3(l, [3]float64{float64(val.Vel[0]), float64(val.Vel[1]), float64(val.Vel[2])})
-               l.SetField(-2, "vel")
-       case *mt.ToCltBlkData:
-               luaPushVec3(l, [3]float64{float64(val.Blkpos[0]), float64(val.Blkpos[1]), float64(val.Blkpos[2])})
-               l.SetField(-2, "blkpos")
-       case *mt.ToCltBreath:
-               l.PushInteger(int(val.Breath))
-               l.SetField(-2, "breath")
-       case *mt.ToCltChangeHUD:
-               if val.Field == mt.HUDAlign {
-                       luaPushVec2(l, [2]float64{float64(val.Align[0]), float64(val.Align[1])})
-                       l.SetField(-2, "align")
-               }
-               if val.Field == mt.HUDDir {
-                       l.PushInteger(int(val.Dir))
-                       l.SetField(-2, "dir")
-               }
-               luaPushHUDField(l, val.Field)
-               l.SetField(-2, "field")
-               l.PushInteger(int(val.ID))
-               l.SetField(-2, "id")
-               if val.Field == mt.HUDItem {
-                       l.PushInteger(int(val.Item))
-                       l.SetField(-2, "item")
-               }
-               if val.Field == mt.HUDName {
-                       l.PushString(string(val.Name))
-                       l.SetField(-2, "name")
-               }
-               if val.Field == mt.HUDNumber {
-                       l.PushInteger(int(val.Number))
-                       l.SetField(-2, "number")
-               }
-               if val.Field == mt.HUDOffset {
-                       luaPushVec2(l, [2]float64{float64(val.Offset[0]), float64(val.Offset[1])})
-                       l.SetField(-2, "offset")
-               }
-               if val.Field == mt.HUDPos {
-                       luaPushVec2(l, [2]float64{float64(val.Pos[0]), float64(val.Pos[1])})
-                       l.SetField(-2, "pos")
-               }
-               if val.Field == mt.HUDSize {
-                       luaPushVec2(l, [2]float64{float64(val.Size[0]), float64(val.Size[1])})
-                       l.SetField(-2, "size")
-               }
-               if val.Field == mt.HUDText {
-                       l.PushString(string(val.Text))
-                       l.SetField(-2, "text")
-               }
-               if val.Field == mt.HUDText2 {
-                       l.PushString(string(val.Text2))
-                       l.SetField(-2, "text_2")
-               }
-               if val.Field == mt.HUDWorldPos {
-                       luaPushVec3(l, [3]float64{float64(val.WorldPos[0]), float64(val.WorldPos[1]), float64(val.WorldPos[2])})
-                       l.SetField(-2, "world_pos")
-               }
-               if val.Field == mt.HUDZIndex {
-                       l.PushInteger(int(val.ZIndex))
-                       l.SetField(-2, "z_index")
-               }
-       case *mt.ToCltChatMsg:
-               l.PushString(string(val.Sender))
-               l.SetField(-2, "sender")
-               l.PushString(string(val.Text))
-               l.SetField(-2, "text")
-               l.PushNumber(float64(val.Timestamp))
-               l.SetField(-2, "timestamp")
-               luaPushChatMsgType(l, val.Type)
-               l.SetField(-2, "type")
-       case *mt.ToCltCloudParams:
-               luaPushColor(l, val.AmbientColor)
-               l.SetField(-2, "ambient_color")
-               l.PushNumber(float64(val.Density))
-               l.SetField(-2, "density")
-               luaPushColor(l, val.DiffuseColor)
-               l.SetField(-2, "diffuse_color")
-               l.PushNumber(float64(val.Height))
-               l.SetField(-2, "height")
-               luaPushVec2(l, [2]float64{float64(val.Speed[0]), float64(val.Speed[1])})
-               l.SetField(-2, "speed")
-               l.PushNumber(float64(val.Thickness))
-               l.SetField(-2, "thickness")
-       case *mt.ToCltCSMRestrictionFlags:
-               luaPushCSMRestrictionFlags(l, val.Flags)
-               l.SetField(-2, "flags")
-               l.PushInteger(int(val.MapRange))
-               l.SetField(-2, "map_range")
-       case *mt.ToCltDeathScreen:
-               luaPushVec3(l, [3]float64{float64(val.PointAt[0]), float64(val.PointAt[1]), float64(val.PointAt[2])})
-               l.SetField(-2, "point_at")
-               l.PushBoolean(bool(val.PointCam))
-               l.SetField(-2, "point_cam")
-       case *mt.ToCltDelParticleSpawner:
-               l.PushInteger(int(val.ID))
-               l.SetField(-2, "id")
-       case *mt.ToCltDetachedInv:
-               l.PushString(string(val.Inv))
-               l.SetField(-2, "inv")
-               l.PushBoolean(bool(val.Keep))
-               l.SetField(-2, "keep")
-               l.PushInteger(int(val.Len))
-               l.SetField(-2, "len")
-               l.PushString(string(val.Name))
-               l.SetField(-2, "name")
-       case *mt.ToCltEyeOffset:
-               luaPushVec3(l, [3]float64{float64(val.First[0]), float64(val.First[1]), float64(val.First[2])})
-               l.SetField(-2, "first")
-               luaPushVec3(l, [3]float64{float64(val.Third[0]), float64(val.Third[1]), float64(val.Third[2])})
-               l.SetField(-2, "third")
-       case *mt.ToCltFadeSound:
-               l.PushNumber(float64(val.Gain))
-               l.SetField(-2, "gain")
-               l.PushInteger(int(val.ID))
-               l.SetField(-2, "id")
-               l.PushNumber(float64(val.Step))
-               l.SetField(-2, "step")
-       case *mt.ToCltFormspecPrepend:
-               l.PushString(string(val.Prepend))
-               l.SetField(-2, "prepend")
-       case *mt.ToCltFOV:
-               l.PushNumber(float64(val.FOV))
-               l.SetField(-2, "fov")
-               l.PushBoolean(bool(val.Multiplier))
-               l.SetField(-2, "multiplier")
-               l.PushNumber(float64(val.TransitionTime))
-               l.SetField(-2, "transition_time")
-       case *mt.ToCltHello:
-               luaPushAuthMethods(l, val.AuthMethods)
-               l.SetField(-2, "auth_methods")
-               l.PushInteger(int(val.Compression))
-               l.SetField(-2, "compression")
-               l.PushInteger(int(val.ProtoVer))
-               l.SetField(-2, "proto_ver")
-               l.PushInteger(int(val.SerializeVer))
-               l.SetField(-2, "serialize_ver")
-               l.PushString(string(val.Username))
-               l.SetField(-2, "username")
-       case *mt.ToCltHP:
-               l.PushInteger(int(val.HP))
-               l.SetField(-2, "hp")
-       case *mt.ToCltHUDFlags:
-               luaPushHUDFlags(l, val.Flags)
-               l.SetField(-2, "flags")
-               luaPushHUDFlags(l, val.Mask)
-               l.SetField(-2, "mask")
-       case *mt.ToCltInv:
-               l.PushString(string(val.Inv))
-               l.SetField(-2, "inv")
-       case *mt.ToCltInvFormspec:
-               l.PushString(string(val.Formspec))
-               l.SetField(-2, "formspec")
-       case *mt.ToCltKick:
-               if dr := val.Reason; dr == mt.Custom || dr == mt.Shutdown || dr == mt.Crash {
-                       l.PushString(string(val.Custom))
-                       l.SetField(-2, "custom")
-               }
-               luaPushKickReason(l, val.Reason)
-               l.SetField(-2, "reason")
-               if dr := val.Reason; dr == mt.Shutdown || dr == mt.Crash {
-                       l.PushBoolean(bool(val.Reconnect))
-                       l.SetField(-2, "reconnect")
-               }
-       case *mt.ToCltLegacyKick:
-               l.PushString(string(val.Reason))
-               l.SetField(-2, "reason")
-       case *mt.ToCltLocalPlayerAnim:
-               luaPushBox1(l, [2]float64{float64(val.Dig[0]), float64(val.Dig[1])})
-               l.SetField(-2, "dig")
-               luaPushBox1(l, [2]float64{float64(val.Idle[0]), float64(val.Idle[1])})
-               l.SetField(-2, "idle")
-               l.PushNumber(float64(val.Speed))
-               l.SetField(-2, "speed")
-               luaPushBox1(l, [2]float64{float64(val.Walk[0]), float64(val.Walk[1])})
-               l.SetField(-2, "walk")
-               luaPushBox1(l, [2]float64{float64(val.WalkDig[0]), float64(val.WalkDig[1])})
-               l.SetField(-2, "walk_dig")
-       case *mt.ToCltMediaPush:
-               l.PushString(string(val.Data))
-               l.SetField(-2, "data")
-               l.PushString(string(val.Filename))
-               l.SetField(-2, "filename")
-               l.PushString(string(val.SHA1[:]))
-               l.SetField(-2, "sha1")
-               l.PushBoolean(bool(val.ShouldCache))
-               l.SetField(-2, "should_cache")
-       case *mt.ToCltModChanMsg:
-               l.PushString(string(val.Channel))
-               l.SetField(-2, "channel")
-               l.PushString(string(val.Msg))
-               l.SetField(-2, "msg")
-               l.PushString(string(val.Sender))
-               l.SetField(-2, "sender")
-       case *mt.ToCltModChanSig:
-               l.PushString(string(val.Channel))
-               l.SetField(-2, "channel")
-               luaPushModChanSig(l, val.Signal)
-               l.SetField(-2, "signal")
-       case *mt.ToCltMoonParams:
-               l.PushNumber(float64(val.Size))
-               l.SetField(-2, "size")
-               l.PushString(string(val.Texture))
-               l.SetField(-2, "texture")
-               l.PushString(string(val.ToneMap))
-               l.SetField(-2, "tone_map")
-               l.PushBoolean(bool(val.Visible))
-               l.SetField(-2, "visible")
-       case *mt.ToCltMovePlayer:
-               l.PushNumber(float64(val.Pitch))
-               l.SetField(-2, "pitch")
-               luaPushVec3(l, [3]float64{float64(val.Pos[0]), float64(val.Pos[1]), float64(val.Pos[2])})
-               l.SetField(-2, "pos")
-               l.PushNumber(float64(val.Yaw))
-               l.SetField(-2, "yaw")
-       case *mt.ToCltMovement:
-               l.PushNumber(float64(val.AirAccel))
-               l.SetField(-2, "air_accel")
-               l.PushNumber(float64(val.ClimbSpeed))
-               l.SetField(-2, "climb_speed")
-               l.PushNumber(float64(val.CrouchSpeed))
-               l.SetField(-2, "crouch_speed")
-               l.PushNumber(float64(val.DefaultAccel))
-               l.SetField(-2, "default_accel")
-               l.PushNumber(float64(val.FastAccel))
-               l.SetField(-2, "fast_accel")
-               l.PushNumber(float64(val.FastSpeed))
-               l.SetField(-2, "fast_speed")
-               l.PushNumber(float64(val.Fluidity))
-               l.SetField(-2, "fluidity")
-               l.PushNumber(float64(val.Gravity))
-               l.SetField(-2, "gravity")
-               l.PushNumber(float64(val.JumpSpeed))
-               l.SetField(-2, "jump_speed")
-               l.PushNumber(float64(val.Sink))
-               l.SetField(-2, "sink")
-               l.PushNumber(float64(val.Smoothing))
-               l.SetField(-2, "smoothing")
-               l.PushNumber(float64(val.WalkSpeed))
-               l.SetField(-2, "walk_speed")
-       case *mt.ToCltOverrideDayNightRatio:
-               l.PushBoolean(bool(val.Override))
-               l.SetField(-2, "override")
-               l.PushInteger(int(val.Ratio))
-               l.SetField(-2, "ratio")
-       case *mt.ToCltPlaySound:
-               l.PushBoolean(bool(val.Ephemeral))
-               l.SetField(-2, "ephemeral")
-               l.PushNumber(float64(val.Fade))
-               l.SetField(-2, "fade")
-               l.PushNumber(float64(val.Gain))
-               l.SetField(-2, "gain")
-               l.PushInteger(int(val.ID))
-               l.SetField(-2, "id")
-               l.PushBoolean(bool(val.Loop))
-               l.SetField(-2, "loop")
-               l.PushString(string(val.Name))
-               l.SetField(-2, "name")
-               l.PushNumber(float64(val.Pitch))
-               l.SetField(-2, "pitch")
-               luaPushVec3(l, [3]float64{float64(val.Pos[0]), float64(val.Pos[1]), float64(val.Pos[2])})
-               l.SetField(-2, "pos")
-               l.PushInteger(int(val.SrcAOID))
-               l.SetField(-2, "src_aoid")
-               luaPushSoundSrcType(l, val.SrcType)
-               l.SetField(-2, "src_type")
-       case *mt.ToCltPrivs:
-               luaPushStringSet(l, val.Privs)
-               l.SetField(-2, "privs")
-       case *mt.ToCltRemoveNode:
-               luaPushVec3(l, [3]float64{float64(val.Pos[0]), float64(val.Pos[1]), float64(val.Pos[2])})
-               l.SetField(-2, "pos")
-       case *mt.ToCltRmHUD:
-               l.PushInteger(int(val.ID))
-               l.SetField(-2, "id")
-       case *mt.ToCltSetHotbarParam:
-               l.PushString(string(val.Img))
-               l.SetField(-2, "img")
-               luaPushHotbarParam(l, val.Param)
-               l.SetField(-2, "param")
-               l.PushInteger(int(val.Size))
-               l.SetField(-2, "size")
-       case *mt.ToCltShowFormspec:
-               l.PushString(string(val.Formname))
-               l.SetField(-2, "formname")
-               l.PushString(string(val.Formspec))
-               l.SetField(-2, "formspec")
-       case *mt.ToCltSkyParams:
-               luaPushColor(l, val.BgColor)
-               l.SetField(-2, "bg_color")
-               l.PushBoolean(bool(val.Clouds))
-               l.SetField(-2, "clouds")
-               if val.Type == "regular" {
-                       luaPushColor(l, val.DawnHorizon)
-                       l.SetField(-2, "dawn_horizon")
-               }
-               if val.Type == "regular" {
-                       luaPushColor(l, val.DawnSky)
-                       l.SetField(-2, "dawn_sky")
-               }
-               if val.Type == "regular" {
-                       luaPushColor(l, val.DayHorizon)
-                       l.SetField(-2, "day_horizon")
-               }
-               if val.Type == "regular" {
-                       luaPushColor(l, val.DaySky)
-                       l.SetField(-2, "day_sky")
-               }
-               l.PushString(string(val.FogTintType))
-               l.SetField(-2, "fog_tint_type")
-               if val.Type == "regular" {
-                       luaPushColor(l, val.Indoor)
-                       l.SetField(-2, "indoor")
-               }
-               luaPushColor(l, val.MoonFogTint)
-               l.SetField(-2, "moon_fog_tint")
-               if val.Type == "regular" {
-                       luaPushColor(l, val.NightHorizon)
-                       l.SetField(-2, "night_horizon")
-               }
-               if val.Type == "regular" {
-                       luaPushColor(l, val.NightSky)
-                       l.SetField(-2, "night_sky")
-               }
-               luaPushColor(l, val.SunFogTint)
-               l.SetField(-2, "sun_fog_tint")
-               if val.Type == "skybox" {
-                       luaPushTextureList(l, val.Textures)
-                       l.SetField(-2, "textures")
-               }
-               l.PushString(string(val.Type))
-               l.SetField(-2, "type")
-       case *mt.ToCltSpawnParticle:
-               luaPushVec3(l, [3]float64{float64(val.Acc[0]), float64(val.Acc[1]), float64(val.Acc[2])})
-               l.SetField(-2, "acc")
-               luaPushTileAnim(l, val.AnimParams)
-               l.SetField(-2, "anim_params")
-               l.PushBoolean(bool(val.AOCollision))
-               l.SetField(-2, "ao_collision")
-               l.PushBoolean(bool(val.Collide))
-               l.SetField(-2, "collide")
-               l.PushBoolean(bool(val.CollisionRm))
-               l.SetField(-2, "collision_rm")
-               l.PushNumber(float64(val.ExpirationTime))
-               l.SetField(-2, "expiration_time")
-               l.PushInteger(int(val.Glow))
-               l.SetField(-2, "glow")
-               l.PushInteger(int(val.NodeParam0))
-               l.SetField(-2, "node_param0")
-               l.PushInteger(int(val.NodeParam2))
-               l.SetField(-2, "node_param2")
-               l.PushInteger(int(val.NodeTile))
-               l.SetField(-2, "node_tile")
-               luaPushVec3(l, [3]float64{float64(val.Pos[0]), float64(val.Pos[1]), float64(val.Pos[2])})
-               l.SetField(-2, "pos")
-               l.PushNumber(float64(val.Size))
-               l.SetField(-2, "size")
-               l.PushString(string(val.Texture))
-               l.SetField(-2, "texture")
-               luaPushVec3(l, [3]float64{float64(val.Vel[0]), float64(val.Vel[1]), float64(val.Vel[2])})
-               l.SetField(-2, "vel")
-               l.PushBoolean(bool(val.Vertical))
-               l.SetField(-2, "vertical")
-       case *mt.ToCltSRPBytesSaltB:
-               l.PushString(string(val.B))
-               l.SetField(-2, "b")
-               l.PushString(string(val.Salt))
-               l.SetField(-2, "salt")
-       case *mt.ToCltStarParams:
-               luaPushColor(l, val.Color)
-               l.SetField(-2, "color")
-               l.PushInteger(int(val.Count))
-               l.SetField(-2, "count")
-               l.PushNumber(float64(val.Size))
-               l.SetField(-2, "size")
-               l.PushBoolean(bool(val.Visible))
-               l.SetField(-2, "visible")
-       case *mt.ToCltStopSound:
-               l.PushInteger(int(val.ID))
-               l.SetField(-2, "id")
-       case *mt.ToCltSunParams:
-               l.PushString(string(val.Rise))
-               l.SetField(-2, "rise")
-               l.PushBoolean(bool(val.Rising))
-               l.SetField(-2, "rising")
-               l.PushNumber(float64(val.Size))
-               l.SetField(-2, "size")
-               l.PushString(string(val.Texture))
-               l.SetField(-2, "texture")
-               l.PushString(string(val.ToneMap))
-               l.SetField(-2, "tone_map")
-               l.PushBoolean(bool(val.Visible))
-               l.SetField(-2, "visible")
-       case *mt.ToCltTimeOfDay:
-               l.PushNumber(float64(val.Speed))
-               l.SetField(-2, "speed")
-               l.PushInteger(int(val.Time))
-               l.SetField(-2, "time")
-       case *mt.ToCltUpdatePlayerList:
-               luaPushStringList(l, val.Players)
-               l.SetField(-2, "players")
-               luaPushPlayerListUpdateType(l, val.Type)
-               l.SetField(-2, "type")
-       }
-}
diff --git a/example/dump-traffic.lua b/example/dump-traffic.lua
new file mode 100755 (executable)
index 0000000..b9f30a3
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/env hydra
+local address, name, password = unpack(arg)
+local client = hydra.client(address)
+
+client:enable("auth")
+client.auth:username(name)
+client.auth:password(password or "")
+
+client:wildcard(true)
+client:connect()
+
+while not hydra.canceled() do
+       local pkt, interrupt = client:poll()
+
+       if pkt then
+               print(pkt._type)
+               for k, v in pairs(pkt) do
+                       if k ~= "_type" then
+                               print("", k, v)
+                       end
+               end
+       elseif not interrupt then
+               print("disconnected")
+               break   
+       end
+end
+
+client:disconnect()
diff --git a/go.mod b/go.mod
index 9a3d9b0493c66754a990a84dde75891f59669d96..cd45843e36ff7f758dbf4b46bb33d095fa127eeb 100644 (file)
--- a/go.mod
+++ b/go.mod
@@ -1,10 +1,9 @@
 module github.com/dragonfireclient/hydra
 
-go 1.17
+go 1.18
 
 require (
-       github.com/HimbeerserverDE/srp v0.0.0 // indirect
-       github.com/Shopify/go-lua v0.0.0-20220120202609-9ab779377807 // indirect
-       github.com/anon55555/mt v0.0.0-20210919124550-bcc58cb3048f // indirect
-       github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 // indirect
+       github.com/HimbeerserverDE/srp v0.0.0
+       github.com/anon55555/mt v0.0.0-20210919124550-bcc58cb3048f
+       github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64
 )
diff --git a/go.sum b/go.sum
index 42ec0ba78dbda68264d75644f48ed2c754daa1d2..0717da66457d11732e5d6994708d66a0b3877b7a 100644 (file)
--- a/go.sum
+++ b/go.sum
@@ -1,7 +1,5 @@
 github.com/HimbeerserverDE/srp v0.0.0 h1:Iy2GIF7DJphXXO9NjncLEBO6VsZd8Yhrlxl/qTr09eE=
 github.com/HimbeerserverDE/srp v0.0.0/go.mod h1:pxNH8S2nh4n2DWE0ToX5GnnDr/uEAuaAhJsCpkDLIWw=
-github.com/Shopify/go-lua v0.0.0-20220120202609-9ab779377807 h1:b10jUZ94GuJk5GBl0iElM5aGIPPHi7FTRvqOKA7Ku+s=
-github.com/Shopify/go-lua v0.0.0-20220120202609-9ab779377807/go.mod h1:1cxA/QL5xgRGP7Crq6tXSOY4eo//me8GHGMyypHynM8=
 github.com/anon55555/mt v0.0.0-20210919124550-bcc58cb3048f h1:tZU8VPYLyRrG3Lj9zBZvTVF5tUGciC/2aUIgTcU4WaM=
 github.com/anon55555/mt v0.0.0-20210919124550-bcc58cb3048f/go.mod h1:jH4ER+ahjl7H6TczzK+q4V9sXY++U2Geh6/vt3r4Xvs=
 github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 h1:5mLPGnFdSsevFRFc9q3yYbBkB6tsm4aCwwQV/j1JQAQ=
index 77d7e055e67c02081da943ca034795378c29aa1d..e3d2427d7e9ece09c06101f479c37950b73c19a8 100644 (file)
--- a/hydra.go
+++ b/hydra.go
@@ -2,7 +2,8 @@ package main
 
 import (
        _ "embed"
-       "github.com/Shopify/go-lua"
+       "github.com/dragonfireclient/hydra/tolua"
+       "github.com/yuin/gopher-lua"
        "os"
        "os/signal"
        "syscall"
@@ -15,15 +16,12 @@ var canceled = false
 //go:embed builtin/vector.lua
 var vectorLibrary string
 
-func l_dtime(l *lua.State) int {
-       l.PushNumber(time.Since(lastTime).Seconds())
-       lastTime = time.Now()
-       return 1
-}
-
-func l_canceled(l *lua.State) int {
-       l.PushBoolean(canceled)
-       return 1
+var hydraFuncs = map[string]lua.LGFunction{
+       "client":     l_client,
+       "dtime":      l_dtime,
+       "canceled":   l_canceled,
+       "poll":       l_poll,
+       "disconnect": l_disconnect,
 }
 
 func signalChannel() chan os.Signal {
@@ -32,6 +30,36 @@ func signalChannel() chan os.Signal {
        return sig
 }
 
+func l_dtime(l *lua.LState) int {
+       l.Push(lua.LNumber(time.Since(lastTime).Seconds()))
+       return 1
+}
+
+func l_canceled(l *lua.LState) int {
+       l.Push(lua.LBool(canceled))
+       return 1
+}
+
+func l_poll(l *lua.LState) int {
+       client, pkt, timeout := doPoll(l, getClients(l))
+       if client == nil {
+               l.Push(lua.LNil)
+       } else {
+               l.Push(client.userdata)
+       }
+       l.Push(tolua.Pkt(l, pkt))
+       l.Push(lua.LBool(timeout))
+       return 3
+}
+
+func l_disconnect(l *lua.LState) int {
+       for _, client := range getClients(l) {
+               client.disconnect()
+       }
+
+       return 0
+}
+
 func main() {
        if len(os.Args) < 2 {
                panic("missing filename")
@@ -42,33 +70,27 @@ func main() {
                canceled = true
        }()
 
-       l := lua.NewState()
-       lua.OpenLibraries(l)
-
-       lua.NewLibrary(l, []lua.RegistryFunction{
-               {Name: "client", Function: l_client},
-               {Name: "dtime", Function: l_dtime},
-               {Name: "canceled", Function: l_canceled},
-               {Name: "poll", Function: l_poll},
-       })
+       l := lua.NewState(lua.Options{IncludeGoStackTrace: true})
+       defer l.Close()
 
-       l.PushNumber(10.0)
-       l.SetField(-2, "BS")
+       arg := l.NewTable()
+       for i, a := range os.Args {
+               l.RawSetInt(arg, i-1, lua.LString(a))
+       }
+       l.SetGlobal("arg", arg)
 
-       l.SetGlobal("hydra")
+       hydra := l.SetFuncs(l.NewTable(), hydraFuncs)
+       l.SetField(hydra, "BS", lua.LNumber(10.0))
+       l.SetGlobal("hydra", hydra)
 
-       l.NewTable()
-       for i, arg := range os.Args {
-               l.PushString(arg)
-               l.RawSetInt(-2, i - 1)
-       }
-       l.SetGlobal("arg")
+       l.SetField(l.NewTypeMetatable("hydra.auth"), "__index", l.SetFuncs(l.NewTable(), authFuncs))
+       l.SetField(l.NewTypeMetatable("hydra.client"), "__index", l.NewFunction(l_client_index))
 
-       if err := lua.DoString(l, vectorLibrary); err != nil {
+       if err := l.DoString(vectorLibrary); err != nil {
                panic(err)
        }
 
-       if err := lua.DoFile(l, os.Args[1]); err != nil {
+       if err := l.DoFile(os.Args[1]); err != nil {
                panic(err)
        }
 }
diff --git a/mkconvert.lua b/mkconvert.lua
deleted file mode 100755 (executable)
index 1141991..0000000
+++ /dev/null
@@ -1,234 +0,0 @@
-#!/usr/bin/env lua
-local function snext(t, state)
-       local key
-
-       if state == nil then
-               t.__sorted = {}
-               for k in pairs(t) do
-                       if k ~= "__sorted" then
-                               table.insert(t.__sorted, k)
-                       end
-               end
-               table.sort(t.__sorted)
-               
-               key = t.__sorted[1]
-       else
-               for i, v in ipairs(t.__sorted) do
-                       if v == state then
-                               key = t.__sorted[i + 1]
-                               break
-                       end
-               end
-       end
-
-       if key then
-               return key, t[key]
-       end
-
-       t.__sorted = nil
-end
-
-local function spairs(t)
-       return snext, t, nil
-end
-
-local function parse_pair(pair, value_first)
-       if pair:sub(1, 1) == "#" then
-               return
-       end
-
-       local idx = pair:find(" ")
-
-       if idx then
-               local first, second = pair:sub(1, idx - 1), pair:sub(idx + 1)
-
-               if value_first and first:sub(1, 1) ~= "[" then
-                       return second, first
-               else
-                       return first, second
-               end
-       else
-               return pair
-       end
-end
-
-local function parse_spec(name, value_first)
-       local f = io.open("spec/" .. name, "r")
-       local spec = {}
-       local top
-
-       for l in f:lines() do
-               if l:sub(1, 1) == "\t" then
-                       local key, val = parse_pair(l:sub(2), value_first)
-
-                       if val then
-                               top[key] = val
-                       elseif key then
-                               table.insert(top, key)
-                       end
-               else
-                       local key, val = parse_pair(l, value_first)
-
-                       if val then
-                               spec[key] = val
-                       elseif key then
-                               top = {}
-                               spec[key] = top
-                       end
-               end
-       end
-
-       f:close()
-       return spec
-end
-
-local casemap = parse_spec("casemap")
-
-local function camel_case(snake)
-       if casemap[snake] then
-               return casemap[snake]
-       end
-
-       local camel = ""
-
-       while #snake > 0 do
-               local idx = snake:find("_") or #snake + 1
-
-               camel = camel
-                       .. snake:sub(1, 1):upper()
-                       .. snake:sub(2, idx - 1)
-
-               snake = snake:sub(idx + 1)
-       end
-
-       return camel
-end
-
-local funcs = ""
-
-for name, fields in spairs(parse_spec("client/enum")) do
-       local camel = camel_case(name)
-       funcs = funcs .. "func luaPush" .. camel .. "(l *lua.State, val mt." .. camel .. ") {\n\tswitch val {\n"
-
-       for _, var in ipairs(fields) do
-               funcs = funcs .. "\tcase mt."
-                       .. (fields.prefix or "") .. camel_case(var) .. (fields.postfix or "")
-                       .. ":\n\t\t" .. (var == "no" and "l.PushNil()" or "l.PushString(\"" .. var .. "\")") .. "\n"
-       end
-
-       funcs = funcs .. "\t}\n}\n\n"
-end
-
-for name, fields in spairs(parse_spec("client/flag")) do
-       local camel = camel_case(name)
-       funcs = funcs .. "func luaPush" .. camel .. "(l *lua.State, val mt." .. camel .. ") {\n\tl.NewTable()\n"
-
-       for _, var in ipairs(fields)    do
-               funcs = funcs .. "\tif val&mt."
-                       .. (fields.prefix or "") .. camel_case(var) .. (fields.postfix or "")
-                       .. " != 0 {\n\t\tl.PushBoolean(true)\n\t\tl.SetField(-2, \"" .. var  .. "\")\n\t}\n"
-       end
-
-       funcs = funcs .. "}\n\n"
-end
-
-local push_type = {
-       string = "l.PushString(string(VAL))",
-       fixed_string = "l.PushString(string(VAL[:]))",
-       boolean = "l.PushBoolean(bool(VAL))",
-       integer = "l.PushInteger(int(VAL))",
-       number = "l.PushNumber(float64(VAL))",
-       vec2 = "luaPushVec2(l, [2]float64{float64(VAL[0]), float64(VAL[1])})",
-       vec3 = "luaPushVec3(l, [3]float64{float64(VAL[0]), float64(VAL[1]), float64(VAL[2])})",
-       box1 = "luaPushBox1(l, [2]float64{float64(VAL[0]), float64(VAL[1])})",
-       box2 = "luaPushBox2(l, [2][2]float64{{float64(VAL[0][0]), float64(VAL[0][1])}, {float64(VAL[1][0]), float64(VAL[1][1])}})",
-       box3 = "luaPushBox3(l, [2][3]float64{{float64(VAL[0][0]), float64(VAL[0][1]), float64(VAL[0][2])}, {float64(VAL[1][0]), float64(VAL[1][1]), float64(VAL[1][2])}})",
-}
-
-local function push_fields(fields, indent)
-       local impl = ""
-       
-       for name, type in spairs(fields) do
-               if name:sub(1, 1) ~= "[" then
-                       local camel = "val." .. camel_case(name)
-
-                       local idt = indent
-                       local condition = fields["[" .. name .. "]"]
-
-                       if condition then
-                               impl = impl .. indent .. "if " .. condition .. " {\n" 
-                               idt = idt .. "\t"
-                       end
-
-                       if push_type[type] then
-                               impl = impl .. idt .. push_type[type]:gsub("VAL", camel) .. "\n"
-                       else
-                               impl = impl .. idt .. "luaPush" .. camel_case(type) .. "(l, " .. camel .. ")\n"
-                       end
-
-                       impl = impl .. idt .. "l.SetField(-2, \"" .. name .. "\")\n"
-
-                       if condition then
-                               impl = impl .. indent .. "}\n"
-                       end
-               end
-       end
-
-       return impl
-end
-
-for name, fields in spairs(parse_spec("client/struct", true)) do
-       local camel = camel_case(name)
-       funcs = funcs
-               .. "func luaPush" .. camel .. "(l *lua.State, val mt." .. camel .. ") {\n\tl.NewTable()\n"
-               .. push_fields(fields, "\t")
-               .. "}\n\n"
-end
-
-local to_string_impl = ""
-local to_lua_impl = ""
-
-for name, fields in spairs(parse_spec("client/pkt", true)) do
-       local case = "\tcase *mt.ToClt" .. camel_case(name) .. ":\n"
-
-       to_string_impl = to_string_impl
-               .. case .. "\t\treturn \"" .. name .. "\"\n"
-
-       if next(fields) then
-               to_lua_impl = to_lua_impl .. case .. push_fields(fields, "\t\t")
-       end
-end
-
-local f = io.open("convert.go", "w")
-f:write([[
-// generated by mkconvert.lua, DO NOT EDIT
-package main
-
-import (
-       "github.com/Shopify/go-lua"
-       "github.com/anon55555/mt"
-)
-
-]] .. funcs .. [[
-func pktToString(pkt *mt.Pkt) string {
-       switch pkt.Cmd.(type) {
-]] .. to_string_impl .. [[
-       }
-       panic("impossible")
-       return ""
-}
-
-func pktToLua(l *lua.State, pkt *mt.Pkt) {
-       if pkt == nil {
-               l.PushNil()
-               return
-       }
-       l.NewTable()
-       l.PushString(pktToString(pkt))
-       l.SetField(-2, "_type")
-       switch val := pkt.Cmd.(type) {
-]] .. to_lua_impl .. [[
-       }
-}
-]])
-f:close()
diff --git a/parse_spec.lua b/parse_spec.lua
new file mode 100644 (file)
index 0000000..449360c
--- /dev/null
@@ -0,0 +1,105 @@
+local function snext(t, state)
+       local key
+
+       if state == nil then
+               t.__sorted = {}
+               for k in pairs(t) do
+                       if k ~= "__sorted" then
+                               table.insert(t.__sorted, k)
+                       end
+               end
+               table.sort(t.__sorted)
+               
+               key = t.__sorted[1]
+       else
+               for i, v in ipairs(t.__sorted) do
+                       if v == state then
+                               key = t.__sorted[i + 1]
+                               break
+                       end
+               end
+       end
+
+       if key then
+               return key, t[key]
+       end
+
+       t.__sorted = nil
+end
+
+function spairs(t)
+       return snext, t, nil
+end
+
+local function parse_pair(pair, value_first)
+       if pair:sub(1, 1) == "#" then
+               return
+       end
+
+       local idx = pair:find(" ")
+
+       if idx then
+               local first, second = pair:sub(1, idx - 1), pair:sub(idx + 1)
+
+               if value_first and first:sub(1, 1) ~= "[" then
+                       return second, first
+               else
+                       return first, second
+               end
+       else
+               return pair
+       end
+end
+
+function parse_spec(name, value_first)
+       local f = io.open("../spec/" .. name, "r")
+       local spec = {}
+       local top
+
+       for l in f:lines() do
+               if l:sub(1, 1) == "\t" then
+                       local key, val = parse_pair(l:sub(2), value_first)
+
+                       if val then
+                               top[key] = val
+                       elseif key then
+                               table.insert(top, key)
+                       end
+               else
+                       local key, val = parse_pair(l, value_first)
+
+                       if val then
+                               spec[key] = val
+                       elseif key then
+                               top = {}
+                               spec[key] = top
+                       end
+               end
+       end
+
+       f:close()
+       return spec
+end
+
+local casemap = parse_spec("casemap")
+
+function camel_case(snake)
+       if casemap[snake] then
+               return casemap[snake]
+       end
+
+       local camel = ""
+
+       while #snake > 0 do
+               local idx = snake:find("_") or #snake + 1
+
+               camel = camel
+                       .. snake:sub(1, 1):upper()
+                       .. snake:sub(2, idx - 1)
+
+               snake = snake:sub(idx + 1)
+       end
+
+       return camel
+end
+
diff --git a/poll.go b/poll.go
index bfbe298ed6d6dcaeeaff326c82bf0bd1c0a029c8..db00ef9561d487de29d1448f50d9b8180408d96b 100644 (file)
--- a/poll.go
+++ b/poll.go
@@ -1,89 +1,64 @@
 package main
 
 import (
-       "github.com/Shopify/go-lua"
        "github.com/anon55555/mt"
+       "github.com/yuin/gopher-lua"
        "reflect"
        "time"
 )
 
-func l_poll(l *lua.State) int {
-       clients := make([]*Client, 0)
-
-       lua.CheckType(l, 1, lua.TypeTable)
-       i := 1
-       for {
-               l.RawGetInt(1, i)
-               if l.IsNil(-1) {
-                       l.Pop(1)
-                       break
-               }
-
-               clients = append(clients, l.ToUserData(-1).(*Client))
-               i++
-       }
-
+func doPoll(l *lua.LState, clients []*Client) (*Client, *mt.Pkt, bool) {
        var timeout time.Duration
        hasTimeout := false
-       if l.IsNumber(3) {
-               timeout = time.Duration(lua.CheckNumber(l, 3) * float64(time.Second))
+       if l.GetTop() > 1 {
+               timeout = time.Duration(float64(l.ToNumber(2)) * float64(time.Second))
                hasTimeout = true
        }
 
-       for {
-               cases := make([]reflect.SelectCase, 0, len(clients)+2)
+       cases := make([]reflect.SelectCase, 0, len(clients)+2)
+       for _, client := range clients {
+               if client.state != csConnected {
+                       continue
+               }
 
-               for _, client := range clients {
-                       if client.state != csConnected {
-                               continue
-                       }
+               cases = append(cases, reflect.SelectCase{
+                       Dir:  reflect.SelectRecv,
+                       Chan: reflect.ValueOf(client.queue),
+               })
+       }
 
-                       cases = append(cases, reflect.SelectCase{
-                               Dir:  reflect.SelectRecv,
-                               Chan: reflect.ValueOf(client.queue),
-                       })
-               }
+       offset := len(cases)
 
-               offset := len(cases)
+       if offset < 1 {
+               return nil, nil, false
+       }
 
-               if offset < 1 {
-                       l.PushBoolean(false)
-                       return 1
-               }
+       cases = append(cases, reflect.SelectCase{
+               Dir:  reflect.SelectRecv,
+               Chan: reflect.ValueOf(signalChannel()),
+       })
 
+       if hasTimeout {
                cases = append(cases, reflect.SelectCase{
                        Dir:  reflect.SelectRecv,
-                       Chan: reflect.ValueOf(signalChannel()),
+                       Chan: reflect.ValueOf(time.After(timeout)),
                })
+       }
 
-               if hasTimeout {
-                       cases = append(cases, reflect.SelectCase{
-                               Dir:  reflect.SelectRecv,
-                               Chan: reflect.ValueOf(time.After(timeout)),
-                       })
-               }
-
-               idx, value, ok := reflect.Select(cases)
-
-               if idx >= offset {
-                       l.PushBoolean(true)
-                       return 1
-               }
+       idx, value, ok := reflect.Select(cases)
 
-               client := clients[idx]
+       if idx >= offset {
+               return nil, nil, true
+       }
 
-               var pkt *mt.Pkt = nil
-               if ok {
-                       pkt = value.Interface().(*mt.Pkt)
-               } else {
-                       client.state = csDisconnected
-               }
+       client := clients[idx]
 
-               for _, handler := range client.handlers {
-                       handler.handle(pkt, l, idx+1)
-               }
+       var pkt *mt.Pkt = nil
+       if ok {
+               pkt = value.Interface().(*mt.Pkt)
+       } else {
+               client.state = csDisconnected
        }
 
-       panic("impossible")
-       return 0
+       return client, pkt, false
 }
index bc041270da6f3f9dea941e538bc8fa10c5a6523e..e7c609bd9b3ba30c901d7c9b62bfb2d18bbb90a2 100644 (file)
@@ -1,7 +1,7 @@
 hello
-       integer serialize_ver
-       integer compression
-       integer proto_ver
+       number serialize_ver
+       number compression
+       number proto_ver
        auth_methods auth_methods
        string username
 accept_auth
@@ -14,9 +14,9 @@ accept_sudo_mode
 deny_sudo_mode
 kick
        kick_reason reason
-       [custom] dr := val.Reason; dr == mt.Custom || dr == mt.Shutdown || dr == mt.Crash
+       [custom] val.Reason == mt.Custom || val.Reason == mt.Shutdown || val.Reason == mt.Crash
        string custom
-       [reconnect] dr := val.Reason; dr == mt.Shutdown || dr == mt.Crash
+       [reconnect] val.Reason == mt.Shutdown || val.Reason == mt.Crash
        boolean reconnect
 blk_data
        vec3 blkpos
@@ -30,11 +30,11 @@ remove_node
 inv
        string inv
 time_of_day
-       integer time
+       number time
        number speed
 csm_restriction_flags
        csm_restriction_flags flags
-       integer map_range
+       number map_range
 add_player_vel
        vec3 vel
 media_push
@@ -53,7 +53,7 @@ ao_rm_add
 ao_msgs
        # TODO
 hp
-       integer hp
+       number hp
 move_player
        vec3 pos
        number pitch
@@ -76,18 +76,18 @@ announce_media
 item_defs
        # TODO
 play_sound
-       integer id
+       number id
        string name
        number gain
        sound_src_type src_type
        vec3 pos
-       integer src_aoid
+       number src_aoid
        boolean loop
        number fade
        number pitch
        boolean ephemeral
 stop_sound
-       integer id
+       number id
 privs
        string_set privs
 inv_formspec
@@ -95,7 +95,7 @@ inv_formspec
 detached_inv
        string name
        boolean keep
-       integer len
+       number len
        string inv
 show_formspec
        string formspec
@@ -124,13 +124,13 @@ spawn_particle
        boolean vertical
        boolean collision_rm
        tile_anim anim_params
-       integer glow
+       number glow
        boolean ao_collision
-       integer node_param0
-       integer node_param2
-       integer node_tile
+       number node_param0
+       number node_param2
+       number node_tile
 add_particle_spawner
-       integer amount
+       number amount
        number duration
        box3 pos
        box3 vel
@@ -139,22 +139,22 @@ add_particle_spawner
        box1 size
        boolean collide
        string texture
-       integer id
+       number id
        boolean vertical
        boolean collision_rm
        tile_anim anim_params
-       integer glow
+       number glow
        boolean ao_collision
-       integer node_param0
-       integer node_param2
-       integer node_tile
+       number node_param0
+       number node_param2
+       number node_tile
 add_hud
-       integer id
+       number id
        hud hud
 rm_hud
-       integer id
+       number id
 change_hud
-       integer id
+       number id
        hud_field field
        [pos] val.Field == mt.HUDPos
        [name] val.Field == mt.HUDName
@@ -171,24 +171,24 @@ change_hud
        vec2 pos
        string name
        string text
-       integer number
-       integer item
-       integer dir
+       number number
+       number item
+       number dir
        vec2 align
        vec2 offset
        vec3 world_pos
        vec2 size
-       integer z_index
+       number z_index
        string text_2
 hud_flags
        hud_flags flags
        hud_flags mask
 set_hotbar_param
        hotbar_param param
-       integer size
+       number size
        string img
 breath
-       integer breath
+       number breath
 sky_params
        color bg_color
        string type
@@ -214,7 +214,7 @@ sky_params
        color indoor
 override_day_night_ratio
        boolean override
-       integer ratio
+       number ratio
 local_player_anim
        box1 idle
        box1 walk
@@ -225,7 +225,7 @@ eye_offset
        vec3 first
        vec3 third
 del_particle_spawner
-       integer id
+       number id
 cloud_params
        number density
        color diffuse_color
@@ -234,7 +234,7 @@ cloud_params
        number thickness
        vec2 speed
 fade_sound
-       integer id
+       number id
        number step
        number gain
 update_player_list
@@ -263,7 +263,7 @@ moon_params
        number size
 star_params
        boolean visible
-       integer count
+       number count
        color color
        number size
 srp_bytes_salt_b
index 1fa149cd6b98466522cb0ae05f73c1b93d069ed3..d18189ca384d0d49890f74d6e844a631f0e68562 100644 (file)
@@ -1,7 +1,7 @@
 node
-       integer param0
-       integer param1
-       integer param2
+       number param0
+       number param1
+       number param2
 tile_anim
        anim_type type
        vec2 aspect_ratio
@@ -13,12 +13,12 @@ hud
        string name
        vec2 scale
        string text
-       integer number
-       integer item
-       integer dir
+       number number
+       number item
+       number dir
        vec2 align
        vec2 offset
        vec3 world_pos
        vec2 size
-       integer z_index
+       number z_index
        string text_2
diff --git a/tolua/generate.lua b/tolua/generate.lua
new file mode 100755 (executable)
index 0000000..7c2d5a7
--- /dev/null
@@ -0,0 +1,129 @@
+#!/usr/bin/env lua
+dofile("../parse_spec.lua")
+
+local funcs = ""
+
+for name, fields in spairs(parse_spec("client/enum")) do
+       local camel = camel_case(name)
+       funcs = funcs .. "func " .. camel .. "(l *lua.LState, val mt." .. camel .. ") lua.LValue {\n\tswitch val {\n"
+
+       for _, var in ipairs(fields) do
+               funcs = funcs .. "\tcase mt."
+                       .. (fields.prefix or "") .. camel_case(var) .. (fields.postfix or "")
+                       .. ":\n\t\t" .. (var == "no" and "return lua.LNil" or "return lua.LString(\"" .. var .. "\")") .. "\n"
+       end
+
+       funcs = funcs .. "\t}\n\tpanic(\"impossible\")\n\treturn lua.LNil\n}\n\n"
+end
+
+for name, fields in spairs(parse_spec("client/flag")) do
+       local camel = camel_case(name)
+       funcs = funcs .. "func " .. camel .. "(l *lua.LState, val mt." .. camel .. ") lua.LValue {\n\ttbl := l.NewTable()\n"
+
+       for _, var in ipairs(fields) do
+               funcs = funcs .. "\tif val&mt."
+                       .. (fields.prefix or "") .. camel_case(var) .. (fields.postfix or "")
+                       .. " != 0 {\n\t\tl.SetField(tbl, \"" .. var  .. "\", lua.LTrue)\n\t}\n"
+       end
+
+       funcs = funcs .. "\treturn tbl\n}\n\n"
+end
+
+local to_lua = {
+       string = "lua.LString(string(VAL))",
+       fixed_string = "lua.LString(string(VAL[:]))",
+       boolean = "lua.LBool(VAL)",
+       number = "lua.LNumber(VAL)",
+       vec2 = "Vec2(l, [2]lua.LNumber{lua.LNumber(VAL[0]), lua.LNumber(VAL[1])})",
+       vec3 = "Vec3(l, [3]lua.LNumber{lua.LNumber(VAL[0]), lua.LNumber(VAL[1]), lua.LNumber(VAL[2])})",
+       box1 = "Box1(l, [2]lua.LNumber{lua.LNumber(VAL[0]), lua.LNumber(VAL[1])})",
+       box2 = "Box2(l, [2][2]lua.LNumber{{lua.LNumber(VAL[0][0]), lua.LNumber(VAL[0][1])}, {lua.LNumber(VAL[1][0]), lua.LNumber(VAL[1][1])}})",
+       box3 = "Box3(l, [2][3]lua.LNumber{{lua.LNumber(VAL[0][0]), lua.LNumber(VAL[0][1]), lua.LNumber(VAL[0][2])}, {lua.LNumber(VAL[1][0]), lua.LNumber(VAL[1][1]), lua.LNumber(VAL[1][2])}})",
+}
+
+local function fields_to_lua(fields, indent)
+       local impl = ""
+       
+       for name, type in spairs(fields) do
+               if name:sub(1, 1) ~= "[" then
+                       local camel = "val." .. camel_case(name)
+
+                       local idt = indent
+                       local condition = fields["[" .. name .. "]"]
+
+                       if condition then
+                               impl = impl .. indent .. "if " .. condition .. " {\n" 
+                               idt = idt .. "\t"
+                       end
+
+                       impl = impl .. idt .. "l.SetField(tbl, \"" .. name .. "\", "
+                       if to_lua[type] then
+                               impl = impl .. to_lua[type]:gsub("VAL", camel)
+                       else
+                               impl = impl .. camel_case(type) .. "(l, " .. camel .. ")"
+                       end
+                       impl = impl .. ")\n"
+
+                       if condition then
+                               impl = impl .. indent .. "}\n"
+                       end
+               end
+       end
+
+       return impl
+end
+
+for name, fields in spairs(parse_spec("client/struct", true)) do
+       local camel = camel_case(name)
+       funcs = funcs
+               .. "func " .. camel .. "(l *lua.LState, val mt." .. camel .. ") lua.LValue {\n\ttbl := l.NewTable()\n"
+               .. fields_to_lua(fields, "\t")
+               .. "\treturn tbl\n}\n\n"
+end
+
+local to_string_impl = ""
+local to_lua_impl = ""
+
+for name, fields in spairs(parse_spec("client/pkt", true)) do
+       local case = "\tcase *mt.ToClt" .. camel_case(name) .. ":\n"
+
+       to_string_impl = to_string_impl
+               .. case .. "\t\treturn lua.LString(\"" .. name .. "\")\n"
+
+       if next(fields) then
+               to_lua_impl = to_lua_impl .. case .. fields_to_lua(fields, "\t\t")
+       end
+end
+
+local f = io.open("generated.go", "w")
+f:write([[
+// generated by generate.lua, DO NOT EDIT
+package tolua
+
+import (
+       "github.com/anon55555/mt"
+       "github.com/yuin/gopher-lua"
+)
+
+]] .. funcs .. [[
+func PktType(pkt *mt.Pkt) lua.LString {
+       switch pkt.Cmd.(type) {
+]] .. to_string_impl .. [[
+       }
+       panic("impossible")
+       return ""
+}
+
+func Pkt(l *lua.LState, pkt *mt.Pkt) lua.LValue {
+       if pkt == nil {
+               return lua.LNil
+       }
+       tbl := l.NewTable()
+       l.SetField(tbl, "_type", PktType(pkt))
+       switch val := pkt.Cmd.(type) {
+]] .. to_lua_impl .. [[
+       }
+       return tbl
+}
+]])
+f:close()
diff --git a/tolua/generated.go b/tolua/generated.go
new file mode 100644 (file)
index 0000000..ec53231
--- /dev/null
@@ -0,0 +1,684 @@
+// generated by generate.lua, DO NOT EDIT
+package tolua
+
+import (
+       "github.com/anon55555/mt"
+       "github.com/yuin/gopher-lua"
+)
+
+func AnimType(l *lua.LState, val mt.AnimType) lua.LValue {
+       switch val {
+       case mt.NoAnim:
+               return lua.LNil
+       case mt.VerticalFrameAnim:
+               return lua.LString("vertical_frame")
+       case mt.SpriteSheetAnim:
+               return lua.LString("sprite_sheet")
+       }
+       panic("impossible")
+       return lua.LNil
+}
+
+func ChatMsgType(l *lua.LState, val mt.ChatMsgType) lua.LValue {
+       switch val {
+       case mt.RawMsg:
+               return lua.LString("raw")
+       case mt.NormalMsg:
+               return lua.LString("normal")
+       case mt.AnnounceMsg:
+               return lua.LString("announce")
+       case mt.SysMsg:
+               return lua.LString("sys")
+       }
+       panic("impossible")
+       return lua.LNil
+}
+
+func HotbarParam(l *lua.LState, val mt.HotbarParam) lua.LValue {
+       switch val {
+       case mt.HotbarSize:
+               return lua.LString("size")
+       case mt.HotbarImg:
+               return lua.LString("img")
+       case mt.HotbarSelImg:
+               return lua.LString("sel_img")
+       }
+       panic("impossible")
+       return lua.LNil
+}
+
+func HUDField(l *lua.LState, val mt.HUDField) lua.LValue {
+       switch val {
+       case mt.HUDPos:
+               return lua.LString("pos")
+       case mt.HUDName:
+               return lua.LString("name")
+       case mt.HUDScale:
+               return lua.LString("scale")
+       case mt.HUDText:
+               return lua.LString("text")
+       case mt.HUDNumber:
+               return lua.LString("number")
+       case mt.HUDItem:
+               return lua.LString("item")
+       case mt.HUDDir:
+               return lua.LString("dir")
+       case mt.HUDAlign:
+               return lua.LString("align")
+       case mt.HUDOffset:
+               return lua.LString("offset")
+       case mt.HUDWorldPos:
+               return lua.LString("world_pos")
+       case mt.HUDSize:
+               return lua.LString("size")
+       case mt.HUDZIndex:
+               return lua.LString("z_index")
+       case mt.HUDText2:
+               return lua.LString("text_2")
+       }
+       panic("impossible")
+       return lua.LNil
+}
+
+func HUDType(l *lua.LState, val mt.HUDType) lua.LValue {
+       switch val {
+       case mt.ImgHUD:
+               return lua.LString("img")
+       case mt.TextHUD:
+               return lua.LString("text")
+       case mt.StatbarHUD:
+               return lua.LString("statbar")
+       case mt.InvHUD:
+               return lua.LString("inv")
+       case mt.WaypointHUD:
+               return lua.LString("waypoint")
+       case mt.ImgWaypointHUD:
+               return lua.LString("img_waypoint")
+       }
+       panic("impossible")
+       return lua.LNil
+}
+
+func KickReason(l *lua.LState, val mt.KickReason) lua.LValue {
+       switch val {
+       case mt.WrongPasswd:
+               return lua.LString("wrong_passwd")
+       case mt.UnexpectedData:
+               return lua.LString("unexpected_data")
+       case mt.SrvIsSingleplayer:
+               return lua.LString("srv_is_singleplayer")
+       case mt.UnsupportedVer:
+               return lua.LString("unsupported_ver")
+       case mt.BadNameChars:
+               return lua.LString("bad_name_chars")
+       case mt.BadName:
+               return lua.LString("bad_name")
+       case mt.TooManyClts:
+               return lua.LString("too_many_clts")
+       case mt.EmptyPasswd:
+               return lua.LString("empty_passwd")
+       case mt.AlreadyConnected:
+               return lua.LString("already_connected")
+       case mt.SrvErr:
+               return lua.LString("srv_err")
+       case mt.Custom:
+               return lua.LString("custom")
+       case mt.Shutdown:
+               return lua.LString("shutdown")
+       case mt.Crash:
+               return lua.LString("crash")
+       }
+       panic("impossible")
+       return lua.LNil
+}
+
+func ModChanSig(l *lua.LState, val mt.ModChanSig) lua.LValue {
+       switch val {
+       case mt.JoinOK:
+               return lua.LString("join_ok")
+       case mt.JoinFail:
+               return lua.LString("join_fail")
+       case mt.LeaveOK:
+               return lua.LString("leave_ok")
+       case mt.LeaveFail:
+               return lua.LString("leave_fail")
+       case mt.NotRegistered:
+               return lua.LString("not_registered")
+       case mt.SetState:
+               return lua.LString("set_state")
+       }
+       panic("impossible")
+       return lua.LNil
+}
+
+func PlayerListUpdateType(l *lua.LState, val mt.PlayerListUpdateType) lua.LValue {
+       switch val {
+       case mt.InitPlayers:
+               return lua.LString("init")
+       case mt.AddPlayers:
+               return lua.LString("add")
+       case mt.RemovePlayers:
+               return lua.LString("remove")
+       }
+       panic("impossible")
+       return lua.LNil
+}
+
+func SoundSrcType(l *lua.LState, val mt.SoundSrcType) lua.LValue {
+       switch val {
+       case mt.NoSrc:
+               return lua.LNil
+       case mt.PosSrc:
+               return lua.LString("pos")
+       case mt.AOSrc:
+               return lua.LString("ao")
+       }
+       panic("impossible")
+       return lua.LNil
+}
+
+func AuthMethods(l *lua.LState, val mt.AuthMethods) lua.LValue {
+       tbl := l.NewTable()
+       if val&mt.LegacyPasswd != 0 {
+               l.SetField(tbl, "legacy_passwd", lua.LTrue)
+       }
+       if val&mt.SRP != 0 {
+               l.SetField(tbl, "srp", lua.LTrue)
+       }
+       if val&mt.FirstSRP != 0 {
+               l.SetField(tbl, "first_srp", lua.LTrue)
+       }
+       return tbl
+}
+
+func CSMRestrictionFlags(l *lua.LState, val mt.CSMRestrictionFlags) lua.LValue {
+       tbl := l.NewTable()
+       if val&mt.NoCSMs != 0 {
+               l.SetField(tbl, "no_csms", lua.LTrue)
+       }
+       if val&mt.NoChatMsgs != 0 {
+               l.SetField(tbl, "no_chat_msgs", lua.LTrue)
+       }
+       if val&mt.NoNodeDefs != 0 {
+               l.SetField(tbl, "no_node_defs", lua.LTrue)
+       }
+       if val&mt.LimitMapRange != 0 {
+               l.SetField(tbl, "limit_map_range", lua.LTrue)
+       }
+       if val&mt.NoPlayerList != 0 {
+               l.SetField(tbl, "no_player_list", lua.LTrue)
+       }
+       return tbl
+}
+
+func HUDFlags(l *lua.LState, val mt.HUDFlags) lua.LValue {
+       tbl := l.NewTable()
+       if val&mt.ShowHotbar != 0 {
+               l.SetField(tbl, "hotbar", lua.LTrue)
+       }
+       if val&mt.ShowHealthBar != 0 {
+               l.SetField(tbl, "health_bar", lua.LTrue)
+       }
+       if val&mt.ShowCrosshair != 0 {
+               l.SetField(tbl, "crosshair", lua.LTrue)
+       }
+       if val&mt.ShowWieldedItem != 0 {
+               l.SetField(tbl, "wielded_item", lua.LTrue)
+       }
+       if val&mt.ShowBreathBar != 0 {
+               l.SetField(tbl, "breath_bar", lua.LTrue)
+       }
+       if val&mt.ShowMinimap != 0 {
+               l.SetField(tbl, "minimap", lua.LTrue)
+       }
+       if val&mt.ShowRadarMinimap != 0 {
+               l.SetField(tbl, "radar_minimap", lua.LTrue)
+       }
+       return tbl
+}
+
+func HUD(l *lua.LState, val mt.HUD) lua.LValue {
+       tbl := l.NewTable()
+       l.SetField(tbl, "align", Vec2(l, [2]lua.LNumber{lua.LNumber(val.Align[0]), lua.LNumber(val.Align[1])}))
+       l.SetField(tbl, "dir", lua.LNumber(val.Dir))
+       l.SetField(tbl, "item", lua.LNumber(val.Item))
+       l.SetField(tbl, "name", lua.LString(string(val.Name)))
+       l.SetField(tbl, "number", lua.LNumber(val.Number))
+       l.SetField(tbl, "offset", Vec2(l, [2]lua.LNumber{lua.LNumber(val.Offset[0]), lua.LNumber(val.Offset[1])}))
+       l.SetField(tbl, "pos", Vec2(l, [2]lua.LNumber{lua.LNumber(val.Pos[0]), lua.LNumber(val.Pos[1])}))
+       l.SetField(tbl, "scale", Vec2(l, [2]lua.LNumber{lua.LNumber(val.Scale[0]), lua.LNumber(val.Scale[1])}))
+       l.SetField(tbl, "size", Vec2(l, [2]lua.LNumber{lua.LNumber(val.Size[0]), lua.LNumber(val.Size[1])}))
+       l.SetField(tbl, "text", lua.LString(string(val.Text)))
+       l.SetField(tbl, "text_2", lua.LString(string(val.Text2)))
+       l.SetField(tbl, "type", HUDType(l, val.Type))
+       l.SetField(tbl, "world_pos", Vec3(l, [3]lua.LNumber{lua.LNumber(val.WorldPos[0]), lua.LNumber(val.WorldPos[1]), lua.LNumber(val.WorldPos[2])}))
+       l.SetField(tbl, "z_index", lua.LNumber(val.ZIndex))
+       return tbl
+}
+
+func Node(l *lua.LState, val mt.Node) lua.LValue {
+       tbl := l.NewTable()
+       l.SetField(tbl, "param0", lua.LNumber(val.Param0))
+       l.SetField(tbl, "param1", lua.LNumber(val.Param1))
+       l.SetField(tbl, "param2", lua.LNumber(val.Param2))
+       return tbl
+}
+
+func TileAnim(l *lua.LState, val mt.TileAnim) lua.LValue {
+       tbl := l.NewTable()
+       l.SetField(tbl, "aspect_ratio", Vec2(l, [2]lua.LNumber{lua.LNumber(val.AspectRatio[0]), lua.LNumber(val.AspectRatio[1])}))
+       l.SetField(tbl, "duration", lua.LNumber(val.Duration))
+       l.SetField(tbl, "n_frames", Vec2(l, [2]lua.LNumber{lua.LNumber(val.NFrames[0]), lua.LNumber(val.NFrames[1])}))
+       l.SetField(tbl, "type", AnimType(l, val.Type))
+       return tbl
+}
+
+func PktType(pkt *mt.Pkt) lua.LString {
+       switch pkt.Cmd.(type) {
+       case *mt.ToCltAcceptAuth:
+               return lua.LString("accept_auth")
+       case *mt.ToCltAcceptSudoMode:
+               return lua.LString("accept_sudo_mode")
+       case *mt.ToCltAddHUD:
+               return lua.LString("add_hud")
+       case *mt.ToCltAddNode:
+               return lua.LString("add_node")
+       case *mt.ToCltAddParticleSpawner:
+               return lua.LString("add_particle_spawner")
+       case *mt.ToCltAddPlayerVel:
+               return lua.LString("add_player_vel")
+       case *mt.ToCltAnnounceMedia:
+               return lua.LString("announce_media")
+       case *mt.ToCltAOMsgs:
+               return lua.LString("ao_msgs")
+       case *mt.ToCltAORmAdd:
+               return lua.LString("ao_rm_add")
+       case *mt.ToCltBlkData:
+               return lua.LString("blk_data")
+       case *mt.ToCltBreath:
+               return lua.LString("breath")
+       case *mt.ToCltChangeHUD:
+               return lua.LString("change_hud")
+       case *mt.ToCltChatMsg:
+               return lua.LString("chat_msg")
+       case *mt.ToCltCloudParams:
+               return lua.LString("cloud_params")
+       case *mt.ToCltCSMRestrictionFlags:
+               return lua.LString("csm_restriction_flags")
+       case *mt.ToCltDeathScreen:
+               return lua.LString("death_screen")
+       case *mt.ToCltDelParticleSpawner:
+               return lua.LString("del_particle_spawner")
+       case *mt.ToCltDenySudoMode:
+               return lua.LString("deny_sudo_mode")
+       case *mt.ToCltDetachedInv:
+               return lua.LString("detached_inv")
+       case *mt.ToCltDisco:
+               return lua.LString("disco")
+       case *mt.ToCltEyeOffset:
+               return lua.LString("eye_offset")
+       case *mt.ToCltFadeSound:
+               return lua.LString("fade_sound")
+       case *mt.ToCltFormspecPrepend:
+               return lua.LString("formspec_prepend")
+       case *mt.ToCltFOV:
+               return lua.LString("fov")
+       case *mt.ToCltHello:
+               return lua.LString("hello")
+       case *mt.ToCltHP:
+               return lua.LString("hp")
+       case *mt.ToCltHUDFlags:
+               return lua.LString("hud_flags")
+       case *mt.ToCltInv:
+               return lua.LString("inv")
+       case *mt.ToCltInvFormspec:
+               return lua.LString("inv_formspec")
+       case *mt.ToCltItemDefs:
+               return lua.LString("item_defs")
+       case *mt.ToCltKick:
+               return lua.LString("kick")
+       case *mt.ToCltLegacyKick:
+               return lua.LString("legacy_kick")
+       case *mt.ToCltLocalPlayerAnim:
+               return lua.LString("local_player_anim")
+       case *mt.ToCltMedia:
+               return lua.LString("media")
+       case *mt.ToCltMediaPush:
+               return lua.LString("media_push")
+       case *mt.ToCltMinimapModes:
+               return lua.LString("minimap_modes")
+       case *mt.ToCltModChanMsg:
+               return lua.LString("mod_chan_msg")
+       case *mt.ToCltModChanSig:
+               return lua.LString("mod_chan_sig")
+       case *mt.ToCltMoonParams:
+               return lua.LString("moon_params")
+       case *mt.ToCltMovePlayer:
+               return lua.LString("move_player")
+       case *mt.ToCltMovement:
+               return lua.LString("movement")
+       case *mt.ToCltNodeDefs:
+               return lua.LString("node_defs")
+       case *mt.ToCltNodeMetasChanged:
+               return lua.LString("node_metas_changed")
+       case *mt.ToCltOverrideDayNightRatio:
+               return lua.LString("override_day_night_ratio")
+       case *mt.ToCltPlaySound:
+               return lua.LString("play_sound")
+       case *mt.ToCltPrivs:
+               return lua.LString("privs")
+       case *mt.ToCltRemoveNode:
+               return lua.LString("remove_node")
+       case *mt.ToCltRmHUD:
+               return lua.LString("rm_hud")
+       case *mt.ToCltSetHotbarParam:
+               return lua.LString("set_hotbar_param")
+       case *mt.ToCltShowFormspec:
+               return lua.LString("show_formspec")
+       case *mt.ToCltSkyParams:
+               return lua.LString("sky_params")
+       case *mt.ToCltSpawnParticle:
+               return lua.LString("spawn_particle")
+       case *mt.ToCltSRPBytesSaltB:
+               return lua.LString("srp_bytes_salt_b")
+       case *mt.ToCltStarParams:
+               return lua.LString("star_params")
+       case *mt.ToCltStopSound:
+               return lua.LString("stop_sound")
+       case *mt.ToCltSunParams:
+               return lua.LString("sun_params")
+       case *mt.ToCltTimeOfDay:
+               return lua.LString("time_of_day")
+       case *mt.ToCltUpdatePlayerList:
+               return lua.LString("update_player_list")
+       }
+       panic("impossible")
+       return ""
+}
+
+func Pkt(l *lua.LState, pkt *mt.Pkt) lua.LValue {
+       if pkt == nil {
+               return lua.LNil
+       }
+       tbl := l.NewTable()
+       l.SetField(tbl, "_type", PktType(pkt))
+       switch val := pkt.Cmd.(type) {
+       case *mt.ToCltAcceptAuth:
+               l.SetField(tbl, "map_seed", lua.LNumber(val.MapSeed))
+               l.SetField(tbl, "player_pos", Vec3(l, [3]lua.LNumber{lua.LNumber(val.PlayerPos[0]), lua.LNumber(val.PlayerPos[1]), lua.LNumber(val.PlayerPos[2])}))
+               l.SetField(tbl, "send_interval", lua.LNumber(val.SendInterval))
+               l.SetField(tbl, "sudo_auth_methods", AuthMethods(l, val.SudoAuthMethods))
+       case *mt.ToCltAddHUD:
+               l.SetField(tbl, "hud", HUD(l, val.HUD))
+               l.SetField(tbl, "id", lua.LNumber(val.ID))
+       case *mt.ToCltAddNode:
+               l.SetField(tbl, "keep_meta", lua.LBool(val.KeepMeta))
+               l.SetField(tbl, "node", Node(l, val.Node))
+               l.SetField(tbl, "pos", Vec3(l, [3]lua.LNumber{lua.LNumber(val.Pos[0]), lua.LNumber(val.Pos[1]), lua.LNumber(val.Pos[2])}))
+       case *mt.ToCltAddParticleSpawner:
+               l.SetField(tbl, "acc", Box3(l, [2][3]lua.LNumber{{lua.LNumber(val.Acc[0][0]), lua.LNumber(val.Acc[0][1]), lua.LNumber(val.Acc[0][2])}, {lua.LNumber(val.Acc[1][0]), lua.LNumber(val.Acc[1][1]), lua.LNumber(val.Acc[1][2])}}))
+               l.SetField(tbl, "amount", lua.LNumber(val.Amount))
+               l.SetField(tbl, "anim_params", TileAnim(l, val.AnimParams))
+               l.SetField(tbl, "ao_collision", lua.LBool(val.AOCollision))
+               l.SetField(tbl, "collide", lua.LBool(val.Collide))
+               l.SetField(tbl, "collision_rm", lua.LBool(val.CollisionRm))
+               l.SetField(tbl, "duration", lua.LNumber(val.Duration))
+               l.SetField(tbl, "expiration_time", Box1(l, [2]lua.LNumber{lua.LNumber(val.ExpirationTime[0]), lua.LNumber(val.ExpirationTime[1])}))
+               l.SetField(tbl, "glow", lua.LNumber(val.Glow))
+               l.SetField(tbl, "id", lua.LNumber(val.ID))
+               l.SetField(tbl, "node_param0", lua.LNumber(val.NodeParam0))
+               l.SetField(tbl, "node_param2", lua.LNumber(val.NodeParam2))
+               l.SetField(tbl, "node_tile", lua.LNumber(val.NodeTile))
+               l.SetField(tbl, "pos", Box3(l, [2][3]lua.LNumber{{lua.LNumber(val.Pos[0][0]), lua.LNumber(val.Pos[0][1]), lua.LNumber(val.Pos[0][2])}, {lua.LNumber(val.Pos[1][0]), lua.LNumber(val.Pos[1][1]), lua.LNumber(val.Pos[1][2])}}))
+               l.SetField(tbl, "size", Box1(l, [2]lua.LNumber{lua.LNumber(val.Size[0]), lua.LNumber(val.Size[1])}))
+               l.SetField(tbl, "texture", lua.LString(string(val.Texture)))
+               l.SetField(tbl, "vel", Box3(l, [2][3]lua.LNumber{{lua.LNumber(val.Vel[0][0]), lua.LNumber(val.Vel[0][1]), lua.LNumber(val.Vel[0][2])}, {lua.LNumber(val.Vel[1][0]), lua.LNumber(val.Vel[1][1]), lua.LNumber(val.Vel[1][2])}}))
+               l.SetField(tbl, "vertical", lua.LBool(val.Vertical))
+       case *mt.ToCltAddPlayerVel:
+               l.SetField(tbl, "vel", Vec3(l, [3]lua.LNumber{lua.LNumber(val.Vel[0]), lua.LNumber(val.Vel[1]), lua.LNumber(val.Vel[2])}))
+       case *mt.ToCltBlkData:
+               l.SetField(tbl, "blkpos", Vec3(l, [3]lua.LNumber{lua.LNumber(val.Blkpos[0]), lua.LNumber(val.Blkpos[1]), lua.LNumber(val.Blkpos[2])}))
+       case *mt.ToCltBreath:
+               l.SetField(tbl, "breath", lua.LNumber(val.Breath))
+       case *mt.ToCltChangeHUD:
+               if val.Field == mt.HUDAlign {
+                       l.SetField(tbl, "align", Vec2(l, [2]lua.LNumber{lua.LNumber(val.Align[0]), lua.LNumber(val.Align[1])}))
+               }
+               if val.Field == mt.HUDDir {
+                       l.SetField(tbl, "dir", lua.LNumber(val.Dir))
+               }
+               l.SetField(tbl, "field", HUDField(l, val.Field))
+               l.SetField(tbl, "id", lua.LNumber(val.ID))
+               if val.Field == mt.HUDItem {
+                       l.SetField(tbl, "item", lua.LNumber(val.Item))
+               }
+               if val.Field == mt.HUDName {
+                       l.SetField(tbl, "name", lua.LString(string(val.Name)))
+               }
+               if val.Field == mt.HUDNumber {
+                       l.SetField(tbl, "number", lua.LNumber(val.Number))
+               }
+               if val.Field == mt.HUDOffset {
+                       l.SetField(tbl, "offset", Vec2(l, [2]lua.LNumber{lua.LNumber(val.Offset[0]), lua.LNumber(val.Offset[1])}))
+               }
+               if val.Field == mt.HUDPos {
+                       l.SetField(tbl, "pos", Vec2(l, [2]lua.LNumber{lua.LNumber(val.Pos[0]), lua.LNumber(val.Pos[1])}))
+               }
+               if val.Field == mt.HUDSize {
+                       l.SetField(tbl, "size", Vec2(l, [2]lua.LNumber{lua.LNumber(val.Size[0]), lua.LNumber(val.Size[1])}))
+               }
+               if val.Field == mt.HUDText {
+                       l.SetField(tbl, "text", lua.LString(string(val.Text)))
+               }
+               if val.Field == mt.HUDText2 {
+                       l.SetField(tbl, "text_2", lua.LString(string(val.Text2)))
+               }
+               if val.Field == mt.HUDWorldPos {
+                       l.SetField(tbl, "world_pos", Vec3(l, [3]lua.LNumber{lua.LNumber(val.WorldPos[0]), lua.LNumber(val.WorldPos[1]), lua.LNumber(val.WorldPos[2])}))
+               }
+               if val.Field == mt.HUDZIndex {
+                       l.SetField(tbl, "z_index", lua.LNumber(val.ZIndex))
+               }
+       case *mt.ToCltChatMsg:
+               l.SetField(tbl, "sender", lua.LString(string(val.Sender)))
+               l.SetField(tbl, "text", lua.LString(string(val.Text)))
+               l.SetField(tbl, "timestamp", lua.LNumber(val.Timestamp))
+               l.SetField(tbl, "type", ChatMsgType(l, val.Type))
+       case *mt.ToCltCloudParams:
+               l.SetField(tbl, "ambient_color", Color(l, val.AmbientColor))
+               l.SetField(tbl, "density", lua.LNumber(val.Density))
+               l.SetField(tbl, "diffuse_color", Color(l, val.DiffuseColor))
+               l.SetField(tbl, "height", lua.LNumber(val.Height))
+               l.SetField(tbl, "speed", Vec2(l, [2]lua.LNumber{lua.LNumber(val.Speed[0]), lua.LNumber(val.Speed[1])}))
+               l.SetField(tbl, "thickness", lua.LNumber(val.Thickness))
+       case *mt.ToCltCSMRestrictionFlags:
+               l.SetField(tbl, "flags", CSMRestrictionFlags(l, val.Flags))
+               l.SetField(tbl, "map_range", lua.LNumber(val.MapRange))
+       case *mt.ToCltDeathScreen:
+               l.SetField(tbl, "point_at", Vec3(l, [3]lua.LNumber{lua.LNumber(val.PointAt[0]), lua.LNumber(val.PointAt[1]), lua.LNumber(val.PointAt[2])}))
+               l.SetField(tbl, "point_cam", lua.LBool(val.PointCam))
+       case *mt.ToCltDelParticleSpawner:
+               l.SetField(tbl, "id", lua.LNumber(val.ID))
+       case *mt.ToCltDetachedInv:
+               l.SetField(tbl, "inv", lua.LString(string(val.Inv)))
+               l.SetField(tbl, "keep", lua.LBool(val.Keep))
+               l.SetField(tbl, "len", lua.LNumber(val.Len))
+               l.SetField(tbl, "name", lua.LString(string(val.Name)))
+       case *mt.ToCltEyeOffset:
+               l.SetField(tbl, "first", Vec3(l, [3]lua.LNumber{lua.LNumber(val.First[0]), lua.LNumber(val.First[1]), lua.LNumber(val.First[2])}))
+               l.SetField(tbl, "third", Vec3(l, [3]lua.LNumber{lua.LNumber(val.Third[0]), lua.LNumber(val.Third[1]), lua.LNumber(val.Third[2])}))
+       case *mt.ToCltFadeSound:
+               l.SetField(tbl, "gain", lua.LNumber(val.Gain))
+               l.SetField(tbl, "id", lua.LNumber(val.ID))
+               l.SetField(tbl, "step", lua.LNumber(val.Step))
+       case *mt.ToCltFormspecPrepend:
+               l.SetField(tbl, "prepend", lua.LString(string(val.Prepend)))
+       case *mt.ToCltFOV:
+               l.SetField(tbl, "fov", lua.LNumber(val.FOV))
+               l.SetField(tbl, "multiplier", lua.LBool(val.Multiplier))
+               l.SetField(tbl, "transition_time", lua.LNumber(val.TransitionTime))
+       case *mt.ToCltHello:
+               l.SetField(tbl, "auth_methods", AuthMethods(l, val.AuthMethods))
+               l.SetField(tbl, "compression", lua.LNumber(val.Compression))
+               l.SetField(tbl, "proto_ver", lua.LNumber(val.ProtoVer))
+               l.SetField(tbl, "serialize_ver", lua.LNumber(val.SerializeVer))
+               l.SetField(tbl, "username", lua.LString(string(val.Username)))
+       case *mt.ToCltHP:
+               l.SetField(tbl, "hp", lua.LNumber(val.HP))
+       case *mt.ToCltHUDFlags:
+               l.SetField(tbl, "flags", HUDFlags(l, val.Flags))
+               l.SetField(tbl, "mask", HUDFlags(l, val.Mask))
+       case *mt.ToCltInv:
+               l.SetField(tbl, "inv", lua.LString(string(val.Inv)))
+       case *mt.ToCltInvFormspec:
+               l.SetField(tbl, "formspec", lua.LString(string(val.Formspec)))
+       case *mt.ToCltKick:
+               if val.Reason == mt.Custom || val.Reason == mt.Shutdown || val.Reason == mt.Crash {
+                       l.SetField(tbl, "custom", lua.LString(string(val.Custom)))
+               }
+               l.SetField(tbl, "reason", KickReason(l, val.Reason))
+               if val.Reason == mt.Shutdown || val.Reason == mt.Crash {
+                       l.SetField(tbl, "reconnect", lua.LBool(val.Reconnect))
+               }
+       case *mt.ToCltLegacyKick:
+               l.SetField(tbl, "reason", lua.LString(string(val.Reason)))
+       case *mt.ToCltLocalPlayerAnim:
+               l.SetField(tbl, "dig", Box1(l, [2]lua.LNumber{lua.LNumber(val.Dig[0]), lua.LNumber(val.Dig[1])}))
+               l.SetField(tbl, "idle", Box1(l, [2]lua.LNumber{lua.LNumber(val.Idle[0]), lua.LNumber(val.Idle[1])}))
+               l.SetField(tbl, "speed", lua.LNumber(val.Speed))
+               l.SetField(tbl, "walk", Box1(l, [2]lua.LNumber{lua.LNumber(val.Walk[0]), lua.LNumber(val.Walk[1])}))
+               l.SetField(tbl, "walk_dig", Box1(l, [2]lua.LNumber{lua.LNumber(val.WalkDig[0]), lua.LNumber(val.WalkDig[1])}))
+       case *mt.ToCltMediaPush:
+               l.SetField(tbl, "data", lua.LString(string(val.Data)))
+               l.SetField(tbl, "filename", lua.LString(string(val.Filename)))
+               l.SetField(tbl, "sha1", lua.LString(string(val.SHA1[:])))
+               l.SetField(tbl, "should_cache", lua.LBool(val.ShouldCache))
+       case *mt.ToCltModChanMsg:
+               l.SetField(tbl, "channel", lua.LString(string(val.Channel)))
+               l.SetField(tbl, "msg", lua.LString(string(val.Msg)))
+               l.SetField(tbl, "sender", lua.LString(string(val.Sender)))
+       case *mt.ToCltModChanSig:
+               l.SetField(tbl, "channel", lua.LString(string(val.Channel)))
+               l.SetField(tbl, "signal", ModChanSig(l, val.Signal))
+       case *mt.ToCltMoonParams:
+               l.SetField(tbl, "size", lua.LNumber(val.Size))
+               l.SetField(tbl, "texture", lua.LString(string(val.Texture)))
+               l.SetField(tbl, "tone_map", lua.LString(string(val.ToneMap)))
+               l.SetField(tbl, "visible", lua.LBool(val.Visible))
+       case *mt.ToCltMovePlayer:
+               l.SetField(tbl, "pitch", lua.LNumber(val.Pitch))
+               l.SetField(tbl, "pos", Vec3(l, [3]lua.LNumber{lua.LNumber(val.Pos[0]), lua.LNumber(val.Pos[1]), lua.LNumber(val.Pos[2])}))
+               l.SetField(tbl, "yaw", lua.LNumber(val.Yaw))
+       case *mt.ToCltMovement:
+               l.SetField(tbl, "air_accel", lua.LNumber(val.AirAccel))
+               l.SetField(tbl, "climb_speed", lua.LNumber(val.ClimbSpeed))
+               l.SetField(tbl, "crouch_speed", lua.LNumber(val.CrouchSpeed))
+               l.SetField(tbl, "default_accel", lua.LNumber(val.DefaultAccel))
+               l.SetField(tbl, "fast_accel", lua.LNumber(val.FastAccel))
+               l.SetField(tbl, "fast_speed", lua.LNumber(val.FastSpeed))
+               l.SetField(tbl, "fluidity", lua.LNumber(val.Fluidity))
+               l.SetField(tbl, "gravity", lua.LNumber(val.Gravity))
+               l.SetField(tbl, "jump_speed", lua.LNumber(val.JumpSpeed))
+               l.SetField(tbl, "sink", lua.LNumber(val.Sink))
+               l.SetField(tbl, "smoothing", lua.LNumber(val.Smoothing))
+               l.SetField(tbl, "walk_speed", lua.LNumber(val.WalkSpeed))
+       case *mt.ToCltOverrideDayNightRatio:
+               l.SetField(tbl, "override", lua.LBool(val.Override))
+               l.SetField(tbl, "ratio", lua.LNumber(val.Ratio))
+       case *mt.ToCltPlaySound:
+               l.SetField(tbl, "ephemeral", lua.LBool(val.Ephemeral))
+               l.SetField(tbl, "fade", lua.LNumber(val.Fade))
+               l.SetField(tbl, "gain", lua.LNumber(val.Gain))
+               l.SetField(tbl, "id", lua.LNumber(val.ID))
+               l.SetField(tbl, "loop", lua.LBool(val.Loop))
+               l.SetField(tbl, "name", lua.LString(string(val.Name)))
+               l.SetField(tbl, "pitch", lua.LNumber(val.Pitch))
+               l.SetField(tbl, "pos", Vec3(l, [3]lua.LNumber{lua.LNumber(val.Pos[0]), lua.LNumber(val.Pos[1]), lua.LNumber(val.Pos[2])}))
+               l.SetField(tbl, "src_aoid", lua.LNumber(val.SrcAOID))
+               l.SetField(tbl, "src_type", SoundSrcType(l, val.SrcType))
+       case *mt.ToCltPrivs:
+               l.SetField(tbl, "privs", StringSet(l, val.Privs))
+       case *mt.ToCltRemoveNode:
+               l.SetField(tbl, "pos", Vec3(l, [3]lua.LNumber{lua.LNumber(val.Pos[0]), lua.LNumber(val.Pos[1]), lua.LNumber(val.Pos[2])}))
+       case *mt.ToCltRmHUD:
+               l.SetField(tbl, "id", lua.LNumber(val.ID))
+       case *mt.ToCltSetHotbarParam:
+               l.SetField(tbl, "img", lua.LString(string(val.Img)))
+               l.SetField(tbl, "param", HotbarParam(l, val.Param))
+               l.SetField(tbl, "size", lua.LNumber(val.Size))
+       case *mt.ToCltShowFormspec:
+               l.SetField(tbl, "formname", lua.LString(string(val.Formname)))
+               l.SetField(tbl, "formspec", lua.LString(string(val.Formspec)))
+       case *mt.ToCltSkyParams:
+               l.SetField(tbl, "bg_color", Color(l, val.BgColor))
+               l.SetField(tbl, "clouds", lua.LBool(val.Clouds))
+               if val.Type == "regular" {
+                       l.SetField(tbl, "dawn_horizon", Color(l, val.DawnHorizon))
+               }
+               if val.Type == "regular" {
+                       l.SetField(tbl, "dawn_sky", Color(l, val.DawnSky))
+               }
+               if val.Type == "regular" {
+                       l.SetField(tbl, "day_horizon", Color(l, val.DayHorizon))
+               }
+               if val.Type == "regular" {
+                       l.SetField(tbl, "day_sky", Color(l, val.DaySky))
+               }
+               l.SetField(tbl, "fog_tint_type", lua.LString(string(val.FogTintType)))
+               if val.Type == "regular" {
+                       l.SetField(tbl, "indoor", Color(l, val.Indoor))
+               }
+               l.SetField(tbl, "moon_fog_tint", Color(l, val.MoonFogTint))
+               if val.Type == "regular" {
+                       l.SetField(tbl, "night_horizon", Color(l, val.NightHorizon))
+               }
+               if val.Type == "regular" {
+                       l.SetField(tbl, "night_sky", Color(l, val.NightSky))
+               }
+               l.SetField(tbl, "sun_fog_tint", Color(l, val.SunFogTint))
+               if val.Type == "skybox" {
+                       l.SetField(tbl, "textures", TextureList(l, val.Textures))
+               }
+               l.SetField(tbl, "type", lua.LString(string(val.Type)))
+       case *mt.ToCltSpawnParticle:
+               l.SetField(tbl, "acc", Vec3(l, [3]lua.LNumber{lua.LNumber(val.Acc[0]), lua.LNumber(val.Acc[1]), lua.LNumber(val.Acc[2])}))
+               l.SetField(tbl, "anim_params", TileAnim(l, val.AnimParams))
+               l.SetField(tbl, "ao_collision", lua.LBool(val.AOCollision))
+               l.SetField(tbl, "collide", lua.LBool(val.Collide))
+               l.SetField(tbl, "collision_rm", lua.LBool(val.CollisionRm))
+               l.SetField(tbl, "expiration_time", lua.LNumber(val.ExpirationTime))
+               l.SetField(tbl, "glow", lua.LNumber(val.Glow))
+               l.SetField(tbl, "node_param0", lua.LNumber(val.NodeParam0))
+               l.SetField(tbl, "node_param2", lua.LNumber(val.NodeParam2))
+               l.SetField(tbl, "node_tile", lua.LNumber(val.NodeTile))
+               l.SetField(tbl, "pos", Vec3(l, [3]lua.LNumber{lua.LNumber(val.Pos[0]), lua.LNumber(val.Pos[1]), lua.LNumber(val.Pos[2])}))
+               l.SetField(tbl, "size", lua.LNumber(val.Size))
+               l.SetField(tbl, "texture", lua.LString(string(val.Texture)))
+               l.SetField(tbl, "vel", Vec3(l, [3]lua.LNumber{lua.LNumber(val.Vel[0]), lua.LNumber(val.Vel[1]), lua.LNumber(val.Vel[2])}))
+               l.SetField(tbl, "vertical", lua.LBool(val.Vertical))
+       case *mt.ToCltSRPBytesSaltB:
+               l.SetField(tbl, "b", lua.LString(string(val.B)))
+               l.SetField(tbl, "salt", lua.LString(string(val.Salt)))
+       case *mt.ToCltStarParams:
+               l.SetField(tbl, "color", Color(l, val.Color))
+               l.SetField(tbl, "count", lua.LNumber(val.Count))
+               l.SetField(tbl, "size", lua.LNumber(val.Size))
+               l.SetField(tbl, "visible", lua.LBool(val.Visible))
+       case *mt.ToCltStopSound:
+               l.SetField(tbl, "id", lua.LNumber(val.ID))
+       case *mt.ToCltSunParams:
+               l.SetField(tbl, "rise", lua.LString(string(val.Rise)))
+               l.SetField(tbl, "rising", lua.LBool(val.Rising))
+               l.SetField(tbl, "size", lua.LNumber(val.Size))
+               l.SetField(tbl, "texture", lua.LString(string(val.Texture)))
+               l.SetField(tbl, "tone_map", lua.LString(string(val.ToneMap)))
+               l.SetField(tbl, "visible", lua.LBool(val.Visible))
+       case *mt.ToCltTimeOfDay:
+               l.SetField(tbl, "speed", lua.LNumber(val.Speed))
+               l.SetField(tbl, "time", lua.LNumber(val.Time))
+       case *mt.ToCltUpdatePlayerList:
+               l.SetField(tbl, "players", StringList(l, val.Players))
+               l.SetField(tbl, "type", PlayerListUpdateType(l, val.Type))
+       }
+       return tbl
+}
diff --git a/tolua/static.go b/tolua/static.go
new file mode 100644 (file)
index 0000000..30d56ef
--- /dev/null
@@ -0,0 +1,97 @@
+package tolua
+
+import (
+       "github.com/anon55555/mt"
+       "github.com/yuin/gopher-lua"
+       "image/color"
+)
+
+//go:generate ./generate.lua
+
+func pushVec2(l *lua.LState, val [2]lua.LNumber) {
+       l.Push(l.GetGlobal("vec2"))
+       l.Push(val[0])
+       l.Push(val[1])
+       l.Call(2, 1)
+}
+
+func pushVec3(l *lua.LState, val [3]lua.LNumber) {
+       l.Push(l.GetGlobal("vec3"))
+       l.Push(val[0])
+       l.Push(val[1])
+       l.Push(val[2])
+       l.Call(3, 1)
+}
+
+func popValue(l *lua.LState) lua.LValue {
+       ret := l.Get(-1)
+       l.Pop(1)
+       return ret
+}
+
+func Vec2(l *lua.LState, val [2]lua.LNumber) lua.LValue {
+       pushVec2(l, val)
+       return popValue(l)
+}
+
+func Vec3(l *lua.LState, val [3]lua.LNumber) lua.LValue {
+       pushVec3(l, val)
+       return popValue(l)
+}
+
+func Box1(l *lua.LState, val [2]lua.LNumber) lua.LValue {
+       l.Push(l.GetGlobal("box"))
+       l.Push(val[0])
+       l.Push(val[1])
+       l.Call(2, 1)
+       return popValue(l)
+}
+
+func Box2(l *lua.LState, val [2][2]lua.LNumber) lua.LValue {
+       l.Push(l.GetGlobal("box"))
+       pushVec2(l, val[0])
+       pushVec2(l, val[1])
+       l.Call(2, 1)
+       return popValue(l)
+}
+
+func Box3(l *lua.LState, val [2][3]lua.LNumber) lua.LValue {
+       l.Push(l.GetGlobal("box"))
+       pushVec3(l, val[0])
+       pushVec3(l, val[1])
+       l.Call(2, 1)
+       return popValue(l)
+}
+
+func Color(l *lua.LState, val color.NRGBA) lua.LValue {
+       tbl := l.NewTable()
+       l.SetField(tbl, "r", lua.LNumber(val.R))
+       l.SetField(tbl, "g", lua.LNumber(val.G))
+       l.SetField(tbl, "b", lua.LNumber(val.B))
+       l.SetField(tbl, "a", lua.LNumber(val.A))
+       return tbl
+}
+
+func StringSet(l *lua.LState, val []string) lua.LValue {
+       tbl := l.NewTable()
+       for _, str := range val {
+               l.SetField(tbl, str, lua.LTrue)
+       }
+       return tbl
+}
+
+func stringList[T ~string](l *lua.LState, val []T) lua.LValue {
+       tbl := l.NewTable()
+       for _, s := range val {
+               tbl.Append(lua.LString(s))
+       }
+       return tbl
+}
+
+func StringList(l *lua.LState, val []string) lua.LValue {
+       return stringList[string](l, val)
+}
+
+func TextureList(l *lua.LState, val []mt.Texture) lua.LValue {
+       return stringList[mt.Texture](l, val)
+}
diff --git a/types.go b/types.go
deleted file mode 100644 (file)
index 0fe1cad..0000000
--- a/types.go
+++ /dev/null
@@ -1,83 +0,0 @@
-package main
-
-import (
-       "github.com/Shopify/go-lua"
-       "github.com/anon55555/mt"
-       "image/color"
-)
-
-//go:generate ./mkconvert.lua
-
-func luaPushVec2(l *lua.State, val [2]float64) {
-       l.Global("vec2")
-       l.PushNumber(val[0])
-       l.PushNumber(val[1])
-       l.Call(2, 1)
-}
-
-func luaPushVec3(l *lua.State, val [3]float64) {
-       l.Global("vec3")
-       l.PushNumber(val[0])
-       l.PushNumber(val[1])
-       l.PushNumber(val[2])
-       l.Call(3, 1)
-}
-
-func luaPushBox1(l *lua.State, val [2]float64) {
-       l.Global("box")
-       l.PushNumber(val[0])
-       l.PushNumber(val[1])
-       l.Call(2, 1)
-}
-
-func luaPushBox2(l *lua.State, val [2][2]float64) {
-       l.Global("box")
-       luaPushVec2(l, val[0])
-       luaPushVec2(l, val[1])
-       l.Call(2, 1)
-}
-
-func luaPushBox3(l *lua.State, val [2][3]float64) {
-       l.Global("box")
-       luaPushVec3(l, val[0])
-       luaPushVec3(l, val[1])
-       l.Call(2, 1)
-}
-
-func luaPushColor(l *lua.State, val color.NRGBA) {
-       l.NewTable()
-       l.PushInteger(int(val.R))
-       l.SetField(-2, "r")
-       l.PushInteger(int(val.G))
-       l.SetField(-2, "g")
-       l.PushInteger(int(val.B))
-       l.SetField(-2, "b")
-       l.PushInteger(int(val.A))
-       l.SetField(-2, "a")
-}
-
-func luaPushStringSet(l *lua.State, val []string) {
-       l.NewTable()
-       for _, str := range val {
-               l.PushBoolean(true)
-               l.SetField(-2, str)
-       }
-}
-
-func luaPushStringList(l *lua.State, val []string) {
-       l.NewTable()
-       for i, str := range val {
-               l.PushString(str)
-               l.RawSetInt(-2, i+1)
-       }
-}
-
-// i hate go for making me do this instead of just using luaPushStringList
-// but i dont want to make an unsafe cast either
-func luaPushTextureList(l *lua.State, val []mt.Texture) {
-       l.NewTable()
-       for i, str := range val {
-               l.PushString(string(str))
-               l.RawSetInt(-2, i+1)
-       }
-}