/
listener.go
139 lines (125 loc) · 4.85 KB
/
listener.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
129
130
131
132
133
134
135
136
137
138
139
package minecraft
import (
"github.com/sandertv/go-raknet"
"github.com/sandertv/gophertunnel/minecraft/resource"
"log"
"net"
"os"
)
// Listener implements a Minecraft listener on top of an unspecific net.Listener. It abstracts away the
// login sequence of connecting clients and provides the implements the net.Listener interface to provide a
// consistent API.
type Listener struct {
// ErrorLog is a log.Logger that errors that occur during packet handling of clients are written to. By
// default, ErrorLog is set to one equal to the global logger.
ErrorLog *log.Logger
// ResourcePacks is a slice of resource packs that the listener may hold. Each client will be asked to
// download these resource packs upon joining.
ResourcePacks []*resource.Pack
// TexturePacksRequired specifies if clients that join must accept the texture pack in order for them to
// be able to join the server. If they don't accept, they can only leave the server.
TexturePacksRequired bool
listener net.Listener
incoming chan *Conn
close chan bool
}
// Listen announces on the local network address. The network must be "tcp", "tcp4", "tcp6", "unix",
// "unixpacket" or "raknet". A Listener is returned which may be used to accept connections.
// If the host in the address parameter is empty or a literal unspecified IP address, Listen listens on all
// available unicast and anycast IP addresses of the local system.
func Listen(network, address string) (*Listener, error) {
var netListener net.Listener
var err error
switch network {
case "raknet":
// Listen specifically for the RakNet network type, as the standard library (obviously) doesn't
// implement that.
netListener, err = raknet.Listen(address)
default:
// Otherwise fall back to the standard net.Listen.
netListener, err = net.Listen(network, address)
}
if err != nil {
return nil, err
}
listener := &Listener{
ErrorLog: log.New(os.Stderr, "", log.LstdFlags),
listener: netListener,
close: make(chan bool, 2),
incoming: make(chan *Conn),
}
// Actually start listening.
go listener.listen()
return listener, nil
}
// Accept accepts a fully connected (on Minecraft layer) connection which is ready to receive and send
// packets. It is recommended to cast the net.Conn returned to a *minecraft.Conn so that it is possible to
// use the conn.ReadPacket() and conn.WritePacket() methods.
func (listener *Listener) Accept() (net.Conn, error) {
return <-listener.incoming, nil
}
// HijackPong hijacks the pong response from a server at an address passed. The listener passed will
// continuously update its pong data by hijacking the pong data of the server at the address.
// The hijack will last until the listener is shut down.
// If the address passed could not be resolved, an error is returned.
// Calling HijackPong means that any current and future pong data set using listener.PongData is overwritten
// each update.
func (listener *Listener) HijackPong(address string) error {
return listener.listener.(*raknet.Listener).HijackPong(address)
}
// Addr returns the address of the underlying listener.
func (listener *Listener) Addr() net.Addr {
return listener.listener.Addr()
}
// Close closes the listener and the underlying net.Listener.
func (listener *Listener) Close() error {
listener.close <- true
return listener.listener.Close()
}
// listen starts listening for incoming connections and packets. When a player is fully connected, it submits
// it to the accepted connections channel so that a call to Accept can pick it up.
func (listener *Listener) listen() {
defer func() {
_ = listener.Close()
}()
for {
netConn, err := listener.listener.Accept()
if err != nil {
// The underlying listener was closed, meaning we should return immediately so this listener can
// close too.
return
}
conn := newConn(netConn, nil, listener.ErrorLog)
conn.texturePacksRequired = listener.TexturePacksRequired
conn.resourcePacks = listener.ResourcePacks
go func() {
defer func() {
_ = conn.Close()
}()
for {
// We finally arrived at the packet decoding loop. We constantly decode packets that arrive
// and push them to the Conn so that they may be processed.
packets, err := conn.decoder.Decode()
if err != nil {
if !raknet.ErrConnectionClosed(err) {
listener.ErrorLog.Printf("error reading from client connection: %v\n", err)
}
return
}
for _, data := range packets {
loggedInBefore := conn.loggedIn
if err := conn.handleIncoming(data); err != nil {
listener.ErrorLog.Printf("error: %v", err)
return
}
if !loggedInBefore && conn.loggedIn {
// The connection was previously not logged in, but was after receiving this packet,
// meaning the connection is fully completely now. We add it to the channel so that
// a call to Accept() can receive it.
listener.incoming <- conn
}
}
}
}()
}
}