This repository has been archived by the owner on Jun 30, 2023. It is now read-only.
forked from paralin/go-dota2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
client_request.go
106 lines (87 loc) · 2 KB
/
client_request.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package dota2
import (
"context"
"sync"
"github.com/faceit/go-steam/protocol/gamecoordinator"
"github.com/golang/protobuf/proto"
gcm "github.com/paralin/go-dota2/protocol"
)
// responseHandler returns handled, and any error
type responseHandler func(resp *gamecoordinator.GCPacket) bool
// MakeRequest starts and tracks a request given a context.
func (d *Dota2) MakeRequest(
ctx context.Context,
reqMsgID uint32,
request proto.Message,
respMsgID uint32,
response proto.Message,
matchesRequest ...func(proto.Message) bool,
) (mrErr error) {
d.le.Debugf("making request: %s", gcm.EDOTAGCMsg(reqMsgID).String())
cctx, err := d.validateConnectionContext()
if err != nil {
return err
}
var respMtx sync.Mutex
respCh := make(chan error, 10)
d.pendReqMtx.Lock()
reqID := d.pendReqID
d.pendReqID++
defer func() {
if mrErr == nil {
return
}
d.pendReqMtx.Lock()
m := d.pendReq[respMsgID]
if m != nil {
delete(m, reqID)
}
if len(m) == 0 {
delete(d.pendReq, respMsgID)
}
d.pendReqMtx.Unlock()
}()
m := d.pendReq[respMsgID]
if m == nil {
m = make(map[uint32]responseHandler)
d.pendReq[respMsgID] = m
}
m[reqID] = func(resp *gamecoordinator.GCPacket) bool {
respMtx.Lock()
defer respMtx.Unlock()
if err := d.unmarshalBody(resp, response); err != nil {
respCh <- err
return false
}
for _, handler := range matchesRequest {
if !handler(response) {
return false
}
}
respCh <- nil
return true
}
d.pendReqMtx.Unlock()
d.write(reqMsgID, request)
select {
case <-ctx.Done():
return ctx.Err()
case <-cctx.Done():
return ErrNotReady
case rErr := <-respCh:
return rErr
}
}
// handleResponsePacket attempts to handle a response packet.
func (d *Dota2) handleResponsePacket(packet *gamecoordinator.GCPacket) (handled bool) {
d.pendReqMtx.Lock()
defer d.pendReqMtx.Unlock()
respHandlers := d.pendReq[packet.MsgType]
for rid, h := range respHandlers {
if hnd := h(packet); hnd {
handled = true
delete(respHandlers, rid)
}
}
return
}