aboutsummaryrefslogtreecommitdiffstats
path: root/client/quake/client.go
blob: 7b48e6c1ba46835e84cdd18dbf70be2e025df805 (plain) (blame)
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)
	}
}