From 9018d87c86806b2c6d7cabd964479031d2a1e7b1 Mon Sep 17 00:00:00 2001 From: Elias Fleckenstein Date: Wed, 1 Jun 2022 18:09:48 +0200 Subject: [PATCH] Allow multiple clients to share a map --- auth.go | 245 ---------------------------------- client.go | 26 ++-- comp_auth.go | 245 ++++++++++++++++++++++++++++++++++ comp_map.go | 52 ++++++++ comp_pkts.go | 99 ++++++++++++++ doc/client.md | 6 +- doc/{auth.md => comp_auth.md} | 7 +- doc/comp_map.md | 16 +++ doc/{pkts.md => comp_pkts.md} | 6 +- doc/hydra.md | 1 + doc/map.md | 7 +- example/print-node.lua | 2 +- hydra.go | 7 +- map.go | 72 ++++------ pkts.go | 99 -------------- 15 files changed, 469 insertions(+), 421 deletions(-) delete mode 100644 auth.go create mode 100644 comp_auth.go create mode 100644 comp_map.go create mode 100644 comp_pkts.go rename doc/{auth.md => comp_auth.md} (82%) create mode 100644 doc/comp_map.md rename doc/{pkts.md => comp_pkts.md} (73%) delete mode 100644 pkts.go diff --git a/auth.go b/auth.go deleted file mode 100644 index 93af0ee..0000000 --- a/auth.go +++ /dev/null @@ -1,245 +0,0 @@ -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 -} diff --git a/client.go b/client.go index 4595d13..2e47588 100644 --- a/client.go +++ b/client.go @@ -166,8 +166,8 @@ func l_client_connect(l *lua.LState) int { 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) { @@ -180,8 +180,8 @@ func l_client_connect(l *lua.LState) int { }() client.mu.Lock() - for _, component := range client.components { - component.connect() + for _, comp := range client.components { + comp.connect() } client.mu.Unlock() @@ -207,22 +207,22 @@ func l_client_enable(l *lua.LState) int { 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) } } diff --git a/comp_auth.go b/comp_auth.go new file mode 100644 index 0000000..5d3f838 --- /dev/null +++ b/comp_auth.go @@ -0,0 +1,245 @@ +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 +} diff --git a/comp_map.go b/comp_map.go new file mode 100644 index 0000000..c45155e --- /dev/null +++ b/comp_map.go @@ -0,0 +1,52 @@ +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 +} diff --git a/comp_pkts.go b/comp_pkts.go new file mode 100644 index 0000000..37e2284 --- /dev/null +++ b/comp_pkts.go @@ -0,0 +1,99 @@ +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 +} diff --git a/doc/client.md b/doc/client.md index fe9fb7d..0886ecf 100644 --- a/doc/client.md +++ b/doc/client.md @@ -18,6 +18,6 @@ After being disconnect, a client cannot be reconnected. Enabled components can be accessed by using `self.`. -- `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). diff --git a/doc/auth.md b/doc/comp_auth.md similarity index 82% rename from doc/auth.md rename to doc/comp_auth.md index f496632..94e4824 100644 --- a/doc/auth.md +++ b/doc/comp_auth.md @@ -1,14 +1,13 @@ # Authentication Component -Source code: [auth.go](../auth.go) +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. -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. +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 diff --git a/doc/comp_map.md b/doc/comp_map.md new file mode 100644 index 0000000..1c72a34 --- /dev/null +++ b/doc/comp_map.md @@ -0,0 +1,16 @@ +# 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`. + diff --git a/doc/pkts.md b/doc/comp_pkts.md similarity index 73% rename from doc/pkts.md rename to doc/comp_pkts.md index fba5083..a5be670 100644 --- a/doc/pkts.md +++ b/doc/comp_pkts.md @@ -1,8 +1,8 @@ -# Packet Handler Component +# Packets 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. +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 diff --git a/doc/hydra.md b/doc/hydra.md index fd916e0..90083bc 100644 --- a/doc/hydra.md +++ b/doc/hydra.md @@ -12,6 +12,7 @@ The `hydra` table contains functions necessary to handle connections. ## 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. diff --git a/doc/map.md b/doc/map.md index 26fd51a..9e857c5 100644 --- a/doc/map.md +++ b/doc/map.md @@ -1,13 +1,10 @@ -# 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. diff --git a/example/print-node.lua b/example/print-node.lua index d72f474..30d2506 100755 --- a/example/print-node.lua +++ b/example/print-node.lua @@ -16,7 +16,7 @@ while true do 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 diff --git a/hydra.go b/hydra.go index 7f7b2da..6da1822 100644 --- a/hydra.go +++ b/hydra.go @@ -40,6 +40,7 @@ var builtinFiles = []string{ var hydraFuncs = map[string]lua.LGFunction{ "client": l_client, + "map": l_map, "dtime": l_dtime, "poll": l_poll, "close": l_close, @@ -86,10 +87,12 @@ func main() { 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 { diff --git a/map.go b/map.go index 08b97ec..1ffae23 100644 --- a/map.go +++ b/map.go @@ -8,74 +8,54 @@ import ( ) 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 { @@ -86,16 +66,16 @@ func l_map_block(l *lua.LState) int { } 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 { diff --git a/pkts.go b/pkts.go deleted file mode 100644 index e34f3b8..0000000 --- a/pkts.go +++ /dev/null @@ -1,99 +0,0 @@ -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 -} -- 2.44.0