-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathgamequery.go
128 lines (110 loc) · 3.18 KB
/
gamequery.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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package gamequery
import (
"errors"
"github.com/wisp-gg/gamequery/api"
"github.com/wisp-gg/gamequery/internal"
"github.com/wisp-gg/gamequery/internal/protocols"
"sort"
"sync"
"time"
)
var queryProtocols = []internal.Protocol{
protocols.SourceQuery{},
protocols.MinecraftUDP{},
protocols.MinecraftTCP{},
}
func findProtocols(name string) []internal.Protocol {
found := make([]internal.Protocol, 0)
for _, protocol := range queryProtocols {
if protocol.Name() == name {
found = append(found, protocol)
} else {
for _, protocolName := range protocol.Aliases() {
if protocolName == name {
found = append(found, protocol)
}
}
}
}
return found
}
type queryResult struct {
Name string
Priority uint16
Err error
Response api.Response
}
// Query the game server by detecting the protocol (trying all available protocols).
// This usually should be used as the initial query function and then use `Query` function
// with the returned protocol if the query succeeds. Otherwise each function call will take always
// <req.Timeout> duration even if the response was received earlier from one of the protocols.
func Detect(req api.Request) (api.Response, string, error) {
return query(req, queryProtocols)
}
// Query the game server using the protocol provided in req.Game.
func Query(req api.Request) (api.Response, error) {
chosenProtocols := findProtocols(req.Game)
if len(chosenProtocols) < 1 {
return api.Response{}, errors.New("could not find protocols for the game")
}
response, _, err := query(req, chosenProtocols)
return response, err
}
func query(req api.Request, chosenProtocols []internal.Protocol) (api.Response, string, error) {
var wg sync.WaitGroup
wg.Add(len(chosenProtocols))
queryResults := make([]queryResult, len(chosenProtocols))
for index, queryProtocol := range chosenProtocols {
go func(queryProtocol internal.Protocol, index int) {
defer wg.Done()
var port = queryProtocol.DefaultPort()
if req.Port != 0 {
port = req.Port
}
var timeout = 5 * time.Second
if req.Timeout != nil {
timeout = *req.Timeout
}
networkHelper := internal.NetworkHelper{}
if err := networkHelper.Initialize(queryProtocol.Network(), req.IP, port, timeout); err != nil {
queryResults[index] = queryResult{
Priority: queryProtocol.Priority(),
Err: err,
Response: api.Response{},
}
return
}
defer networkHelper.Close()
response, err := queryProtocol.Execute(networkHelper)
if err != nil {
queryResults[index] = queryResult{
Priority: queryProtocol.Priority(),
Err: err,
Response: api.Response{},
}
return
}
queryResults[index] = queryResult{
Name: queryProtocol.Name(),
Priority: queryProtocol.Priority(),
Err: nil,
Response: response,
}
}(queryProtocol, index)
}
wg.Wait()
sort.Slice(queryResults, func(i, j int) bool {
return queryResults[i].Priority > queryResults[j].Priority
})
var firstError error
for _, result := range queryResults {
if result.Err != nil {
if firstError == nil {
firstError = result.Err
}
} else {
return result.Response, result.Name, nil
}
}
return api.Response{}, "", firstError
}