--- /dev/null
+--[[ builtin/client.lua ]]--
+function package.loaded.client()
+ local address, name, password = unpack(arg)
+ local client = hydra.client(address)
+
+ client:enable("auth")
+ client.auth:username(name)
+ client.auth:password(password or "")
+
+ return client
+end
return "(" .. self.x .. ", " .. self.y .. ")"
end
+mt_vec2.__index = {
+ validate = function(self)
+ assert(type(self.x) == "number")
+ assert(type(self.y) == "number")
+ return self
+ end
+}
+
function vec2(a, b)
local o = {}
end
setmetatable(o, mt_vec2)
- return o
+ return o:validate()
end
-- vec3
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
]]
end)
return "(" .. self.x .. ", " .. self.y .. ", " .. self.z .. ")"
end
+mt_vec3.__index = {
+ validate = function(self)
+ assert(type(self.x) == "number")
+ assert(type(self.y) == "number")
+ assert(type(self.z) == "number")
+ return self
+ end
+}
+
function vec3(a, b, c)
local o = {}
end
setmetatable(o, mt_vec3)
- return o
+ return o:validate()
end
-- box
return a.min <= b.min and a.max >= b.max
end
end,
+ validate = function(self)
+ if type(self.min) == "number" then
+ assert(type(self.max) == "number")
+ else
+ assert(not self.min.z == not self.max.z)
+ self.min:validate()
+ self.max:validate()
+ end
+ end,
}
function box(a, b)
end
setmetatable(o, mt_box)
- return o
+ return o:validate()
end
import (
"errors"
"github.com/anon55555/mt"
+ "github.com/dragonfireclient/hydra-dragonfire/fromlua"
"github.com/dragonfireclient/hydra-dragonfire/tolua"
"github.com/yuin/gopher-lua"
"net"
"subscribe": l_client_subscribe,
"unsubscribe": l_client_unsubscribe,
"wildcard": l_client_wildcard,
+ "send": l_client_send,
}
func getClient(l *lua.LState) *Client {
return 0
}
-/*
-
func l_client_send(l *lua.LState) int {
client := getClient(l)
- pkt := fromlua.Pkt(l.CheckTable(2))
+ cmd := fromlua.Cmd(l)
+ doAck := l.ToBool(4)
client.mu.Lock()
defer client.mu.Unlock()
if client.state == csConnected {
- client.conn.Send(pkt)
+ ack, err := client.conn.SendCmd(cmd)
+ if err != nil {
+ panic(err)
+ }
+
+ if doAck && !cmd.DefaultPktInfo().Unrel {
+ <-ack
+ }
}
return 0
}
-
-*/
#!/usr/bin/env hydra-dragonfire
local escapes = require("escapes")
-local address, name, password = unpack(arg)
-local client = hydra.client(address)
-
-client:enable("auth")
-client.auth:username(name)
-client.auth:password(password or "")
+local client = require("client")()
client:subscribe("chat_msg")
client:connect()
while not hydra.canceled() do
- local pkt, interrupt = client:poll()
+ local pkt, interrupt = client:poll(1)
if pkt then
print(escapes.strip_all(pkt.text))
- elseif not interrupt then
+ elseif interrupt then
+ client:send("chat_msg", {msg = "test"})
+ else
print("disconnected")
break
end
#!/usr/bin/env hydra-dragonfire
-local address, name, password = unpack(arg)
-local client = hydra.client(address)
-
-client:enable("auth")
-client.auth:username(name)
-client.auth:password(password or "")
+local client = require("client")()
client:wildcard(true)
client:connect()
end
elseif not interrupt then
print("disconnected")
- break
+ break
end
end
--- /dev/null
+#!/usr/bin/env lua
+dofile("../parse_spec.lua")
+
+local readers = {
+ SliceByte = true,
+ Byte = true,
+ String = true,
+ SliceField = true,
+ Field = true,
+ Bool = true,
+ PointedThing = true,
+}
+
+local static_uses = {
+ "[3]int16",
+ "AOID"
+}
+
+local function generate(name)
+ local fnname, index, child, childfn, childtype
+ local type = name
+
+ local open = name:find("%[")
+ local clos = name:find("%]")
+
+ if open == 1 then
+ index = name:sub(open + 1, clos - 1)
+ child = name:sub(clos + 1)
+ childfn, childtype = generate(child)
+ fnname = (index == "" and "Slice" or "Vec" .. index) .. childfn
+
+ type = "[" .. index .. "]" .. childtype
+ else
+ fnname = camel_case(name)
+
+ local c = name:sub(1, 1)
+ if c == c:upper() then
+ type = "mt." .. name
+ end
+ end
+
+ if not readers[fnname] then
+ local fun = "func read" .. fnname .. "(l *lua.LState, val lua.LValue, ptr *" .. type .. ") {\n"
+
+ if child then
+ fun = fun .. "\tif val.Type() != lua.LTTable {\n\t\tpanic(\"invalid value for "
+ .. name .. ": must be a table\")\n\t}\n"
+
+ if index == "" then
+ fun = fun ..
+[[
+ tbl := val.(*lua.LTable)
+ n := tbl.MaxN()
+ *ptr = make(]] .. type .. [[, n)
+ for i := range *ptr {
+ read]] .. childfn .. [[(l, l.RawGetInt(tbl, i+1), &(*ptr)[i])
+ }
+]]
+ else
+ local n = tonumber(index)
+ for i, v in ipairs({"x", "y", "z"}) do
+ if i > n then
+ break
+ end
+
+ fun = fun
+ .. "\tread" .. childfn
+ .. "(l, l.GetField(val, \"" .. v .. "\"), &(*ptr)[" .. (i - 1) .. "])\n"
+ end
+ end
+ else
+ fun = fun .. "\tif val.Type() != lua.LTNumber {\n\t\tpanic(\"invalid value for "
+ .. name .. ": must be a number\")\n\t}\n"
+ .. "\t*ptr = " .. type .. "(val.(lua.LNumber))\n"
+ end
+
+ fun = fun .. "}\n\n"
+
+ readers[fnname] = fun
+ end
+
+ return fnname, type
+end
+
+for _, use in ipairs(static_uses) do
+ generate(use)
+end
+
+local function signature(name, prefix, type)
+ local camel = camel_case(name)
+ return "func read" .. camel .. "(l *lua.LState, val lua.LValue, ptr *" .. prefix .. camel .. ") {\n"
+end
+
+for name, fields in spairs(parse_spec("server/enum")) do
+ local camel = camel_case(name)
+ local fun = signature(name, "mt.")
+
+ local impl = ""
+ for _, var in ipairs(fields) do
+ local equals = "*ptr = mt." .. apply_prefix(fields, var) .. "\n"
+
+ if var == "no" then
+ fun = fun .. "\tif val.Type() == lua.LTNil {\n\t\t" .. equals .. "\t\treturn\n\t}\n"
+ else
+ impl = impl .. "\tcase \"" .. var .. "\":\n\t\t" .. equals
+ end
+ end
+
+ fun = fun
+ .. "\tif val.Type() != lua.LTString {\n\t\tpanic(\"invalid value for "
+ .. camel .. ": must be a string\")\n\t}\n"
+ .. "\tstr := string(val.(lua.LString))\n"
+ .. "\tswitch str {\n" .. impl
+ .. "\tdefault:\n\t\tpanic(\"invalid value for " .. name .. ": \" + str)\n\t}\n}\n\n"
+
+ readers[camel] = fun
+end
+
+for name, fields in spairs(parse_spec("server/flag")) do
+ local camel = camel_case(name)
+ local fun = signature(name, "mt.")
+ .. "\tif val.Type() != lua.LTTable {\n\t\tpanic(\"invalid value for "
+ .. camel .. ": must be a table\")\n\t}\n"
+
+ for _, var in ipairs(fields) do
+ fun = fun .. "\tif l.GetField(val, \"" .. var .. "\") == lua.LTrue {\n"
+ .. "\t\t*ptr = *ptr | mt." .. apply_prefix(fields, var) .. "\n\t}\n"
+ end
+
+ fun = fun .. "}\n\n"
+ readers[camel] = fun
+end
+
+local function fields_fromlua(fields, indent)
+ local impl = ""
+
+ for name, type in spairs(fields) do
+ impl = impl .. indent .. "read" .. generate(type) .. "(l, l.GetField(val, \"" .. name .. "\"), &ptr."
+ .. camel_case(name) .. ")\n"
+ end
+
+ return impl
+end
+
+for name, fields in spairs(parse_spec("server/struct", true)) do
+ local camel = camel_case(name)
+ readers[camel] = signature(name, "mt.")
+ .. "\tif val.Type() != lua.LTTable {\n"
+ .. "\t\tpanic(\"invalid value for " .. camel .. ": must be a table\")\n\t}\n"
+ .. fields_fromlua(fields, "\t")
+ .. "}\n\n"
+end
+
+local pkt_impl = ""
+
+for name, fields in spairs(parse_spec("server/pkt", true)) do
+ pkt_impl = pkt_impl
+ .. "\tcase \"" .. name .. "\"" .. "" .. ":\n"
+ .. "\t\tptr := &mt.ToSrv" .. camel_case(name) .. "{}\n"
+
+ if next(fields) then
+ pkt_impl = pkt_impl
+ .. "\t\tval := l.CheckTable(3)\n"
+ .. fields_fromlua(fields, "\t\t")
+ end
+
+ pkt_impl = pkt_impl
+ .. "\t\treturn ptr\n"
+end
+
+local funcs = ""
+for _, fn in spairs(readers) do
+ if type(fn) == "string" then
+ funcs = funcs .. fn
+ end
+end
+
+local f = io.open("generated.go", "w")
+f:write([[
+// generated by generate.lua, DO NOT EDIT
+package fromlua
+
+import (
+ "github.com/anon55555/mt"
+ "github.com/yuin/gopher-lua"
+)
+
+]] .. funcs .. [[
+func Cmd(l *lua.LState) mt.Cmd {
+ str := l.CheckString(2)
+ switch str {
+]] .. pkt_impl .. [[
+ }
+
+ panic("invalid packet type: " + str)
+}
+]])
+f:close()
--- /dev/null
+// generated by generate.lua, DO NOT EDIT
+package fromlua
+
+import (
+ "github.com/anon55555/mt"
+ "github.com/yuin/gopher-lua"
+)
+
+func readAOID(l *lua.LState, val lua.LValue, ptr *mt.AOID) {
+ if val.Type() != lua.LTNumber {
+ panic("invalid value for AOID: must be a number")
+ }
+ *ptr = mt.AOID(val.(lua.LNumber))
+}
+
+func readCompressionModes(l *lua.LState, val lua.LValue, ptr *mt.CompressionModes) {
+ if val.Type() != lua.LTNumber {
+ panic("invalid value for CompressionModes: must be a number")
+ }
+ *ptr = mt.CompressionModes(val.(lua.LNumber))
+}
+
+func readInt16(l *lua.LState, val lua.LValue, ptr *int16) {
+ if val.Type() != lua.LTNumber {
+ panic("invalid value for int16: must be a number")
+ }
+ *ptr = int16(val.(lua.LNumber))
+}
+
+func readInt32(l *lua.LState, val lua.LValue, ptr *int32) {
+ if val.Type() != lua.LTNumber {
+ panic("invalid value for int32: must be a number")
+ }
+ *ptr = int32(val.(lua.LNumber))
+}
+
+func readInteraction(l *lua.LState, val lua.LValue, ptr *mt.Interaction) {
+ if val.Type() != lua.LTString {
+ panic("invalid value for Interaction: must be a string")
+ }
+ str := string(val.(lua.LString))
+ switch str {
+ case "dig":
+ *ptr = mt.Dig
+ case "stop_digging":
+ *ptr = mt.StopDigging
+ case "dug":
+ *ptr = mt.Dug
+ case "place":
+ *ptr = mt.Place
+ case "use":
+ *ptr = mt.Use
+ case "activate":
+ *ptr = mt.Activate
+ default:
+ panic("invalid value for interaction: " + str)
+ }
+}
+
+func readKeys(l *lua.LState, val lua.LValue, ptr *mt.Keys) {
+ if val.Type() != lua.LTTable {
+ panic("invalid value for Keys: must be a table")
+ }
+ if l.GetField(val, "forward") == lua.LTrue {
+ *ptr = *ptr | mt.ForwardKey
+ }
+ if l.GetField(val, "backward") == lua.LTrue {
+ *ptr = *ptr | mt.BackwardKey
+ }
+ if l.GetField(val, "left") == lua.LTrue {
+ *ptr = *ptr | mt.LeftKey
+ }
+ if l.GetField(val, "right") == lua.LTrue {
+ *ptr = *ptr | mt.RightKey
+ }
+ if l.GetField(val, "jump") == lua.LTrue {
+ *ptr = *ptr | mt.JumpKey
+ }
+ if l.GetField(val, "special") == lua.LTrue {
+ *ptr = *ptr | mt.SpecialKey
+ }
+ if l.GetField(val, "sneak") == lua.LTrue {
+ *ptr = *ptr | mt.SneakKey
+ }
+ if l.GetField(val, "dig") == lua.LTrue {
+ *ptr = *ptr | mt.DigKey
+ }
+ if l.GetField(val, "place") == lua.LTrue {
+ *ptr = *ptr | mt.PlaceKey
+ }
+ if l.GetField(val, "zoom") == lua.LTrue {
+ *ptr = *ptr | mt.ZoomKey
+ }
+}
+
+func readPlayerPos(l *lua.LState, val lua.LValue, ptr *mt.PlayerPos) {
+ if val.Type() != lua.LTTable {
+ panic("invalid value for PlayerPos: must be a table")
+ }
+ readUint8(l, l.GetField(val, "fov80"), &ptr.FOV80)
+ readKeys(l, l.GetField(val, "keys"), &ptr.Keys)
+ readInt32(l, l.GetField(val, "pitch100"), &ptr.Pitch100)
+ readVec3Int32(l, l.GetField(val, "pos100"), &ptr.Pos100)
+ readVec3Int32(l, l.GetField(val, "vel100"), &ptr.Vel100)
+ readUint8(l, l.GetField(val, "wanted_range"), &ptr.WantedRange)
+ readInt32(l, l.GetField(val, "yaw100"), &ptr.Yaw100)
+}
+
+func readSliceSoundID(l *lua.LState, val lua.LValue, ptr *[]mt.SoundID) {
+ if val.Type() != lua.LTTable {
+ panic("invalid value for []SoundID: must be a table")
+ }
+ tbl := val.(*lua.LTable)
+ n := tbl.MaxN()
+ *ptr = make([]mt.SoundID, n)
+ for i := range *ptr {
+ readSoundID(l, l.RawGetInt(tbl, i+1), &(*ptr)[i])
+ }
+}
+
+func readSliceString(l *lua.LState, val lua.LValue, ptr *[]string) {
+ if val.Type() != lua.LTTable {
+ panic("invalid value for []string: must be a table")
+ }
+ tbl := val.(*lua.LTable)
+ n := tbl.MaxN()
+ *ptr = make([]string, n)
+ for i := range *ptr {
+ readString(l, l.RawGetInt(tbl, i+1), &(*ptr)[i])
+ }
+}
+
+func readSliceVec3Int16(l *lua.LState, val lua.LValue, ptr *[][3]int16) {
+ if val.Type() != lua.LTTable {
+ panic("invalid value for [][3]int16: must be a table")
+ }
+ tbl := val.(*lua.LTable)
+ n := tbl.MaxN()
+ *ptr = make([][3]int16, n)
+ for i := range *ptr {
+ readVec3Int16(l, l.RawGetInt(tbl, i+1), &(*ptr)[i])
+ }
+}
+
+func readSoundID(l *lua.LState, val lua.LValue, ptr *mt.SoundID) {
+ if val.Type() != lua.LTNumber {
+ panic("invalid value for SoundID: must be a number")
+ }
+ *ptr = mt.SoundID(val.(lua.LNumber))
+}
+
+func readUint16(l *lua.LState, val lua.LValue, ptr *uint16) {
+ if val.Type() != lua.LTNumber {
+ panic("invalid value for uint16: must be a number")
+ }
+ *ptr = uint16(val.(lua.LNumber))
+}
+
+func readUint8(l *lua.LState, val lua.LValue, ptr *uint8) {
+ if val.Type() != lua.LTNumber {
+ panic("invalid value for uint8: must be a number")
+ }
+ *ptr = uint8(val.(lua.LNumber))
+}
+
+func readVec3Int16(l *lua.LState, val lua.LValue, ptr *[3]int16) {
+ if val.Type() != lua.LTTable {
+ panic("invalid value for [3]int16: must be a table")
+ }
+ readInt16(l, l.GetField(val, "x"), &(*ptr)[0])
+ readInt16(l, l.GetField(val, "y"), &(*ptr)[1])
+ readInt16(l, l.GetField(val, "z"), &(*ptr)[2])
+}
+
+func readVec3Int32(l *lua.LState, val lua.LValue, ptr *[3]int32) {
+ if val.Type() != lua.LTTable {
+ panic("invalid value for [3]int32: must be a table")
+ }
+ readInt32(l, l.GetField(val, "x"), &(*ptr)[0])
+ readInt32(l, l.GetField(val, "y"), &(*ptr)[1])
+ readInt32(l, l.GetField(val, "z"), &(*ptr)[2])
+}
+
+func Cmd(l *lua.LState) mt.Cmd {
+ str := l.CheckString(2)
+ switch str {
+ case "chat_msg":
+ ptr := &mt.ToSrvChatMsg{}
+ val := l.CheckTable(3)
+ readString(l, l.GetField(val, "msg"), &ptr.Msg)
+ return ptr
+ case "clt_ready":
+ ptr := &mt.ToSrvCltReady{}
+ val := l.CheckTable(3)
+ readUint16(l, l.GetField(val, "formspec"), &ptr.Formspec)
+ readUint8(l, l.GetField(val, "major"), &ptr.Major)
+ readUint8(l, l.GetField(val, "minor"), &ptr.Minor)
+ readUint8(l, l.GetField(val, "patch"), &ptr.Patch)
+ readString(l, l.GetField(val, "version"), &ptr.Version)
+ return ptr
+ case "deleted_blks":
+ ptr := &mt.ToSrvDeletedBlks{}
+ val := l.CheckTable(3)
+ readSliceVec3Int16(l, l.GetField(val, "blks"), &ptr.Blks)
+ return ptr
+ case "fall_dmg":
+ ptr := &mt.ToSrvFallDmg{}
+ val := l.CheckTable(3)
+ readUint16(l, l.GetField(val, "amount"), &ptr.Amount)
+ return ptr
+ case "first_srp":
+ ptr := &mt.ToSrvFirstSRP{}
+ val := l.CheckTable(3)
+ readBool(l, l.GetField(val, "empty_passwd"), &ptr.EmptyPasswd)
+ readSliceByte(l, l.GetField(val, "salt"), &ptr.Salt)
+ readSliceByte(l, l.GetField(val, "verifier"), &ptr.Verifier)
+ return ptr
+ case "got_blks":
+ ptr := &mt.ToSrvGotBlks{}
+ val := l.CheckTable(3)
+ readSliceVec3Int16(l, l.GetField(val, "blks"), &ptr.Blks)
+ return ptr
+ case "init":
+ ptr := &mt.ToSrvInit{}
+ val := l.CheckTable(3)
+ readUint16(l, l.GetField(val, "max_proto_ver"), &ptr.MaxProtoVer)
+ readUint16(l, l.GetField(val, "min_proto_ver"), &ptr.MinProtoVer)
+ readString(l, l.GetField(val, "player_name"), &ptr.PlayerName)
+ readBool(l, l.GetField(val, "send_full_item_meta"), &ptr.SendFullItemMeta)
+ readUint8(l, l.GetField(val, "serialize_ver"), &ptr.SerializeVer)
+ readCompressionModes(l, l.GetField(val, "supported_compression"), &ptr.SupportedCompression)
+ return ptr
+ case "init2":
+ ptr := &mt.ToSrvInit2{}
+ val := l.CheckTable(3)
+ readString(l, l.GetField(val, "lang"), &ptr.Lang)
+ return ptr
+ case "interact":
+ ptr := &mt.ToSrvInteract{}
+ val := l.CheckTable(3)
+ readInteraction(l, l.GetField(val, "action"), &ptr.Action)
+ readUint16(l, l.GetField(val, "item_slot"), &ptr.ItemSlot)
+ readPointedThing(l, l.GetField(val, "pointed"), &ptr.Pointed)
+ readPlayerPos(l, l.GetField(val, "pos"), &ptr.Pos)
+ return ptr
+ case "inv_action":
+ ptr := &mt.ToSrvInvAction{}
+ val := l.CheckTable(3)
+ readString(l, l.GetField(val, "action"), &ptr.Action)
+ return ptr
+ case "inv_fields":
+ ptr := &mt.ToSrvInvFields{}
+ val := l.CheckTable(3)
+ readSliceField(l, l.GetField(val, "fields"), &ptr.Fields)
+ readString(l, l.GetField(val, "formname"), &ptr.Formname)
+ return ptr
+ case "join_mod_chan":
+ ptr := &mt.ToSrvJoinModChan{}
+ val := l.CheckTable(3)
+ readString(l, l.GetField(val, "channel"), &ptr.Channel)
+ return ptr
+ case "leave_mod_chan":
+ ptr := &mt.ToSrvLeaveModChan{}
+ val := l.CheckTable(3)
+ readString(l, l.GetField(val, "channel"), &ptr.Channel)
+ return ptr
+ case "msg_mod_chan":
+ ptr := &mt.ToSrvMsgModChan{}
+ val := l.CheckTable(3)
+ readString(l, l.GetField(val, "channel"), &ptr.Channel)
+ readString(l, l.GetField(val, "msg"), &ptr.Msg)
+ return ptr
+ case "nil":
+ ptr := &mt.ToSrvNil{}
+ return ptr
+ case "node_meta_fields":
+ ptr := &mt.ToSrvNodeMetaFields{}
+ val := l.CheckTable(3)
+ readSliceField(l, l.GetField(val, "fields"), &ptr.Fields)
+ readString(l, l.GetField(val, "formname"), &ptr.Formname)
+ readVec3Int16(l, l.GetField(val, "pos"), &ptr.Pos)
+ return ptr
+ case "player_pos":
+ ptr := &mt.ToSrvPlayerPos{}
+ val := l.CheckTable(3)
+ readPlayerPos(l, l.GetField(val, "pos"), &ptr.Pos)
+ return ptr
+ case "removed_sounds":
+ ptr := &mt.ToSrvRemovedSounds{}
+ val := l.CheckTable(3)
+ readSliceSoundID(l, l.GetField(val, "ids"), &ptr.IDs)
+ return ptr
+ case "req_media":
+ ptr := &mt.ToSrvReqMedia{}
+ val := l.CheckTable(3)
+ readSliceString(l, l.GetField(val, "filenames"), &ptr.Filenames)
+ return ptr
+ case "respawn":
+ ptr := &mt.ToSrvRespawn{}
+ return ptr
+ case "select_item":
+ ptr := &mt.ToSrvSelectItem{}
+ val := l.CheckTable(3)
+ readUint16(l, l.GetField(val, "slot"), &ptr.Slot)
+ return ptr
+ case "srp_bytes_a":
+ ptr := &mt.ToSrvSRPBytesA{}
+ val := l.CheckTable(3)
+ readSliceByte(l, l.GetField(val, "a"), &ptr.A)
+ readBool(l, l.GetField(val, "no_sha1"), &ptr.NoSHA1)
+ return ptr
+ case "srp_bytes_m":
+ ptr := &mt.ToSrvSRPBytesM{}
+ val := l.CheckTable(3)
+ readSliceByte(l, l.GetField(val, "m"), &ptr.M)
+ return ptr
+ }
+
+ panic("invalid packet type: " + str)
+}
--- /dev/null
+package fromlua
+
+import (
+ "github.com/anon55555/mt"
+ "github.com/yuin/gopher-lua"
+)
+
+//go:generate ./generate.lua
+
+func readBool(l *lua.LState, val lua.LValue, ptr *bool) {
+ if val.Type() != lua.LTBool {
+ panic("invalid value for bool: must be a boolean")
+ }
+ *ptr = bool(val.(lua.LBool))
+}
+
+func readString(l *lua.LState, val lua.LValue, ptr *string) {
+ if val.Type() != lua.LTString {
+ panic("invalid value for string: must be a string")
+ }
+ *ptr = string(val.(lua.LString))
+}
+
+func readSliceByte(l *lua.LState, val lua.LValue, ptr *[]byte) {
+ if val.Type() != lua.LTString {
+ panic("invalid value for []byte: must be a string")
+ }
+ *ptr = []byte(val.(lua.LString))
+}
+
+func readSliceField(l *lua.LState, val lua.LValue, ptr *[]mt.Field) {
+ if val.Type() != lua.LTTable {
+ panic("invalid value for []Field: must be a table")
+ }
+ val.(*lua.LTable).ForEach(func(k, v lua.LValue) {
+ if k.Type() != lua.LTString || v.Type() != lua.LTString {
+ panic("invalid value for Field: key and value must be strings")
+ }
+ *ptr = append(*ptr, mt.Field{Name: string(k.(lua.LString)), Value: string(v.(lua.LString))})
+ })
+}
+
+func readPointedThing(l *lua.LState, val lua.LValue, ptr *mt.PointedThing) {
+ if val.Type() != lua.LTTable {
+ panic("invalid value for PointedThing: must be a table")
+ }
+ id := l.GetField(val, "id")
+
+ if id == lua.LNil {
+ pt := &mt.PointedAO{}
+ readAOID(l, id, &(*pt).ID)
+ *ptr = pt
+ } else {
+ pt := &mt.PointedNode{}
+ readVec3Int16(l, l.GetField(val, "under"), &(*pt).Under)
+ readVec3Int16(l, l.GetField(val, "above"), &(*pt).Above)
+ *ptr = pt
+ }
+}
//go:embed builtin/escapes.lua
var builtinEscapes string
+//go:embed builtin/client.lua
+var builtinClient string
+
var builtinFiles = []string{
builtinLuaX,
builtinVector,
builtinEscapes,
+ builtinClient,
}
var hydraFuncs = map[string]lua.LGFunction{
end
end
table.sort(t.__sorted)
-
+
key = t.__sorted[1]
else
for i, v in ipairs(t.__sorted) do
if idx then
local first, second = pair:sub(1, idx - 1), pair:sub(idx + 1)
- if value_first and first:sub(1, 1) ~= "[" then
+ if value_first and first:sub(1, 1) ~= "{" then
return second, first
else
return first, second
return camel
end
+function apply_prefix(fields, str)
+ return (fields.prefix or "") .. camel_case(str) .. (fields.postfix or "")
+end
first_srp FirstSRP
csm_restriction_flags CSMRestrictionFlags
srp_bytes_salt_b SRPBytesSaltB
+srp_bytes_a SRPBytesA
+srp_bytes_m SRPBytesM
no_csms NoCSMs
join_ok JoinOK
leave_ok LeaveOK
+no_sha1 NoSHA1
+fov80 FOV80
+ids IDs
string username
accept_auth
vec3 player_pos
- # int64
number map_seed
number send_interval
auth_methods sudo_auth_methods
deny_sudo_mode
kick
kick_reason reason
- [custom] val.Reason == mt.Custom || val.Reason == mt.Shutdown || val.Reason == mt.Crash
+ {custom} val.Reason == mt.Custom || val.Reason == mt.Shutdown || val.Reason == mt.Crash
string custom
- [reconnect] val.Reason == mt.Shutdown || val.Reason == mt.Crash
+ {reconnect} val.Reason == mt.Shutdown || val.Reason == mt.Crash
boolean reconnect
blk_data
vec3 blkpos
chat_msg_type type
string sender
string text
- # int64
number timestamp
ao_rm_add
# TODO
change_hud
number 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
+ {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
color sun_fog_tint
color moon_fog_tint
string fog_tint_type
- [textures] val.Type == "skybox"
+ {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"
+ {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
--- /dev/null
+interaction
+ dig
+ stop_digging
+ dug
+ place
+ use
+ activate
--- /dev/null
+keys
+ postfix Key
+ forward
+ backward
+ left
+ right
+ jump
+ special
+ sneak
+ dig
+ place
+ zoom
--- /dev/null
+nil
+init
+ uint8 serialize_ver
+ CompressionModes supported_compression
+ uint16 min_proto_ver
+ uint16 max_proto_ver
+ string player_name
+ bool send_full_item_meta
+init2
+ string lang
+join_mod_chan
+ string channel
+leave_mod_chan
+ string channel
+msg_mod_chan
+ string channel
+ string msg
+player_pos
+ PlayerPos pos
+got_blks
+ [][3]int16 blks
+deleted_blks
+ [][3]int16 blks
+inv_action
+ string action
+chat_msg
+ string msg
+fall_dmg
+ uint16 amount
+select_item
+ uint16 slot
+respawn
+interact
+ Interaction action
+ uint16 item_slot
+ PointedThing pointed
+ PlayerPos pos
+removed_sounds
+ []SoundID ids
+node_meta_fields
+ [3]int16 pos
+ string formname
+ []Field fields
+inv_fields
+ string formname
+ []Field fields
+req_media
+ []string filenames
+clt_ready
+ uint8 major
+ uint8 minor
+ uint8 patch
+ string version
+ uint16 formspec
+first_srp
+ []byte salt
+ []byte verifier
+ bool empty_passwd
+srp_bytes_a
+ []byte a
+ bool no_sha1
+srp_bytes_m
+ []byte m
--- /dev/null
+player_pos
+ [3]int32 pos100
+ [3]int32 vel100
+ int32 pitch100
+ int32 yaw100
+ Keys keys
+ uint8 fov80
+ uint8 wanted_range
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"
+ funcs = funcs .. "\tcase mt." .. apply_prefix(fields, var) .. ":\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"
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 "")
+ funcs = funcs .. "\tif val&mt." .. apply_prefix(fields, var)
.. " != 0 {\n\t\tl.SetField(tbl, \"" .. var .. "\", lua.LTrue)\n\t}\n"
end
funcs = funcs .. "\treturn tbl\n}\n\n"
end
-local to_lua = {
+local tolua = {
string = "lua.LString(string(VAL))",
fixed_string = "lua.LString(string(VAL[:]))",
boolean = "lua.LBool(VAL)",
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 function fields_tolua(fields, indent)
local impl = ""
-
+
for name, type in spairs(fields) do
- if name:sub(1, 1) ~= "[" then
+ if name:sub(1, 1) ~= "{" then
local camel = "val." .. camel_case(name)
local idt = indent
- local condition = fields["[" .. name .. "]"]
+ local condition = fields["{" .. name .. "}"]
if condition then
- impl = impl .. indent .. "if " .. condition .. " {\n"
+ 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)
+ if tolua[type] then
+ impl = impl .. tolua[type]:gsub("VAL", camel)
else
impl = impl .. camel_case(type) .. "(l, " .. camel .. ")"
end
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")
+ .. fields_tolua(fields, "\t")
.. "\treturn tbl\n}\n\n"
end
-local to_string_impl = ""
-local to_lua_impl = ""
+local pkt_type_impl = ""
+local pkt_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
+ pkt_type_impl = pkt_type_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")
+ pkt_impl = pkt_impl .. case .. fields_tolua(fields, "\t\t")
end
end
]] .. funcs .. [[
func PktType(pkt *mt.Pkt) lua.LString {
switch pkt.Cmd.(type) {
-]] .. to_string_impl .. [[
+]] .. pkt_type_impl .. [[
}
panic("impossible")
return ""
tbl := l.NewTable()
l.SetField(tbl, "_type", PktType(pkt))
switch val := pkt.Cmd.(type) {
-]] .. to_lua_impl .. [[
+]] .. pkt_impl .. [[
}
return tbl
}