aboutsummaryrefslogtreecommitdiff
path: root/client_conn.go
blob: 1a16d94ccb22fe4682429e9a1deb8081baa720a4 (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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package proxy

import (
	"errors"
	"log"
	"net"
	"sync"
	"time"

	"github.com/HimbeerserverDE/mt"
	"github.com/HimbeerserverDE/mt/rudp"
)

type clientState uint8

const (
	csCreated clientState = iota
	csInit
	csActive
	csSudo
)

// A ClientConn is a connection to a minetest client.
type ClientConn struct {
	mt.Peer
	created time.Time
	srv     *ServerConn
	mu      sync.RWMutex

	logger *log.Logger

	cstate   clientState
	cstateMu sync.RWMutex
	name     string
	initCh   chan struct{}
	hopMu    sync.Mutex

	auth struct {
		method                       mt.AuthMethods
		salt, srpA, srpB, srpM, srpK []byte
	}

	fallbackFrom string
	whyKicked    *mt.ToCltKick

	lang string

	major, minor, patch uint8
	reservedVer         uint8
	versionStr          string
	formspecVer         uint16

	denyPools map[string]struct{}
	itemDefs  []mt.ItemDef
	aliases   []struct{ Alias, Orig string }
	nodeDefs  []mt.NodeDef
	p0Map     param0Map
	p0SrvMap  param0SrvMap
	media     []mediaFile

	playerCAO, currentCAO mt.AOID

	playerListInit bool

	modChs   map[string]struct{}
	modChsMu sync.RWMutex

	cltInfo *mt.ToSrvCltInfo
}

// Name returns the player name of the ClientConn.
func (cc *ClientConn) Name() string { return cc.name }

func (cc *ClientConn) hasPlayerCAO() bool { return cc.playerCAO != 0 }

func (cc *ClientConn) server() *ServerConn {
	cc.mu.RLock()
	defer cc.mu.RUnlock()

	return cc.srv
}

// ServerName returns the name of the current upstream server
// of the ClientConn. It is empty if there is no upstream connection.
func (cc *ClientConn) ServerName() string {
	srv := cc.server()
	if srv != nil {
		return srv.name
	}

	return ""
}

func (cc *ClientConn) state() clientState {
	cc.cstateMu.RLock()
	defer cc.cstateMu.RUnlock()

	return cc.cstate
}

func (cc *ClientConn) setState(state clientState) {
	cc.cstateMu.Lock()
	defer cc.cstateMu.Unlock()

	cc.cstate = state
}

// Init returns a channel that is closed
// when the ClientConn enters the csActive state.
func (cc *ClientConn) Init() <-chan struct{} { return cc.initCh }

// Log logs an interaction with the ClientConn.
// dir indicates the direction of the interaction.
func (cc *ClientConn) Log(dir string, v ...interface{}) {
	cc.logger.Println(append([]interface{}{dir}, v...)...)
}

// CltInfo returns the ToSrvCltInfo known about the client.
func (cc *ClientConn) ToSrvCltInfo() *mt.ToSrvCltInfo { return cc.cltInfo }

func handleClt(cc *ClientConn) {
	for {
		pkt, err := cc.Recv()
		if err != nil {
			if errors.Is(err, net.ErrClosed) {
				if cc.state() == csActive {
					handleLeave(cc)
				}

				if errors.Is(cc.WhyClosed(), rudp.ErrTimedOut) {
					cc.Log("<->", "timeout")
				} else {
					cc.Log("<->", "disconnect")
				}

				if cc.Name() != "" {
					playersMu.Lock()
					delete(players, cc.Name())
					playersMu.Unlock()
				}

				if cc.server() != nil {
					cc.server().Close()

					cc.server().mu.Lock()
					cc.server().clt = nil
					cc.server().mu.Unlock()

					cc.mu.Lock()
					cc.srv = nil
					cc.mu.Unlock()
				}

				break
			}

			cc.Log("->", err)
			continue
		}

		cc.process(pkt)
	}
}