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
|
package quake
import (
"errors"
"log"
"net"
"os"
"sync"
"time"
"github.com/osm/quake/client"
"github.com/osm/quake/common/context"
"github.com/osm/quake/common/rand"
"github.com/osm/quake/common/sequencer"
"github.com/osm/quake/packet"
"github.com/osm/quake/packet/command"
"github.com/osm/quake/packet/command/stringcmd"
"github.com/osm/quake/protocol"
)
var (
ErrNoName = errors.New("no name supplied")
ErrNoTeam = errors.New("no team supplied")
)
const connectReadDeadline = time.Duration(13)
type Client struct {
conn *net.UDPConn
logger *log.Logger
ctx *context.Context
cmdsMu sync.Mutex
cmds []command.Command
seq *sequencer.Sequencer
handlers []func(packet.Packet) []command.Command
addrPort string
qPort uint16
serverCount int32
readDeadline time.Duration
clientVersion string
isSpectator bool
mapName string
name string
ping int16
team string
bottomColor byte
topColor byte
fteEnabled bool
fteExtensions uint32
fte2Enabled bool
fte2Extensions uint32
mvdEnabled bool
mvdExtensions uint32
zQuakeEnabled bool
zQuakeExtensions uint32
}
func New(name, team string, opts ...Option) (client.Client, error) {
if name == "" {
return nil, ErrNoName
}
if team == "" {
return nil, ErrNoTeam
}
c := &Client{
ctx: context.New(context.WithProtocolVersion(protocol.VersionQW)),
logger: log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime),
seq: sequencer.New(),
readDeadline: connectReadDeadline,
qPort: rand.Uint16(),
name: name,
ping: 999,
team: team,
}
for _, opt := range opts {
opt(c)
}
c.seq.SetPing(c.ping)
return c, nil
}
func (c *Client) Enqueue(cmds []command.Command) {
c.cmdsMu.Lock()
c.cmds = append(c.cmds, cmds...)
c.cmdsMu.Unlock()
}
func (c *Client) HandleFunc(h func(h packet.Packet) []command.Command) {
c.handlers = append(c.handlers, h)
}
func (c *Client) Quit() {
c.cmds = append(c.cmds, &stringcmd.Command{String: "drop"})
if c.seq.GetState() == sequencer.Connected {
time.Sleep(time.Duration(c.ping) * time.Millisecond)
}
}
|