forked from cooldogedev/spectrum
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathspectrum.go
128 lines (109 loc) · 3.6 KB
/
spectrum.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 spectrum
import (
"context"
"errors"
"log/slog"
"github.com/brentp/intintmap"
"github.com/cooldogedev/spectrum/internal"
"github.com/cooldogedev/spectrum/server"
"github.com/cooldogedev/spectrum/session"
tr "github.com/cooldogedev/spectrum/transport"
"github.com/cooldogedev/spectrum/util"
"github.com/sandertv/gophertunnel/minecraft"
)
// Spectrum represents a proxy server managing server discovery,
// network transport, and connections through an underlying minecraft.Listener.
type Spectrum struct {
discovery server.Discovery
transport tr.Transport
listener *minecraft.Listener
registry *session.Registry
logger *slog.Logger
opts util.Opts
}
// NewSpectrum creates a new Spectrum instance using the provided server.Discovery.
// It initializes opts with default options from util.DefaultOpts() if opts is nil,
// and defaults to TCP transport if transport is nil transport.TCP.
func NewSpectrum(discovery server.Discovery, logger *slog.Logger, opts *util.Opts, transport tr.Transport) *Spectrum {
if opts == nil {
opts = util.DefaultOpts()
}
if len(opts.ClientDecode) > 0 {
internal.ClientPacketMap = intintmap.New(len(opts.ClientDecode), 0.999)
for _, id := range opts.ClientDecode {
internal.ClientPacketMap.Put(int64(id), 1)
}
}
if transport == nil {
transport = tr.NewSpectral(logger)
}
return &Spectrum{
discovery: discovery,
transport: transport,
registry: session.NewRegistry(),
logger: logger,
opts: *opts,
}
}
// Listen sets up a minecraft.Listener for incoming connections based on the provided minecraft.ListenConfig.
// The listener is then used by the Accept() method for accepting incoming connections.
func (s *Spectrum) Listen(config minecraft.ListenConfig) (err error) {
listener, err := config.Listen("raknet", s.opts.Addr)
if err != nil {
s.logger.Error("failed to listen", "err", err)
return err
}
s.listener = listener
s.logger.Info("started listening", "addr", listener.Addr())
return nil
}
// Accept accepts an incoming minecraft.Conn and creates a new session for it.
// This method should be called in a loop to continuously accept new connections.
func (s *Spectrum) Accept() (*session.Session, error) {
c, err := s.listener.Accept()
if err != nil {
s.logger.Error("failed to accept session", "err", err)
return nil, err
}
conn := c.(*minecraft.Conn)
newSession := session.NewSession(conn, s.logger, s.registry, s.discovery, s.opts, s.transport)
if s.opts.AutoLogin {
go func() {
if err := newSession.Login(); err != nil {
newSession.Disconnect(err.Error())
if !errors.Is(err, context.Canceled) {
s.logger.Error("failed to login session", "err", err)
}
}
}()
}
s.logger.Debug("accepted session", "username", conn.IdentityData().DisplayName, "addr", conn.RemoteAddr().String())
return newSession, nil
}
// Discovery returns the server discovery instance.
func (s *Spectrum) Discovery() server.Discovery {
return s.discovery
}
// Opts returns the configuration options.
func (s *Spectrum) Opts() util.Opts {
return s.opts
}
// Listener returns the listener instance.
func (s *Spectrum) Listener() *minecraft.Listener {
return s.listener
}
// Registry returns the session registry instance.
func (s *Spectrum) Registry() *session.Registry {
return s.registry
}
// Transport returns the transport instance.
func (s *Spectrum) Transport() tr.Transport {
return s.transport
}
// Close closes the listener and stops listening for incoming connections.
func (s *Spectrum) Close() error {
for _, activeSession := range s.registry.GetSessions() {
activeSession.Disconnect(s.opts.ShutdownMessage)
}
return s.listener.Close()
}