diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | auth.go | 254 | ||||
-rw-r--r-- | builtin/vector.lua | 126 | ||||
-rw-r--r-- | callbacks.go | 85 | ||||
-rw-r--r-- | client.go | 172 | ||||
-rw-r--r-- | convert.go | 882 | ||||
-rw-r--r-- | go.mod | 10 | ||||
-rw-r--r-- | go.sum | 8 | ||||
-rw-r--r-- | hydra.go | 74 | ||||
-rwxr-xr-x | mkconvert.lua | 201 | ||||
-rw-r--r-- | poll.go | 89 | ||||
-rw-r--r-- | spec/casemap | 23 | ||||
-rw-r--r-- | spec/client/enum | 70 | ||||
-rw-r--r-- | spec/client/flag | 19 | ||||
-rw-r--r-- | spec/client/pkt | 276 | ||||
-rw-r--r-- | spec/client/struct | 24 | ||||
-rw-r--r-- | types.go | 81 |
17 files changed, 2395 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..83f0206 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +hydra @@ -0,0 +1,254 @@ +package main + +import ( + "github.com/HimbeerserverDE/srp" + "github.com/Shopify/go-lua" + "github.com/anon55555/mt" + "strings" + "time" +) + +type authState uint8 + +const ( + asInit authState = iota + asRequested + asVerified + asActive + asError +) + +type Auth struct { + client *Client + username string + password string + language string + state authState + err string + srpBytesA, bytesA []byte +} + +func getAuth(l *lua.State) *Auth { + return lua.CheckUserData(l, 1, "hydra.auth").(*Auth) +} + +func (auth *Auth) create(client *Client) { + auth.client = client + auth.language = "en_US" + auth.state = asInit +} + +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) canConnect() (bool, string) { + if auth.username == "" { + return false, "missing username" + } + + return true, "" +} + +func (auth *Auth) connect() { + go func() { + for auth.state == asInit && auth.client.state == csConnected { + auth.client.conn.SendCmd(&mt.ToSrvInit{ + SerializeVer: 28, + MinProtoVer: 39, + MaxProtoVer: 39, + PlayerName: auth.username, + }) + time.Sleep(500 * time.Millisecond) + } + }() +} + +func (auth *Auth) setError(err string) { + auth.state = asError + auth.err = err + auth.client.conn.Close() +} + +func (auth *Auth) checkState(state authState, pkt *mt.Pkt) bool { + if auth.state == state { + return true + } + + auth.setError("received " + pktToString(pkt) + " in invalid state") + return false +} + +func (auth *Auth) handle(pkt *mt.Pkt, l *lua.State, idx int) { + if pkt == nil { + return + } + + switch cmd := pkt.Cmd.(type) { + case *mt.ToCltHello: + if !auth.checkState(asInit, pkt) { + return + } + + if cmd.SerializeVer != 28 { + auth.setError("unsupported serialize_ver") + 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()) + return + } + + auth.client.conn.SendCmd(&mt.ToSrvFirstSRP{ + Salt: salt, + Verifier: verifier, + EmptyPasswd: auth.password == "", + }) + auth.state = asVerified + } else if cmd.AuthMethods == mt.SRP { + var err error + auth.srpBytesA, auth.bytesA, err = srp.InitiateHandshake() + if err != nil { + auth.setError(err.Error()) + return + } + + auth.client.conn.SendCmd(&mt.ToSrvSRPBytesA{ + A: auth.srpBytesA, + NoSHA1: true, + }) + auth.state = asRequested + } else { + auth.setError("invalid auth methods") + return + } + + case *mt.ToCltSRPBytesSaltB: + if !auth.checkState(asRequested, pkt) { + return + } + + 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()) + return + } + + M := srp.ClientProof([]byte(auth.username), cmd.Salt, auth.srpBytesA, cmd.B, srpBytesK) + auth.srpBytesA = []byte{} + auth.bytesA = []byte{} + + if M == nil { + auth.setError("srp safety check fail") + return + } + + auth.client.conn.SendCmd(&mt.ToSrvSRPBytesM{ + M: M, + }) + auth.state = asVerified + + case *mt.ToCltAcceptAuth: + auth.client.conn.SendCmd(&mt.ToSrvInit2{Lang: auth.language}) + + case *mt.ToCltTimeOfDay: + if auth.state == asActive { + return + } + + if !auth.checkState(asVerified, pkt) { + return + } + + auth.client.conn.SendCmd(&mt.ToSrvCltReady{ + Major: 5, + Minor: 6, + Patch: 0, + Reserved: 0, + Formspec: 4, + // Version: "hydra-dragonfire", + Version: "astolfo", + }) + 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") + } + auth.username = lua.CheckString(l, 2) + return 0 + } else { + l.PushString(auth.username) + return 1 + } +} + +func l_auth_password(l *lua.State) int { + auth := getAuth(l) + + 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_language(l *lua.State) int { + auth := getAuth(l) + + 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_state(l *lua.State) int { + auth := getAuth(l) + + switch auth.state { + case asInit: + l.PushString("init") + case asRequested: + l.PushString("requested") + case asVerified: + l.PushString("verified") + case asActive: + l.PushString("active") + case asError: + l.PushString("error") + l.PushString(auth.err) + return 2 + } + + return 1 +} diff --git a/builtin/vector.lua b/builtin/vector.lua new file mode 100644 index 0000000..2fd926a --- /dev/null +++ b/builtin/vector.lua @@ -0,0 +1,126 @@ +--[[ builtin/vector.lua ]]-- + +local function wrap(op, body_wrapper, ...) + return load("return function(a, b) " .. body_wrapper(op, ...) .. "end")() +end + +local function arith_mt(...) + return { + __add = wrap("+", ...), + __sub = wrap("-", ...), + __mul = wrap("*", ...), + __div = wrap("/", ...), + __mod = wrap("%", ...), + } +end + +-- vec2 + +local mt_vec2 = arith_mt(function(op) + return [[ + if type(b) == "number" then + return vec2(a.x ]] .. op.. [[ b, a.y ]] .. op .. [[ b) + else + return vec2(a.x ]] .. op.. [[ b.x, a.y ]] .. op.. [[ b.y) + end + ]] +end) + +function mt_vec2:__neg() + return vec2(-self.x, -self.y) +end + +function mt_vec2:__tostring() + return "(" .. self.x .. ", " .. self.y .. ")" +end + +function vec2(a, b) + local o = {} + + if type(a) == "number" then + o.x = a + o.y = b or a + else + o.x = a.x + o.y = a.y + end + + setmetatable(o, mt_vec2) + return o +end + +-- vec3 + +local mt_vec3 = arith_mt(function(op) + return [[ + if type(b) == "number" then + return vec3(a.x ]] .. op.. [[ b, a.y ]] .. op .. [[ b, a.z ]] .. op .. [[ b) + else + return vec3(a.x ]] .. op.. [[ b.x, a.y ]] .. op.. [[ b.y, a.z ]] .. op.. [[ b.z) + end + ]] +end) + +function mt_vec3:__neg() + return vec3(-self.x, -self.y, -self.z) +end + +function mt_vec3:__tostring() + return "(" .. self.x .. ", " .. self.y .. ", " .. self.z .. ")" +end + +function vec3(a, b, c) + local o = {} + + if type(a) == "number" then + o.x = a + o.y = b or a + o.z = c or a + else + o.x = a.x + o.y = a.y + o.z = a.z + end + + setmetatable(o, mt_vec3) + return o +end + +-- box + +local mt_box = arith_mt(function(op) + return "return box(a.min " .. op .. " b, a.max " .. op .. " b)" +end) + +function mt_box:__neg() + return box(-self.min, -self.max) +end + +function mt_box:__tostring() + return "[" .. self.min .. "; " .. self.max .. "]" +end + +mt_box.__index = { + contains = function(a, b) + if type(b) == "number" or b.x then + return a.min <= b and a.max >= b + else + return a.min <= b.min and a.max >= b.max + end + end, +} + +function box(a, b) + local o = {} + + if type(a) == "number" or a.x then + o.min = a + o.max = b + else + o.min = a.min + o.max = a.max + end + + setmetatable(o, mt_box) + return o +end diff --git a/callbacks.go b/callbacks.go new file mode 100644 index 0000000..e16aec7 --- /dev/null +++ b/callbacks.go @@ -0,0 +1,85 @@ +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 +} diff --git a/client.go b/client.go new file mode 100644 index 0000000..a57e032 --- /dev/null +++ b/client.go @@ -0,0 +1,172 @@ +package main + +import ( + "errors" + "github.com/Shopify/go-lua" + "github.com/anon55555/mt" + "net" +) + +type clientState uint8 + +const ( + csNew clientState = iota + csConnected + csDisconnected +) + +type Handler interface { + create(client *Client) + push(l *lua.State) + canConnect() (bool, string) + connect() + handle(pkt *mt.Pkt, l *lua.State, idx int) +} + +type Client struct { + address string + state clientState + handlers map[string]Handler + conn mt.Peer + queue chan *mt.Pkt +} + +func getClient(l *lua.State) *Client { + return lua.CheckUserData(l, 1, "hydra.client").(*Client) +} + +func l_client(l *lua.State) int { + client := &Client{ + address: lua.CheckString(l, 1), + state: csNew, + handlers: map[string]Handler{}, + } + + 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") + } + l.SetMetaTable(-2) + + return 1 +} + +func l_client_address(l *lua.State) int { + client := getClient(l) + l.PushString(client.address) + return 1 +} + +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") + } + return 1 +} + +func l_client_handler(l *lua.State) int { + client := getClient(l) + name := lua.CheckString(l, 2) + + handler, exists := client.handlers[name] + if !exists { + switch name { + case "callbacks": + handler = &Callbacks{} + + case "auth": + handler = &Auth{} + + default: + return 0 + } + + client.handlers[name] = handler + handler.create(client) + } + + handler.push(l) + return 1 +} + +func l_client_connect(l *lua.State) int { + client := getClient(l) + + if client.state != csNew { + l.PushBoolean(false) + l.PushString("invalid state") + return 2 + } + + for _, handler := range client.handlers { + ok, err := handler.canConnect() + + if !ok { + l.PushBoolean(false) + l.PushString(err) + return 2 + } + } + + addr, err := net.ResolveUDPAddr("udp", client.address) + if err != nil { + l.PushBoolean(false) + l.PushString(err.Error()) + return 2 + } + + conn, err := net.DialUDP("udp", nil, addr) + if err != nil { + l.PushBoolean(false) + l.PushString(err.Error()) + return 2 + } + + 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 + } else if errors.Is(err, net.ErrClosed) { + close(client.queue) + return + } + } + }() + + l.PushBoolean(true) + return 1 +} + +func l_client_disconnect(l *lua.State) int { + client := getClient(l) + + if client.state == csConnected { + client.conn.Close() + } + + return 0 +} diff --git a/convert.go b/convert.go new file mode 100644 index 0000000..c5e4daa --- /dev/null +++ b/convert.go @@ -0,0 +1,882 @@ +// generated by mkconvert.lua, DO NOT EDIT +package main + +import ( + "github.com/Shopify/go-lua" + "github.com/anon55555/mt" +) + +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 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 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 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 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 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 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 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 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 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 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 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 luaPushTileAnim(l *lua.State, val mt.TileAnim) { + l.NewTable() + luaPushAnimType(l, val.Type) + l.SetField(-2, "type") + luaPushVec2(l, [2]float64{float64(val.NFrames[0]), float64(val.NFrames[1])}) + l.SetField(-2, "n_frames") + l.PushNumber(float64(val.Duration)) + l.SetField(-2, "duration") + luaPushVec2(l, [2]float64{float64(val.AspectRatio[0]), float64(val.AspectRatio[1])}) + l.SetField(-2, "aspect_ratio") +} + +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 luaPushHUD(l *lua.State, val mt.HUD) { + l.NewTable() + luaPushHUDType(l, val.Type) + l.SetField(-2, "type") + l.PushInteger(int(val.ZIndex)) + l.SetField(-2, "z_index") + luaPushVec2(l, [2]float64{float64(val.Scale[0]), float64(val.Scale[1])}) + l.SetField(-2, "scale") + l.PushString(string(val.Name)) + l.SetField(-2, "name") + luaPushVec3(l, [3]float64{float64(val.WorldPos[0]), float64(val.WorldPos[1]), float64(val.WorldPos[2])}) + l.SetField(-2, "world_pos") + l.PushString(string(val.Text)) + l.SetField(-2, "text") + l.PushString(string(val.Text2)) + l.SetField(-2, "text_2") + luaPushVec2(l, [2]float64{float64(val.Size[0]), float64(val.Size[1])}) + l.SetField(-2, "size") + luaPushVec2(l, [2]float64{float64(val.Align[0]), float64(val.Align[1])}) + l.SetField(-2, "align") + luaPushVec2(l, [2]float64{float64(val.Pos[0]), float64(val.Pos[1])}) + l.SetField(-2, "pos") + l.PushInteger(int(val.Dir)) + l.SetField(-2, "dir") + luaPushVec2(l, [2]float64{float64(val.Offset[0]), float64(val.Offset[1])}) + l.SetField(-2, "offset") + l.PushInteger(int(val.Number)) + l.SetField(-2, "number") + l.PushInteger(int(val.Item)) + l.SetField(-2, "item") +} + +func pktToString(pkt *mt.Pkt) string { + switch pkt.Cmd.(type) { + case *mt.ToCltPlaySound: + return "play_sound" + case *mt.ToCltLegacyKick: + return "legacy_kick" + case *mt.ToCltFOV: + return "fov" + case *mt.ToCltNodeMetasChanged: + return "node_metas_changed" + case *mt.ToCltHello: + return "hello" + case *mt.ToCltAcceptSudoMode: + return "accept_sudo_mode" + case *mt.ToCltPrivs: + return "privs" + case *mt.ToCltDetachedInv: + return "detached_inv" + case *mt.ToCltSpawnParticle: + return "spawn_particle" + case *mt.ToCltAcceptAuth: + return "accept_auth" + case *mt.ToCltOverrideDayNightRatio: + return "override_day_night_ratio" + case *mt.ToCltMinimapModes: + return "minimap_modes" + case *mt.ToCltAddHUD: + return "add_hud" + case *mt.ToCltHP: + return "hp" + case *mt.ToCltChangeHUD: + return "change_hud" + case *mt.ToCltFormspecPrepend: + return "formspec_prepend" + case *mt.ToCltSRPBytesSaltB: + return "srp_bytes_salt_b" + case *mt.ToCltSetHotbarParam: + return "set_hotbar_param" + case *mt.ToCltMovePlayer: + return "move_player" + case *mt.ToCltAddParticleSpawner: + return "add_particle_spawner" + case *mt.ToCltKick: + return "kick" + case *mt.ToCltDelParticleSpawner: + return "del_particle_spawner" + case *mt.ToCltModChanSig: + return "mod_chan_sig" + case *mt.ToCltMoonParams: + return "moon_params" + case *mt.ToCltModChanMsg: + return "mod_chan_msg" + case *mt.ToCltSunParams: + return "sun_params" + case *mt.ToCltInv: + return "inv" + case *mt.ToCltRemoveNode: + return "remove_node" + case *mt.ToCltNodeDefs: + return "node_defs" + case *mt.ToCltMediaPush: + return "media_push" + case *mt.ToCltLocalPlayerAnim: + return "local_player_anim" + case *mt.ToCltFadeSound: + return "fade_sound" + case *mt.ToCltItemDefs: + return "item_defs" + case *mt.ToCltUpdatePlayerList: + return "update_player_list" + case *mt.ToCltEyeOffset: + return "eye_offset" + case *mt.ToCltMedia: + return "media" + case *mt.ToCltDisco: + return "disco" + case *mt.ToCltBlkData: + return "blk_data" + case *mt.ToCltSkyParams: + return "sky_params" + case *mt.ToCltBreath: + return "breath" + case *mt.ToCltChatMsg: + return "chat_msg" + case *mt.ToCltHUDFlags: + return "hud_flags" + case *mt.ToCltAOMsgs: + return "ao_msgs" + case *mt.ToCltRmHUD: + return "rm_hud" + case *mt.ToCltStarParams: + return "star_params" + case *mt.ToCltDeathScreen: + return "death_screen" + case *mt.ToCltAORmAdd: + return "ao_rm_add" + case *mt.ToCltAddPlayerVel: + return "add_player_vel" + case *mt.ToCltMovement: + return "movement" + case *mt.ToCltCloudParams: + return "cloud_params" + case *mt.ToCltDenySudoMode: + return "deny_sudo_mode" + case *mt.ToCltCSMRestrictionFlags: + return "csm_restriction_flags" + case *mt.ToCltAddNode: + return "add_node" + case *mt.ToCltStopSound: + return "stop_sound" + case *mt.ToCltInvFormspec: + return "inv_formspec" + case *mt.ToCltAnnounceMedia: + return "announce_media" + case *mt.ToCltShowFormspec: + return "show_formspec" + case *mt.ToCltTimeOfDay: + return "time_of_day" + } + 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.ToCltPlaySound: + l.PushNumber(float64(val.Gain)) + l.SetField(-2, "gain") + l.PushInteger(int(val.ID)) + l.SetField(-2, "id") + l.PushNumber(float64(val.Pitch)) + l.SetField(-2, "pitch") + luaPushSoundSrcType(l, val.SrcType) + l.SetField(-2, "src_type") + l.PushInteger(int(val.SrcAOID)) + l.SetField(-2, "src_aoid") + l.PushNumber(float64(val.Fade)) + l.SetField(-2, "fade") + l.PushBoolean(bool(val.Ephemeral)) + l.SetField(-2, "ephemeral") + l.PushBoolean(bool(val.Loop)) + l.SetField(-2, "loop") + l.PushString(string(val.Name)) + l.SetField(-2, "name") + luaPushVec3(l, [3]float64{float64(val.Pos[0]), float64(val.Pos[1]), float64(val.Pos[2])}) + l.SetField(-2, "pos") + case *mt.ToCltLegacyKick: + l.PushString(string(val.Reason)) + l.SetField(-2, "reason") + case *mt.ToCltFOV: + l.PushNumber(float64(val.TransitionTime)) + l.SetField(-2, "transition_time") + l.PushBoolean(bool(val.Multiplier)) + l.SetField(-2, "multiplier") + l.PushNumber(float64(val.FOV)) + l.SetField(-2, "fov") + case *mt.ToCltHello: + luaPushAuthMethods(l, val.AuthMethods) + l.SetField(-2, "auth_methods") + l.PushString(string(val.Username)) + l.SetField(-2, "username") + 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") + case *mt.ToCltPrivs: + luaPushStringSet(l, val.Privs) + l.SetField(-2, "privs") + 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.ToCltSpawnParticle: + l.PushBoolean(bool(val.Collide)) + l.SetField(-2, "collide") + l.PushString(string(val.Texture)) + l.SetField(-2, "texture") + luaPushVec3(l, [3]float64{float64(val.Pos[0]), float64(val.Pos[1]), float64(val.Pos[2])}) + l.SetField(-2, "pos") + l.PushInteger(int(val.NodeTile)) + l.SetField(-2, "node_tile") + l.PushBoolean(bool(val.Vertical)) + l.SetField(-2, "vertical") + l.PushInteger(int(val.Glow)) + l.SetField(-2, "glow") + l.PushInteger(int(val.NodeParam2)) + l.SetField(-2, "node_param2") + l.PushInteger(int(val.NodeParam0)) + l.SetField(-2, "node_param0") + l.PushBoolean(bool(val.AOCollision)) + l.SetField(-2, "ao_collision") + l.PushNumber(float64(val.Size)) + l.SetField(-2, "size") + l.PushNumber(float64(val.ExpirationTime)) + l.SetField(-2, "expiration_time") + l.PushBoolean(bool(val.CollisionRm)) + l.SetField(-2, "collision_rm") + luaPushTileAnim(l, val.AnimParams) + l.SetField(-2, "anim_params") + luaPushVec3(l, [3]float64{float64(val.Acc[0]), float64(val.Acc[1]), float64(val.Acc[2])}) + l.SetField(-2, "acc") + luaPushVec3(l, [3]float64{float64(val.Vel[0]), float64(val.Vel[1]), float64(val.Vel[2])}) + l.SetField(-2, "vel") + case *mt.ToCltAcceptAuth: + l.PushNumber(float64(val.MapSeed)) + l.SetField(-2, "map_seed") + luaPushAuthMethods(l, val.SudoAuthMethods) + l.SetField(-2, "sudo_auth_methods") + l.PushNumber(float64(val.SendInterval)) + l.SetField(-2, "send_interval") + luaPushVec3(l, [3]float64{float64(val.PlayerPos[0]), float64(val.PlayerPos[1]), float64(val.PlayerPos[2])}) + l.SetField(-2, "player_pos") + case *mt.ToCltOverrideDayNightRatio: + l.PushInteger(int(val.Ratio)) + l.SetField(-2, "ratio") + l.PushBoolean(bool(val.Override)) + l.SetField(-2, "override") + case *mt.ToCltAddHUD: + luaPushHUD(l, val.HUD) + l.SetField(-2, "hud") + l.PushInteger(int(val.ID)) + l.SetField(-2, "id") + case *mt.ToCltHP: + l.PushInteger(int(val.HP)) + l.SetField(-2, "hp") + case *mt.ToCltChangeHUD: + 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.HUDText2 { + l.PushString(string(val.Text2)) + l.SetField(-2, "text_2") + } + if val.Field == mt.HUDItem { + l.PushInteger(int(val.Item)) + l.SetField(-2, "item") + } + if val.Field == mt.HUDZIndex { + l.PushInteger(int(val.ZIndex)) + l.SetField(-2, "z_index") + } + 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.HUDName { + l.PushString(string(val.Name)) + l.SetField(-2, "name") + } + if val.Field == mt.HUDDir { + l.PushInteger(int(val.Dir)) + l.SetField(-2, "dir") + } + 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.HUDNumber { + l.PushInteger(int(val.Number)) + l.SetField(-2, "number") + } + if val.Field == mt.HUDText { + l.PushString(string(val.Text)) + l.SetField(-2, "text") + } + if val.Field == mt.HUDOffset { + luaPushVec2(l, [2]float64{float64(val.Offset[0]), float64(val.Offset[1])}) + l.SetField(-2, "offset") + } + luaPushHUDField(l, val.Field) + l.SetField(-2, "field") + l.PushInteger(int(val.ID)) + l.SetField(-2, "id") + case *mt.ToCltFormspecPrepend: + l.PushString(string(val.Prepend)) + l.SetField(-2, "prepend") + case *mt.ToCltSRPBytesSaltB: + l.PushString(string(val.B)) + l.SetField(-2, "b") + l.PushString(string(val.Salt)) + l.SetField(-2, "salt") + case *mt.ToCltSetHotbarParam: + luaPushHotbarParam(l, val.Param) + l.SetField(-2, "param") + l.PushInteger(int(val.Size)) + l.SetField(-2, "size") + l.PushString(string(val.Img)) + l.SetField(-2, "img") + case *mt.ToCltMovePlayer: + l.PushNumber(float64(val.Yaw)) + l.SetField(-2, "yaw") + 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") + 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.ID)) + l.SetField(-2, "id") + l.PushString(string(val.Texture)) + l.SetField(-2, "texture") + l.PushBoolean(bool(val.Vertical)) + l.SetField(-2, "vertical") + luaPushBox1(l, [2]float64{float64(val.ExpirationTime[0]), float64(val.ExpirationTime[1])}) + l.SetField(-2, "expiration_time") + luaPushTileAnim(l, val.AnimParams) + l.SetField(-2, "anim_params") + l.PushBoolean(bool(val.AOCollision)) + l.SetField(-2, "ao_collision") + 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") + l.PushInteger(int(val.Glow)) + l.SetField(-2, "glow") + l.PushInteger(int(val.NodeParam0)) + l.SetField(-2, "node_param0") + 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.Collide)) + l.SetField(-2, "collide") + luaPushBox1(l, [2]float64{float64(val.Size[0]), float64(val.Size[1])}) + l.SetField(-2, "size") + l.PushInteger(int(val.NodeParam2)) + l.SetField(-2, "node_param2") + l.PushNumber(float64(val.Duration)) + l.SetField(-2, "duration") + l.PushInteger(int(val.NodeTile)) + l.SetField(-2, "node_tile") + l.PushInteger(int(val.Amount)) + l.SetField(-2, "amount") + l.PushBoolean(bool(val.CollisionRm)) + l.SetField(-2, "collision_rm") + case *mt.ToCltKick: + luaPushKickReason(l, val.Reason) + l.SetField(-2, "reason") + if dr := val.Reason; dr == mt.Custom || dr == mt.Shutdown || dr == mt.Crash { + l.PushString(string(val.Custom)) + l.SetField(-2, "custom") + } + if dr := val.Reason; dr == mt.Shutdown || dr == mt.Crash { + l.PushBoolean(bool(val.Reconnect)) + l.SetField(-2, "reconnect") + } + case *mt.ToCltDelParticleSpawner: + l.PushInteger(int(val.ID)) + l.SetField(-2, "id") + 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.ToneMap)) + l.SetField(-2, "tone_map") + l.PushString(string(val.Texture)) + l.SetField(-2, "texture") + l.PushBoolean(bool(val.Visible)) + l.SetField(-2, "visible") + case *mt.ToCltModChanMsg: + l.PushString(string(val.Channel)) + l.SetField(-2, "channel") + l.PushString(string(val.Sender)) + l.SetField(-2, "sender") + l.PushString(string(val.Msg)) + l.SetField(-2, "msg") + case *mt.ToCltSunParams: + l.PushString(string(val.Texture)) + l.SetField(-2, "texture") + l.PushBoolean(bool(val.Visible)) + l.SetField(-2, "visible") + l.PushNumber(float64(val.Size)) + l.SetField(-2, "size") + l.PushString(string(val.ToneMap)) + l.SetField(-2, "tone_map") + l.PushBoolean(bool(val.Rising)) + l.SetField(-2, "rising") + l.PushString(string(val.Rise)) + l.SetField(-2, "rise") + case *mt.ToCltInv: + l.PushString(string(val.Inv)) + l.SetField(-2, "inv") + 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.ToCltMediaPush: + l.PushString(string(val.SHA1[:])) + l.SetField(-2, "sha1") + l.PushString(string(val.Data)) + l.SetField(-2, "data") + l.PushBoolean(bool(val.ShouldCache)) + l.SetField(-2, "should_cache") + l.PushString(string(val.Filename)) + l.SetField(-2, "filename") + case *mt.ToCltLocalPlayerAnim: + luaPushBox1(l, [2]float64{float64(val.Walk[0]), float64(val.Walk[1])}) + l.SetField(-2, "walk") + 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.Dig[0]), float64(val.Dig[1])}) + l.SetField(-2, "dig") + luaPushBox1(l, [2]float64{float64(val.WalkDig[0]), float64(val.WalkDig[1])}) + l.SetField(-2, "walk_dig") + case *mt.ToCltFadeSound: + l.PushNumber(float64(val.Step)) + l.SetField(-2, "step") + l.PushInteger(int(val.ID)) + l.SetField(-2, "id") + l.PushNumber(float64(val.Gain)) + l.SetField(-2, "gain") + case *mt.ToCltUpdatePlayerList: + luaPushPlayerListUpdateType(l, val.Type) + l.SetField(-2, "type") + luaPushStringList(l, val.Players) + l.SetField(-2, "players") + 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.ToCltBlkData: + luaPushVec3(l, [3]float64{float64(val.Blkpos[0]), float64(val.Blkpos[1]), float64(val.Blkpos[2])}) + l.SetField(-2, "blkpos") + case *mt.ToCltSkyParams: + luaPushColor(l, val.SunFogTint) + l.SetField(-2, "sun_fog_tint") + l.PushString(string(val.FogTintType)) + l.SetField(-2, "fog_tint_type") + if val.Type == "regular" { + luaPushColor(l, val.DawnHorizon) + l.SetField(-2, "dawn_horizon") + } + if val.Type == "regular" { + luaPushColor(l, val.DaySky) + l.SetField(-2, "day_sky") + } + l.PushBoolean(bool(val.Clouds)) + l.SetField(-2, "clouds") + l.PushString(string(val.Type)) + l.SetField(-2, "type") + luaPushColor(l, val.BgColor) + l.SetField(-2, "bg_color") + if val.Type == "regular" { + luaPushColor(l, val.DawnSky) + l.SetField(-2, "dawn_sky") + } + 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") + } + if val.Type == "regular" { + luaPushColor(l, val.DayHorizon) + l.SetField(-2, "day_horizon") + } + if val.Type == "skybox" { + luaPushTextureList(l, val.Textures) + l.SetField(-2, "textures") + } + luaPushColor(l, val.MoonFogTint) + l.SetField(-2, "moon_fog_tint") + if val.Type == "regular" { + luaPushColor(l, val.Indoor) + l.SetField(-2, "indoor") + } + case *mt.ToCltBreath: + l.PushInteger(int(val.Breath)) + l.SetField(-2, "breath") + case *mt.ToCltChatMsg: + luaPushChatMsgType(l, val.Type) + l.SetField(-2, "type") + l.PushString(string(val.Sender)) + l.SetField(-2, "sender") + l.PushNumber(float64(val.Timestamp)) + l.SetField(-2, "timestamp") + l.PushString(string(val.Text)) + l.SetField(-2, "text") + case *mt.ToCltHUDFlags: + luaPushHUDFlags(l, val.Flags) + l.SetField(-2, "flags") + luaPushHUDFlags(l, val.Mask) + l.SetField(-2, "mask") + case *mt.ToCltRmHUD: + l.PushInteger(int(val.ID)) + l.SetField(-2, "id") + case *mt.ToCltStarParams: + l.PushInteger(int(val.Count)) + l.SetField(-2, "count") + l.PushNumber(float64(val.Size)) + l.SetField(-2, "size") + luaPushColor(l, val.Color) + l.SetField(-2, "color") + l.PushBoolean(bool(val.Visible)) + l.SetField(-2, "visible") + case *mt.ToCltDeathScreen: + l.PushBoolean(bool(val.PointCam)) + l.SetField(-2, "point_cam") + luaPushVec3(l, [3]float64{float64(val.PointAt[0]), float64(val.PointAt[1]), float64(val.PointAt[2])}) + l.SetField(-2, "point_at") + 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.ToCltMovement: + l.PushNumber(float64(val.JumpSpeed)) + l.SetField(-2, "jump_speed") + l.PushNumber(float64(val.FastAccel)) + l.SetField(-2, "fast_accel") + l.PushNumber(float64(val.FastSpeed)) + l.SetField(-2, "fast_speed") + l.PushNumber(float64(val.Sink)) + l.SetField(-2, "sink") + l.PushNumber(float64(val.AirAccel)) + l.SetField(-2, "air_accel") + l.PushNumber(float64(val.Gravity)) + l.SetField(-2, "gravity") + l.PushNumber(float64(val.CrouchSpeed)) + l.SetField(-2, "crouch_speed") + l.PushNumber(float64(val.Smoothing)) + l.SetField(-2, "smoothing") + l.PushNumber(float64(val.WalkSpeed)) + l.SetField(-2, "walk_speed") + l.PushNumber(float64(val.Fluidity)) + l.SetField(-2, "fluidity") + l.PushNumber(float64(val.ClimbSpeed)) + l.SetField(-2, "climb_speed") + l.PushNumber(float64(val.DefaultAccel)) + l.SetField(-2, "default_accel") + 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.ToCltAddNode: + luaPushNode(l, val.Node) + l.SetField(-2, "node") + l.PushBoolean(bool(val.KeepMeta)) + l.SetField(-2, "keep_meta") + luaPushVec3(l, [3]float64{float64(val.Pos[0]), float64(val.Pos[1]), float64(val.Pos[2])}) + l.SetField(-2, "pos") + case *mt.ToCltStopSound: + l.PushInteger(int(val.ID)) + l.SetField(-2, "id") + case *mt.ToCltInvFormspec: + l.PushString(string(val.Formspec)) + l.SetField(-2, "formspec") + case *mt.ToCltShowFormspec: + l.PushString(string(val.Formspec)) + l.SetField(-2, "formspec") + l.PushString(string(val.Formname)) + l.SetField(-2, "formname") + case *mt.ToCltTimeOfDay: + l.PushNumber(float64(val.Speed)) + l.SetField(-2, "speed") + l.PushInteger(int(val.Time)) + l.SetField(-2, "time") + } +} @@ -0,0 +1,10 @@ +module github.com/dragonfireclient/hydra + +go 1.17 + +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 +) @@ -0,0 +1,8 @@ +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= +github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= diff --git a/hydra.go b/hydra.go new file mode 100644 index 0000000..77d7e05 --- /dev/null +++ b/hydra.go @@ -0,0 +1,74 @@ +package main + +import ( + _ "embed" + "github.com/Shopify/go-lua" + "os" + "os/signal" + "syscall" + "time" +) + +var lastTime = time.Now() +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 +} + +func signalChannel() chan os.Signal { + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP) + return sig +} + +func main() { + if len(os.Args) < 2 { + panic("missing filename") + } + + go func() { + <-signalChannel() + 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.PushNumber(10.0) + l.SetField(-2, "BS") + + l.SetGlobal("hydra") + + l.NewTable() + for i, arg := range os.Args { + l.PushString(arg) + l.RawSetInt(-2, i - 1) + } + l.SetGlobal("arg") + + if err := lua.DoString(l, vectorLibrary); err != nil { + panic(err) + } + + if err := lua.DoFile(l, os.Args[1]); err != nil { + panic(err) + } +} diff --git a/mkconvert.lua b/mkconvert.lua new file mode 100755 index 0000000..d49d46d --- /dev/null +++ b/mkconvert.lua @@ -0,0 +1,201 @@ +#!/usr/bin/env lua +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 pairs(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 pairs(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 pairs(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 pairs(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 pairs(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() @@ -0,0 +1,89 @@ +package main + +import ( + "github.com/Shopify/go-lua" + "github.com/anon55555/mt" + "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++ + } + + var timeout time.Duration + hasTimeout := false + if l.IsNumber(3) { + timeout = time.Duration(lua.CheckNumber(l, 3) * float64(time.Second)) + hasTimeout = true + } + + for { + cases := make([]reflect.SelectCase, 0, len(clients)+2) + + for _, client := range clients { + if client.state != csConnected { + continue + } + + cases = append(cases, reflect.SelectCase{ + Dir: reflect.SelectRecv, + Chan: reflect.ValueOf(client.queue), + }) + } + + offset := len(cases) + + 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(time.After(timeout)), + }) + } + + idx, value, ok := reflect.Select(cases) + + if idx >= offset { + l.PushBoolean(true) + return 1 + } + + client := clients[idx] + + var pkt *mt.Pkt = nil + if ok { + pkt = value.Interface().(*mt.Pkt) + } else { + client.state = csDisconnected + } + + for _, handler := range client.handlers { + handler.handle(pkt, l, idx+1) + } + } + + panic("impossible") + return 0 +} diff --git a/spec/casemap b/spec/casemap new file mode 100644 index 0000000..c72df40 --- /dev/null +++ b/spec/casemap @@ -0,0 +1,23 @@ +id ID +ao AO +hud HUD +hp HP +fov FOV +srp SRP +sha1 SHA1 +ao_rm_add AORmAdd +ao_msgs AOMsgs +src_aoid SrcAOID +ao_collision AOCollision +add_hud AddHUD +rm_hud RmHUD +change_hud ChangeHUD +hud_flags HUDFlags +hud_type HUDType +hud_field HUDField +first_srp FirstSRP +csm_restriction_flags CSMRestrictionFlags +srp_bytes_salt_b SRPBytesSaltB +no_csms NoCSMs +join_ok JoinOK +leave_ok LeaveOK diff --git a/spec/client/enum b/spec/client/enum new file mode 100644 index 0000000..cd5f166 --- /dev/null +++ b/spec/client/enum @@ -0,0 +1,70 @@ +kick_reason + wrong_passwd + unexpected_data + srv_is_singleplayer + unsupported_ver + bad_name_chars + bad_name + too_many_clts + empty_passwd + already_connected + srv_err + custom + shutdown + crash +chat_msg_type + postfix Msg + raw + normal + announce + sys +sound_src_type + postfix Src + no + pos + ao +anim_type + postfix Anim + no + vertical_frame + sprite_sheet +hud_type + postfix HUD + img + text + statbar + inv + waypoint + img_waypoint +hud_field + prefix HUD + pos + name + scale + text + number + item + dir + align + offset + world_pos + size + z_index + text_2 +hotbar_param + prefix Hotbar + size + img + sel_img +mod_chan_sig + join_ok + join_fail + leave_ok + leave_fail + not_registered + set_state +player_list_update_type + postfix Players + init + add + remove diff --git a/spec/client/flag b/spec/client/flag new file mode 100644 index 0000000..e13f4da --- /dev/null +++ b/spec/client/flag @@ -0,0 +1,19 @@ +auth_methods + legacy_passwd + srp + first_srp +csm_restriction_flags + no_csms + no_chat_msgs + no_node_defs + limit_map_range + no_player_list +hud_flags + prefix Show + hotbar + health_bar + crosshair + wielded_item + breath_bar + minimap + radar_minimap diff --git a/spec/client/pkt b/spec/client/pkt new file mode 100644 index 0000000..bc04127 --- /dev/null +++ b/spec/client/pkt @@ -0,0 +1,276 @@ +hello + integer serialize_ver + integer compression + integer proto_ver + auth_methods auth_methods + string username +accept_auth + vec3 player_pos + # int64 + number map_seed + number send_interval + auth_methods sudo_auth_methods +accept_sudo_mode +deny_sudo_mode +kick + kick_reason reason + [custom] dr := val.Reason; dr == mt.Custom || dr == mt.Shutdown || dr == mt.Crash + string custom + [reconnect] dr := val.Reason; dr == mt.Shutdown || dr == mt.Crash + boolean reconnect +blk_data + vec3 blkpos + # TODO +add_node + vec3 pos + node node + boolean keep_meta +remove_node + vec3 pos +inv + string inv +time_of_day + integer time + number speed +csm_restriction_flags + csm_restriction_flags flags + integer map_range +add_player_vel + vec3 vel +media_push + fixed_string sha1 + string filename + boolean should_cache + string data +chat_msg + chat_msg_type type + string sender + string text + # int64 + number timestamp +ao_rm_add + # TODO +ao_msgs + # TODO +hp + integer hp +move_player + vec3 pos + number pitch + number yaw +legacy_kick + string reason +fov + number fov + boolean multiplier + number transition_time +death_screen + boolean point_cam + vec3 point_at +media + # TODO +node_defs + # TODO +announce_media + # TODO +item_defs + # TODO +play_sound + integer id + string name + number gain + sound_src_type src_type + vec3 pos + integer src_aoid + boolean loop + number fade + number pitch + boolean ephemeral +stop_sound + integer id +privs + string_set privs +inv_formspec + string formspec +detached_inv + string name + boolean keep + integer len + string inv +show_formspec + string formspec + string formname +movement + number default_accel + number air_accel + number fast_accel + number walk_speed + number crouch_speed + number fast_speed + number climb_speed + number jump_speed + number fluidity + number smoothing + number sink + number gravity +spawn_particle + vec3 pos + vec3 vel + vec3 acc + number expiration_time + number size + boolean collide + string texture + boolean vertical + boolean collision_rm + tile_anim anim_params + integer glow + boolean ao_collision + integer node_param0 + integer node_param2 + integer node_tile +add_particle_spawner + integer amount + number duration + box3 pos + box3 vel + box3 acc + box1 expiration_time + box1 size + boolean collide + string texture + integer id + boolean vertical + boolean collision_rm + tile_anim anim_params + integer glow + boolean ao_collision + integer node_param0 + integer node_param2 + integer node_tile +add_hud + integer id + hud hud +rm_hud + integer id +change_hud + integer id + hud_field field + [pos] val.Field == mt.HUDPos + [name] val.Field == mt.HUDName + [text] val.Field == mt.HUDText + [number] val.Field == mt.HUDNumber + [item] val.Field == mt.HUDItem + [dir] val.Field == mt.HUDDir + [align] val.Field == mt.HUDAlign + [offset] val.Field == mt.HUDOffset + [world_pos] val.Field == mt.HUDWorldPos + [size] val.Field == mt.HUDSize + [z_index] val.Field == mt.HUDZIndex + [text_2] val.Field == mt.HUDText2 + vec2 pos + string name + string text + integer number + integer item + integer dir + vec2 align + vec2 offset + vec3 world_pos + vec2 size + integer z_index + string text_2 +hud_flags + hud_flags flags + hud_flags mask +set_hotbar_param + hotbar_param param + integer size + string img +breath + integer breath +sky_params + color bg_color + string type + boolean clouds + color sun_fog_tint + color moon_fog_tint + string fog_tint_type + [textures] val.Type == "skybox" + texture_list textures + [day_sky] val.Type == "regular" + [day_horizon] val.Type == "regular" + [dawn_sky] val.Type == "regular" + [dawn_horizon] val.Type == "regular" + [night_sky] val.Type == "regular" + [night_horizon] val.Type == "regular" + [indoor] val.Type == "regular" + color day_sky + color day_horizon + color dawn_sky + color dawn_horizon + color night_sky + color night_horizon + color indoor +override_day_night_ratio + boolean override + integer ratio +local_player_anim + box1 idle + box1 walk + box1 dig + box1 walk_dig + number speed +eye_offset + vec3 first + vec3 third +del_particle_spawner + integer id +cloud_params + number density + color diffuse_color + color ambient_color + number height + number thickness + vec2 speed +fade_sound + integer id + number step + number gain +update_player_list + player_list_update_type type + string_list players +mod_chan_msg + string channel + string sender + string msg +mod_chan_sig + mod_chan_sig signal + string channel +node_metas_changed + # TODO +sun_params + boolean visible + string texture + string tone_map + string rise + boolean rising + number size +moon_params + boolean visible + string texture + string tone_map + number size +star_params + boolean visible + integer count + color color + number size +srp_bytes_salt_b + string salt + string b +formspec_prepend + string prepend +minimap_modes + # TODO +disco diff --git a/spec/client/struct b/spec/client/struct new file mode 100644 index 0000000..1fa149c --- /dev/null +++ b/spec/client/struct @@ -0,0 +1,24 @@ +node + integer param0 + integer param1 + integer param2 +tile_anim + anim_type type + vec2 aspect_ratio + vec2 n_frames + number duration +hud + hud_type type + vec2 pos + string name + vec2 scale + string text + integer number + integer item + integer dir + vec2 align + vec2 offset + vec3 world_pos + vec2 size + integer z_index + string text_2 diff --git a/types.go b/types.go new file mode 100644 index 0000000..6410457 --- /dev/null +++ b/types.go @@ -0,0 +1,81 @@ +package main + +import ( + "github.com/Shopify/go-lua" + "github.com/anon55555/mt" + "image/color" +) + +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) + } +} |