+++ /dev/null
-package main
-
-import (
- "github.com/HimbeerserverDE/srp"
- "github.com/anon55555/mt"
- "github.com/dragonfireclient/hydra-dragonfire/convert"
- "github.com/yuin/gopher-lua"
- "strings"
- "time"
-)
-
-type authState uint8
-
-const (
- asInit authState = iota
- asRequested
- asVerified
- asActive
- asError
-)
-
-type Auth struct {
- client *Client
- username string
- password string
- language string
- version string
- state authState
- err string
- srpBytesA, bytesA []byte
- userdata *lua.LUserData
-}
-
-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 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() lua.LValue {
- return auth.userdata
-}
-
-func (auth *Auth) connect() {
- if auth.username == "" {
- panic("missing username")
- }
-
- go func() {
- for auth.client.state == csConnected && auth.state == asInit {
- auth.client.conn.SendCmd(&mt.ToSrvInit{
- SerializeVer: serializeVer,
- MinProtoVer: protoVer,
- MaxProtoVer: protoVer,
- PlayerName: auth.username,
- })
- time.Sleep(500 * time.Millisecond)
- }
- }()
-}
-
-func (auth *Auth) fail(err string) {
- auth.err = err
- auth.state = asError
- auth.client.closeConn()
-}
-
-func (auth *Auth) checkState(state authState, pkt *mt.Pkt) bool {
- if auth.state == state {
- return true
- }
-
- auth.fail("received " + string(convert.PushPktType(pkt)) + " in invalid state")
- return false
-}
-
-func (auth *Auth) process(pkt *mt.Pkt) {
- if auth.state == asError {
- return
- }
-
- switch cmd := pkt.Cmd.(type) {
- case *mt.ToCltHello:
- if !auth.checkState(asInit, pkt) {
- return
- }
-
- if cmd.SerializeVer != 28 {
- 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.fail(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.fail(err.Error())
- return
- }
-
- auth.client.conn.SendCmd(&mt.ToSrvSRPBytesA{
- A: auth.srpBytesA,
- NoSHA1: true,
- })
- auth.state = asRequested
- } else {
- auth.fail("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.fail(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.fail("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: auth.version,
- })
- auth.state = asActive
- }
-}
-
-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")
- }
- *ptr = string(str)
- return 0
- } else {
- l.Push(lua.LString(*ptr))
- return 1
- }
-}
-
-func l_auth_username(l *lua.LState) int {
- auth := getAuth(l)
- return auth.accessProperty(l, "username", &auth.username)
-}
-
-func l_auth_password(l *lua.LState) int {
- auth := getAuth(l)
- return auth.accessProperty(l, "password", &auth.password)
-}
-
-func l_auth_language(l *lua.LState) int {
- auth := getAuth(l)
- return auth.accessProperty(l, "language", &auth.language)
-}
-
-func l_auth_version(l *lua.LState) int {
- auth := getAuth(l)
- return auth.accessProperty(l, "version", &auth.version)
-}
-
-func l_auth_state(l *lua.LState) int {
- auth := getAuth(l)
-
- switch auth.state {
- case asInit:
- l.Push(lua.LString("init"))
- case asRequested:
- l.Push(lua.LString("requested"))
- case asVerified:
- l.Push(lua.LString("verified"))
- case asActive:
- l.Push(lua.LString("active"))
- case asError:
- l.Push(lua.LString("error"))
- l.Push(lua.LString(auth.err))
- return 2
- }
-
- return 1
-}
if err == nil {
client.mu.Lock()
- for _, component := range client.components {
- component.process(&pkt)
+ for _, comp := range client.components {
+ comp.process(&pkt)
}
client.mu.Unlock()
} else if errors.Is(err, net.ErrClosed) {
}()
client.mu.Lock()
- for _, component := range client.components {
- component.connect()
+ for _, comp := range client.components {
+ comp.connect()
}
client.mu.Unlock()
defer client.mu.Unlock()
for i := 2; i <= n; i++ {
- compname := l.CheckString(i)
+ name := l.CheckString(i)
- if component, exists := client.components[compname]; !exists {
- switch compname {
+ if comp, exists := client.components[name]; !exists {
+ switch name {
case "auth":
- component = &Auth{}
+ comp = &CompAuth{}
case "map":
- component = &Map{}
+ comp = &CompMap{}
case "pkts":
- component = &Pkts{}
+ comp = &CompPkts{}
default:
- panic("invalid component: " + compname)
+ panic("invalid component: " + name)
}
- client.components[compname] = component
- component.create(client, l)
+ client.components[name] = comp
+ comp.create(client, l)
}
}
--- /dev/null
+package main
+
+import (
+ "github.com/HimbeerserverDE/srp"
+ "github.com/anon55555/mt"
+ "github.com/dragonfireclient/hydra-dragonfire/convert"
+ "github.com/yuin/gopher-lua"
+ "strings"
+ "time"
+)
+
+type authState uint8
+
+const (
+ asInit authState = iota
+ asRequested
+ asVerified
+ asActive
+ asError
+)
+
+type CompAuth struct {
+ client *Client
+ username string
+ password string
+ language string
+ version string
+ state authState
+ err string
+ srpBytesA, bytesA []byte
+ userdata *lua.LUserData
+}
+
+var compAuthFuncs = map[string]lua.LGFunction{
+ "username": l_comp_auth_username,
+ "password": l_comp_auth_password,
+ "language": l_comp_auth_language,
+ "version": l_comp_auth_version,
+ "state": l_comp_auth_state,
+}
+
+func getCompAuth(l *lua.LState) *CompAuth {
+ return l.CheckUserData(1).Value.(*CompAuth)
+}
+
+func (comp *CompAuth) create(client *Client, l *lua.LState) {
+ if client.state != csNew {
+ panic("can't add auth component after connect")
+ }
+
+ comp.client = client
+ comp.language = "en_US"
+ comp.version = "hydra-dragonfire"
+ comp.state = asInit
+ comp.userdata = l.NewUserData()
+ comp.userdata.Value = comp
+ l.SetMetatable(comp.userdata, l.GetTypeMetatable("hydra.comp.auth"))
+}
+
+func (comp *CompAuth) push() lua.LValue {
+ return comp.userdata
+}
+
+func (comp *CompAuth) connect() {
+ if comp.username == "" {
+ panic("missing username")
+ }
+
+ go func() {
+ for comp.client.state == csConnected && comp.state == asInit {
+ comp.client.conn.SendCmd(&mt.ToSrvInit{
+ SerializeVer: serializeVer,
+ MinProtoVer: protoVer,
+ MaxProtoVer: protoVer,
+ PlayerName: comp.username,
+ })
+ time.Sleep(500 * time.Millisecond)
+ }
+ }()
+}
+
+func (comp *CompAuth) fail(err string) {
+ comp.err = err
+ comp.state = asError
+ comp.client.closeConn()
+}
+
+func (comp *CompAuth) checkState(state authState, pkt *mt.Pkt) bool {
+ if comp.state == state {
+ return true
+ }
+
+ comp.fail("received " + string(convert.PushPktType(pkt)) + " in invalid state")
+ return false
+}
+
+func (comp *CompAuth) process(pkt *mt.Pkt) {
+ if comp.state == asError {
+ return
+ }
+
+ switch cmd := pkt.Cmd.(type) {
+ case *mt.ToCltHello:
+ if !comp.checkState(asInit, pkt) {
+ return
+ }
+
+ if cmd.SerializeVer != 28 {
+ comp.fail("unsupported serialize version")
+ return
+ }
+
+ if cmd.AuthMethods == mt.FirstSRP {
+ salt, verifier, err := srp.NewClient([]byte(strings.ToLower(comp.username)), []byte(comp.password))
+ if err != nil {
+ comp.fail(err.Error())
+ return
+ }
+
+ comp.client.conn.SendCmd(&mt.ToSrvFirstSRP{
+ Salt: salt,
+ Verifier: verifier,
+ EmptyPasswd: comp.password == "",
+ })
+ comp.state = asVerified
+ } else if cmd.AuthMethods == mt.SRP {
+ var err error
+ comp.srpBytesA, comp.bytesA, err = srp.InitiateHandshake()
+ if err != nil {
+ comp.fail(err.Error())
+ return
+ }
+
+ comp.client.conn.SendCmd(&mt.ToSrvSRPBytesA{
+ A: comp.srpBytesA,
+ NoSHA1: true,
+ })
+ comp.state = asRequested
+ } else {
+ comp.fail("invalid auth methods")
+ return
+ }
+
+ case *mt.ToCltSRPBytesSaltB:
+ if !comp.checkState(asRequested, pkt) {
+ return
+ }
+
+ srpBytesK, err := srp.CompleteHandshake(comp.srpBytesA, comp.bytesA, []byte(strings.ToLower(comp.username)), []byte(comp.password), cmd.Salt, cmd.B)
+ if err != nil {
+ comp.fail(err.Error())
+ return
+ }
+
+ M := srp.ClientProof([]byte(comp.username), cmd.Salt, comp.srpBytesA, cmd.B, srpBytesK)
+ comp.srpBytesA = []byte{}
+ comp.bytesA = []byte{}
+
+ if M == nil {
+ comp.fail("srp safety check fail")
+ return
+ }
+
+ comp.client.conn.SendCmd(&mt.ToSrvSRPBytesM{
+ M: M,
+ })
+ comp.state = asVerified
+
+ case *mt.ToCltAcceptAuth:
+ comp.client.conn.SendCmd(&mt.ToSrvInit2{Lang: comp.language})
+
+ case *mt.ToCltTimeOfDay:
+ if comp.state == asActive {
+ return
+ }
+
+ if !comp.checkState(asVerified, pkt) {
+ return
+ }
+
+ comp.client.conn.SendCmd(&mt.ToSrvCltReady{
+ Major: 5,
+ Minor: 6,
+ Patch: 0,
+ Reserved: 0,
+ Formspec: 4,
+ Version: comp.version,
+ })
+ comp.state = asActive
+ }
+}
+
+func (comp *CompAuth) accessProperty(l *lua.LState, key string, ptr *string) int {
+ if str, ok := l.Get(2).(lua.LString); ok {
+ if comp.client.state != csNew {
+ panic("can't change " + key + " after connecting")
+ }
+ *ptr = string(str)
+ return 0
+ } else {
+ l.Push(lua.LString(*ptr))
+ return 1
+ }
+}
+
+func l_comp_auth_username(l *lua.LState) int {
+ comp := getCompAuth(l)
+ return comp.accessProperty(l, "username", &comp.username)
+}
+
+func l_comp_auth_password(l *lua.LState) int {
+ comp := getCompAuth(l)
+ return comp.accessProperty(l, "password", &comp.password)
+}
+
+func l_comp_auth_language(l *lua.LState) int {
+ comp := getCompAuth(l)
+ return comp.accessProperty(l, "language", &comp.language)
+}
+
+func l_comp_auth_version(l *lua.LState) int {
+ comp := getCompAuth(l)
+ return comp.accessProperty(l, "version", &comp.version)
+}
+
+func l_comp_auth_state(l *lua.LState) int {
+ comp := getCompAuth(l)
+
+ switch comp.state {
+ case asInit:
+ l.Push(lua.LString("init"))
+ case asRequested:
+ l.Push(lua.LString("requested"))
+ case asVerified:
+ l.Push(lua.LString("verified"))
+ case asActive:
+ l.Push(lua.LString("active"))
+ case asError:
+ l.Push(lua.LString("error"))
+ l.Push(lua.LString(comp.err))
+ return 2
+ }
+
+ return 1
+}
--- /dev/null
+package main
+
+import (
+ "github.com/anon55555/mt"
+ "github.com/yuin/gopher-lua"
+)
+
+type CompMap struct {
+ client *Client
+ mapdata *Map
+ userdata *lua.LUserData
+}
+
+var compMapFuncs = map[string]lua.LGFunction{
+ "get": l_comp_map_get,
+ "set": l_comp_map_set,
+}
+
+func getCompMap(l *lua.LState) *CompMap {
+ return l.CheckUserData(1).Value.(*CompMap)
+}
+
+func (comp *CompMap) create(client *Client, l *lua.LState) {
+ comp.client = client
+ comp.mapdata = newMap(l)
+ comp.userdata = l.NewUserData()
+ comp.userdata.Value = comp
+ l.SetMetatable(comp.userdata, l.GetTypeMetatable("hydra.comp.map"))
+}
+
+func (comp *CompMap) push() lua.LValue {
+ return comp.userdata
+}
+
+func (comp *CompMap) connect() {
+}
+
+func (comp *CompMap) process(pkt *mt.Pkt) {
+ comp.mapdata.process(comp.client, pkt)
+}
+
+func l_comp_map_set(l *lua.LState) int {
+ comp := getCompMap(l)
+ comp.mapdata = getMap(l, 2)
+ return 0
+}
+
+func l_comp_map_get(l *lua.LState) int {
+ comp := getCompMap(l)
+ l.Push(comp.mapdata.userdata)
+ return 1
+}
--- /dev/null
+package main
+
+import (
+ "github.com/anon55555/mt"
+ "github.com/dragonfireclient/hydra-dragonfire/convert"
+ "github.com/yuin/gopher-lua"
+ "sync"
+)
+
+type CompPkts struct {
+ client *Client
+ mu sync.Mutex
+ wildcard bool
+ subscribed map[string]struct{}
+ userdata *lua.LUserData
+}
+
+var compPktsFuncs = map[string]lua.LGFunction{
+ "subscribe": l_comp_pkts_subscribe,
+ "unsubscribe": l_comp_pkts_unsubscribe,
+ "wildcard": l_comp_pkts_wildcard,
+}
+
+type EventPkt struct {
+ pktType string
+ pktData *mt.Pkt
+}
+
+func (evt EventPkt) handle(l *lua.LState, val lua.LValue) {
+ l.SetField(val, "type", lua.LString("pkt"))
+ l.SetField(val, "pkt_type", lua.LString(evt.pktType))
+ l.SetField(val, "pkt_data", convert.PushPkt(l, evt.pktData))
+}
+
+func getCompPkts(l *lua.LState) *CompPkts {
+ return l.CheckUserData(1).Value.(*CompPkts)
+}
+
+func (comp *CompPkts) create(client *Client, l *lua.LState) {
+ comp.client = client
+ comp.wildcard = false
+ comp.subscribed = map[string]struct{}{}
+ comp.userdata = l.NewUserData()
+ comp.userdata.Value = comp
+ l.SetMetatable(comp.userdata, l.GetTypeMetatable("hydra.comp.pkts"))
+}
+
+func (comp *CompPkts) push() lua.LValue {
+ return comp.userdata
+}
+
+func (comp *CompPkts) connect() {
+}
+
+func (comp *CompPkts) process(pkt *mt.Pkt) {
+ pktType := string(convert.PushPktType(pkt))
+
+ comp.mu.Lock()
+ _, subscribed := comp.subscribed[pktType]
+ comp.mu.Unlock()
+
+ if subscribed || comp.wildcard {
+ comp.client.queue <- EventPkt{pktType: pktType, pktData: pkt}
+ }
+}
+
+func l_comp_pkts_subscribe(l *lua.LState) int {
+ comp := getCompPkts(l)
+ n := l.GetTop()
+
+ comp.mu.Lock()
+ defer comp.mu.Unlock()
+
+ for i := 2; i <= n; i++ {
+ comp.subscribed[l.CheckString(i)] = struct{}{}
+ }
+
+ return 0
+}
+
+func l_comp_pkts_unsubscribe(l *lua.LState) int {
+ comp := getCompPkts(l)
+ n := l.GetTop()
+
+ comp.mu.Lock()
+ defer comp.mu.Unlock()
+
+ for i := 2; i <= n; i++ {
+ delete(comp.subscribed, l.CheckString(i))
+ }
+
+ return 0
+}
+
+func l_comp_pkts_wildcard(l *lua.LState) int {
+ comp := getCompPkts(l)
+ comp.wildcard = l.ToBool(2)
+ return 0
+}
+++ /dev/null
-# Authentication Component
-Source code: [auth.go](../auth.go)
-
-Handles packets necessary to complete authentication and responds with according packets. Sends the `init` packet unpon connection open.
-Invalid packets related to auth received from server or detected incompabilities may result in the client being closed. In this case, an error state is set that can be read using the `self:state()` method.
-
-**Important: ** the auth component does not automatically disconnect if authentication fails due to an invalid password or already being logged in; it is up to the API user to handle these cases by subscribing to the `kick` and `legacy_kick` packets.
-
-Authentication handles the `hello`, `srp_bytes_salt_b`, `accept_auth` and `time_of_day` packets (the last one is only handled when received the first time and sets the state to active).
-
-Authentication may send `init`, `first_srp`, `srp_bytes_a`, `srp_bytes_m`, `init2` and `ready` packets.
-
-## Functions
-
-`self:username([username])`: Sets or gets the username (string). Setting may not occur after having connected the client. A username must be set before connecting.
-`self:password([password])`: Sets or gets the password (string). Setting may not occur after having connected the client. By default, an empty password is used.
-`self:language([language])`: Sets or gets the language sent to server. Setting may not occur after having connected the client. By default, "en_US" is used.
-`self:version([version])`: Sets or gets the version string sent to server. Setting may not occur after having connected the client. By default, "hydra-dragonfire" is used.
-`self:state()`: Returns `state, error`. State is one of "init", "requested", "verified", "active", "error". If state is "error", error is a string containing a description of the problem that occured. Otherwise, error is nil.
Enabled components can be accessed by using `self.<component name>`.
-- `self.pkt`: Allows you to handle selected packets yourself. Most scripts use this. See [pkts.md](pkts.md).
-- `self.auth`: Handles authentication. Recommended for the vast majority of scripts. See [auth.md](auth.md).
-- `self.map`: Stores MapBlocks received from server. See [map.md](map.md).
+- `self.pkt`: Allows you to handle selected packets yourself. Most scripts use this. See [comp_pkts.md](comp_pkts.md).
+- `self.auth`: Handles authentication. Recommended for the vast majority of scripts. See [comp_auth.md](comp_auth.md).
+- `self.map`: Stores MapBlocks received from server. See [comp_map.md](comp_map.md).
--- /dev/null
+# Authentication Component
+Source code: [comp_auth.go](../comp_auth.go)
+
+Handles packets necessary to complete authentication and responds with according packets. Sends the `init` packet unpon connection open.
+Invalid packets related to auth received from server or detected incompabilities may result in the client being closed. In this case, an error state is set that can be read using the `self:state()` method.
+
+**Important: ** the auth component does not automatically disconnect if authentication fails due to an invalid password or already being logged in; it is up to the API user to handle these cases by subscribing to the `kick` and `legacy_kick` packets.
+
+Handles the `hello`, `srp_bytes_salt_b`, `accept_auth` and `time_of_day` packets (the last one is only handled when received the first time and sets the state to active).
+May send `init`, `first_srp`, `srp_bytes_a`, `srp_bytes_m`, `init2` and `ready` packets.
+
+## Functions
+
+`self:username([username])`: Sets or gets the username (string). Setting may not occur after having connected the client. A username must be set before connecting.
+`self:password([password])`: Sets or gets the password (string). Setting may not occur after having connected the client. By default, an empty password is used.
+`self:language([language])`: Sets or gets the language sent to server. Setting may not occur after having connected the client. By default, "en_US" is used.
+`self:version([version])`: Sets or gets the version string sent to server. Setting may not occur after having connected the client. By default, "hydra-dragonfire" is used.
+`self:state()`: Returns `state, error`. State is one of "init", "requested", "verified", "active", "error". If state is "error", error is a string containing a description of the problem that occured. Otherwise, error is nil.
--- /dev/null
+# Map Component
+Source code: [comp_map.go](../comp_map.go)
+
+The Map component stores a reference to a `hydra.map` (See [map.md](map.md)).
+
+Initially, an empty map is created. You can replace this by a map reference obtained from `hydra.map` however: this way, multiple clients can share a map and explore different areas of it.
+
+Handles the `blk_data` and `node_metas_changed` packets.
+May send `got_blks` packets.
+
+## Functions
+
+`self:set(mapref)`: Data will be stored in `mapref` in the future.
+
+`self:get()`: Returns the current `mapref`.
+
--- /dev/null
+# Packets Component
+Source code: [pkts.go](../pkts.go)
+
+The packets component allows you to handle packets yourself. It fires events in the form of `{ type = "pkt", client = ..., pkt_type = "...", pkt_data = { ... } }``` when subscribed packets are received.
+For available packets, see [client_pkts.md](client_pkts.md). By default, no packets are packets subscribed.
+
+## Wildcard mode
+
+If wildcard is enabled, events for all packets are fired, even ones that are not subscribed. It is not recommended to use this without a reason since converting packets to Lua costs performance and creates and overhead due to poll returning more often. `wildcard` is unnecessary if only certain packets are handled anyway, but it is useful for traffic inspection and debugging.
+
+## Functions
+
+- `self:subscribe(pkt1, [pkt2, ...])`: Subscribes to all packet types passed as arguments (strings).
+
+- `self:unsubscribe(pkt1, [pkt2, ...])`: Unsubscribes from all packet passed as arguments (strings).
+
+- `self:wildcard(wildcard)`: Sets wildcard mode to `wildcard` (boolean).
+
## Functions
- `hydra.client(address)`: Returns a new client. Address must be a string. For client functions, see [client.md](client.md).
+- `hydra.map()`: Return a new map. For map functions, see [map.md](map.md).
- `hydra.dtime()`: Utility function that turns the elapsed time in seconds (floating point) since it was last called (or since program start).
- `hydra.poll(clients, [timeout])`: Polls events from all clients in `clients` (table). For behavior and return value, see [poll.md](poll.md).
- `hydra.close(clients)`: Closes all clients in `clients` (table) that are currently connected. See `client:close()` in [client.md](client.md) for more info.
-# Map Component
+# Map interface
Source code: [map.go](../map.go)
-Map handles the `blk_data` and `node_metas_changed` packets.
-Map may send `got_blks`, `deleted_blks` packets.
+A map stores mapblocks received from server. To be of any use, a map needs to be added to one or more clients. See [comp_map.md](comp_map.md) and [client.md](client.md).
## Functions
-`self:clear()`: Forget all blocks.
-
`self:block(blkpos)`: Return the `map_blk` at `blkpos` as found in the `blk_data` packet (See [client_pkts.md](client_pkts.md)). `nil` if block is not present.
`self:node(pos)`: Return a node in the form `{ param0 = 126, param1 = 0, param2 = 0, meta = { ... } }`. The meta field is a `node_meta` as found in the `blk_data` packet. `nil` if node is not present.
+++ /dev/null
-# Packet Handler Component
-Source code: [pkts.go](../pkts.go)
-
-The packet handler component allows you to handle packets yourself. It fires events in the form of `{ type = "pkt", client = ..., pkt_type = "...", pkt_data = { ... } }``` when subscribed packets are received.
-For available packets, see [client_pkts.md](client_pkts.md). By default, not packets are packets subscribed.
-
-## Wildcard mode
-
-If wildcard is enabled, events for all packets are fired, even ones that are not subscribed. It is not recommended to use this without a reason since converting packets to Lua costs performance and creates and overhead due to poll returning more often. `wildcard` is unnecessary if only certain packets are handled anyway, but it is useful for traffic inspection and debugging.
-
-## Functions
-
-- `self:subscribe(pkt1, [pkt2, ...])`: Subscribes to all packet types passed as arguments (strings).
-
-- `self:unsubscribe(pkt1, [pkt2, ...])`: Unsubscribes from all packet passed as arguments (strings).
-
-- `self:wildcard(wildcard)`: Sets wildcard mode to `wildcard` (boolean).
-
elseif evt.type == "pkt" then
pos = (evt.pkt_data.pos / hydra.BS + vec3(0, -1, 0)):round()
elseif evt.type == "timeout" and pos then
- local node = client.map:node(pos)
+ local node = client.map:get():node(pos)
print(pos, node and node.param0)
end
end
var hydraFuncs = map[string]lua.LGFunction{
"client": l_client,
+ "map": l_map,
"dtime": l_dtime,
"poll": l_poll,
"close": l_close,
l.SetField(hydra, "proto_ver", lua.LNumber(protoVer))
l.SetGlobal("hydra", hydra)
- l.SetField(l.NewTypeMetatable("hydra.auth"), "__index", l.SetFuncs(l.NewTable(), authFuncs))
l.SetField(l.NewTypeMetatable("hydra.client"), "__index", l.NewFunction(l_client_index))
l.SetField(l.NewTypeMetatable("hydra.map"), "__index", l.SetFuncs(l.NewTable(), mapFuncs))
- l.SetField(l.NewTypeMetatable("hydra.pkts"), "__index", l.SetFuncs(l.NewTable(), pktsFuncs))
+
+ l.SetField(l.NewTypeMetatable("hydra.comp.auth"), "__index", l.SetFuncs(l.NewTable(), compAuthFuncs))
+ l.SetField(l.NewTypeMetatable("hydra.comp.map"), "__index", l.SetFuncs(l.NewTable(), compMapFuncs))
+ l.SetField(l.NewTypeMetatable("hydra.comp.pkts"), "__index", l.SetFuncs(l.NewTable(), compPktsFuncs))
for _, str := range builtinFiles {
if err := l.DoString(str); err != nil {
)
type Map struct {
- client *Client
mu sync.Mutex
blocks map[[3]int16]*mt.MapBlk
userdata *lua.LUserData
}
var mapFuncs = map[string]lua.LGFunction{
- "clear": l_map_clear,
"block": l_map_block,
"node": l_map_node,
}
-func getMap(l *lua.LState) *Map {
- return l.CheckUserData(1).Value.(*Map)
+func getMap(l *lua.LState, idx int) *Map {
+ return l.CheckUserData(idx).Value.(*Map)
}
-func (mtmap *Map) create(client *Client, l *lua.LState) {
- mtmap.client = client
- mtmap.blocks = map[[3]int16]*mt.MapBlk{}
- mtmap.userdata = l.NewUserData()
- mtmap.userdata.Value = mtmap
- l.SetMetatable(mtmap.userdata, l.GetTypeMetatable("hydra.map"))
+func newMap(l *lua.LState) *Map {
+ mp := &Map{}
+ mp.blocks = map[[3]int16]*mt.MapBlk{}
+ mp.userdata = l.NewUserData()
+ mp.userdata.Value = mp
+ l.SetMetatable(mp.userdata, l.GetTypeMetatable("hydra.map"))
+ return mp
}
-func (mtmap *Map) push() lua.LValue {
- return mtmap.userdata
-}
-
-func (mtmap *Map) connect() {
-}
-
-func (mtmap *Map) process(pkt *mt.Pkt) {
+func (mp *Map) process(client *Client, pkt *mt.Pkt) {
switch cmd := pkt.Cmd.(type) {
case *mt.ToCltBlkData:
- mtmap.mu.Lock()
- mtmap.blocks[cmd.Blkpos] = &cmd.Blk
- mtmap.client.conn.SendCmd(&mt.ToSrvGotBlks{Blks: [][3]int16{cmd.Blkpos}})
- mtmap.mu.Unlock()
+ mp.mu.Lock()
+ mp.blocks[cmd.Blkpos] = &cmd.Blk
+ mp.mu.Unlock()
+ client.conn.SendCmd(&mt.ToSrvGotBlks{Blks: [][3]int16{cmd.Blkpos}})
}
}
-func l_map_clear(l *lua.LState) int {
- mtmap := getMap(l)
-
- mtmap.mu.Lock()
- defer mtmap.mu.Unlock()
-
- var cmd mt.ToSrvDeletedBlks
- for pos := range mtmap.blocks {
- cmd.Blks = append(cmd.Blks, pos)
- }
-
- mtmap.blocks = map[[3]int16]*mt.MapBlk{}
-
- mtmap.client.conn.SendCmd(&cmd)
-
- return 0
+func l_map(l *lua.LState) int {
+ mp := newMap(l)
+ l.Push(mp.userdata)
+ return 1
}
func l_map_block(l *lua.LState) int {
- mtmap := getMap(l)
+ mp := getMap(l, 1)
var blkpos [3]int16
convert.ReadVec3Int16(l, l.Get(2), &blkpos)
- mtmap.mu.Lock()
- defer mtmap.mu.Unlock()
+ mp.mu.Lock()
+ defer mp.mu.Unlock()
- block, ok := mtmap.blocks[blkpos]
+ block, ok := mp.blocks[blkpos]
if ok {
l.Push(convert.PushMapBlk(l, *block))
} else {
}
func l_map_node(l *lua.LState) int {
- mtmap := getMap(l)
+ mp := getMap(l, 1)
var pos [3]int16
convert.ReadVec3Int16(l, l.Get(2), &pos)
blkpos, i := mt.Pos2Blkpos(pos)
- mtmap.mu.Lock()
- defer mtmap.mu.Unlock()
+ mp.mu.Lock()
+ defer mp.mu.Unlock()
- block, block_exists := mtmap.blocks[blkpos]
+ block, block_exists := mp.blocks[blkpos]
if block_exists {
meta, meta_exists := block.NodeMetas[i]
if !meta_exists {
+++ /dev/null
-package main
-
-import (
- "github.com/anon55555/mt"
- "github.com/dragonfireclient/hydra-dragonfire/convert"
- "github.com/yuin/gopher-lua"
- "sync"
-)
-
-type Pkts struct {
- client *Client
- mu sync.Mutex
- wildcard bool
- subscribed map[string]struct{}
- userdata *lua.LUserData
-}
-
-var pktsFuncs = map[string]lua.LGFunction{
- "subscribe": l_pkts_subscribe,
- "unsubscribe": l_pkts_unsubscribe,
- "wildcard": l_pkts_wildcard,
-}
-
-type EventPkt struct {
- pktType string
- pktData *mt.Pkt
-}
-
-func (evt EventPkt) handle(l *lua.LState, val lua.LValue) {
- l.SetField(val, "type", lua.LString("pkt"))
- l.SetField(val, "pkt_type", lua.LString(evt.pktType))
- l.SetField(val, "pkt_data", convert.PushPkt(l, evt.pktData))
-}
-
-func getPkts(l *lua.LState) *Pkts {
- return l.CheckUserData(1).Value.(*Pkts)
-}
-
-func (pkts *Pkts) create(client *Client, l *lua.LState) {
- pkts.client = client
- pkts.wildcard = false
- pkts.subscribed = map[string]struct{}{}
- pkts.userdata = l.NewUserData()
- pkts.userdata.Value = pkts
- l.SetMetatable(pkts.userdata, l.GetTypeMetatable("hydra.pkts"))
-}
-
-func (pkts *Pkts) push() lua.LValue {
- return pkts.userdata
-}
-
-func (pkts *Pkts) connect() {
-}
-
-func (pkts *Pkts) process(pkt *mt.Pkt) {
- pktType := string(convert.PushPktType(pkt))
-
- pkts.mu.Lock()
- _, subscribed := pkts.subscribed[pktType]
- pkts.mu.Unlock()
-
- if subscribed || pkts.wildcard {
- pkts.client.queue <- EventPkt{pktType: pktType, pktData: pkt}
- }
-}
-
-func l_pkts_subscribe(l *lua.LState) int {
- pkts := getPkts(l)
- n := l.GetTop()
-
- pkts.mu.Lock()
- defer pkts.mu.Unlock()
-
- for i := 2; i <= n; i++ {
- pkts.subscribed[l.CheckString(i)] = struct{}{}
- }
-
- return 0
-}
-
-func l_pkts_unsubscribe(l *lua.LState) int {
- pkts := getPkts(l)
- n := l.GetTop()
-
- pkts.mu.Lock()
- defer pkts.mu.Unlock()
-
- for i := 2; i <= n; i++ {
- delete(pkts.subscribed, l.CheckString(i))
- }
-
- return 0
-}
-
-func l_pkts_wildcard(l *lua.LState) int {
- pkts := getPkts(l)
- pkts.wildcard = l.ToBool(2)
- return 0
-}