Refactor servers

This commit is contained in:
Andrew Gutekanst
2020-01-13 17:19:29 -05:00
parent 0922ff4f9c
commit e5257eb6ed
16 changed files with 749 additions and 221 deletions

View File

@@ -1,80 +1,120 @@
package entranceserver
import (
"database/sql"
"encoding/hex"
"fmt"
"io"
"net"
"sync"
"github.com/Andoryuuta/Erupe/config"
"github.com/Andoryuuta/Erupe/network"
"go.uber.org/zap"
)
func handleEntranceServerConnection(conn net.Conn) {
// Server is a MHF entrance server.
type Server struct {
sync.Mutex
logger *zap.Logger
erupeConfig *config.Config
db *sql.DB
listener net.Listener
isShuttingDown bool
}
// Config struct allows configuring the server.
type Config struct {
Logger *zap.Logger
DB *sql.DB
ErupeConfig *config.Config
}
// NewServer creates a new Server type.
func NewServer(config *Config) *Server {
s := &Server{
logger: config.Logger,
erupeConfig: config.ErupeConfig,
db: config.DB,
}
return s
}
// Start starts the server in a new goroutine.
func (s *Server) Start() error {
l, err := net.Listen("tcp", fmt.Sprintf(":%d", s.erupeConfig.Entrance.Port))
if err != nil {
return err
}
s.listener = l
go s.acceptClients()
return nil
}
// Shutdown exits the server gracefully.
func (s *Server) Shutdown() {
s.logger.Debug("Shutting down")
s.Lock()
s.isShuttingDown = true
s.Unlock()
// This will cause the acceptor goroutine to error and exit gracefully.
s.listener.Close()
}
//acceptClients handles accepting new clients in a loop.
func (s *Server) acceptClients() {
for {
conn, err := s.listener.Accept()
if err != nil {
// Check if we are shutting down and exit gracefully if so.
s.Lock()
shutdown := s.isShuttingDown
s.Unlock()
if shutdown {
break
} else {
continue
}
}
// Start a new goroutine for the connection so that we don't block other incoming connections.
go s.handleEntranceServerConnection(conn)
}
}
func (s *Server) handleEntranceServerConnection(conn net.Conn) {
// Client initalizes the connection with a one-time buffer of 8 NULL bytes.
nullInit := make([]byte, 8)
n, err := io.ReadFull(conn, nullInit)
if err != nil {
fmt.Println(err)
s.logger.Warn("Failed to read 8 NULL init", zap.Error(err))
return
} else if n != len(nullInit) {
fmt.Println("io.ReadFull couldn't read the full 8 byte init.")
s.logger.Warn("io.ReadFull couldn't read the full 8 byte init.")
return
}
// Create a new encrypted connection handler and read a packet from it.
cc := network.NewCryptConn(conn)
for {
pkt, err := cc.ReadPacket()
if err != nil {
return
}
fmt.Printf("Got entrance server command:\n%s\n", hex.Dump(pkt))
data := makeResp([]ServerInfo{
ServerInfo{
IP: net.ParseIP("127.0.0.1"),
Unk2: 0,
Type: 1,
Season: 0,
Unk6: 3,
Name: "AErupe Server in Go! @localhost",
AllowedClientFlags: 4096,
Channels: []ChannelInfo{
ChannelInfo{
Port: 54001,
MaxPlayers: 100,
CurrentPlayers: 0,
Unk4: 0,
Unk5: 0,
Unk6: 0,
Unk7: 0,
Unk8: 0,
Unk9: 0,
Unk10: 319,
Unk11: 248,
Unk12: 159,
Unk13: 12345,
},
},
},
})
cc.SendPacket(data)
}
}
func DoEntranceServer(listenAddr string) {
l, err := net.Listen("tcp", listenAddr)
pkt, err := cc.ReadPacket()
if err != nil {
panic(err)
s.logger.Warn("Error reading packet", zap.Error(err))
return
}
defer l.Close()
for {
conn, err := l.Accept()
if err != nil {
panic(err)
}
go handleEntranceServerConnection(conn)
}
s.logger.Debug("Got entrance server command:\n", zap.String("raw", hex.Dump(pkt)))
data := makeResp(s.erupeConfig.Entrance.Entries)
cc.SendPacket(data)
// Close because we only need to send the response once.
// Any further requests from the client will come from a new connection.
conn.Close()
}

View File

@@ -4,6 +4,7 @@ import (
"encoding/binary"
"net"
"github.com/Andoryuuta/Erupe/config"
"github.com/Andoryuuta/byteframe"
)
@@ -16,45 +17,11 @@ func paddedString(x string, size uint) []byte {
return out
}
// ServerInfo represents an entry in the serverlist.
type ServerInfo struct {
IP net.IP
Unk2 uint16
Type uint8 // Server type. 0=?, 1=open, 2=cities, 3=newbie, 4=bar
Season uint8 // Server activity. 0 = green, 1 = orange, 2 = blue
Unk6 uint8 // Something to do with server recommendation on 0, 3, and 5.
Name string // Server name, 66 byte null terminated Shift-JIS.
// 4096(PC, PS3/PS4)?, 8258(PC, PS3/PS4)?, 8192 == nothing?
// THIS ONLY EXISTS IF Binary8Header.type == "SV2", NOT "SVR"!
AllowedClientFlags uint32
Channels []ChannelInfo
}
// ChannelInfo represents an entry in a server's channel list.
type ChannelInfo struct {
Port uint16
//ChannelIndex uint16
MaxPlayers uint16
CurrentPlayers uint16
Unk4 uint16
Unk5 uint16
Unk6 uint16
Unk7 uint16
Unk8 uint16
Unk9 uint16
Unk10 uint16
Unk11 uint16
Unk12 uint16
Unk13 uint16
}
func encodeServerInfo(serverInfos []ServerInfo) []byte {
func encodeServerInfo(serverInfos []config.EntranceServerInfo) []byte {
bf := byteframe.NewByteFrame()
for serverIdx, si := range serverInfos {
bf.WriteUint32(binary.LittleEndian.Uint32(si.IP.To4()))
bf.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(si.IP).To4()))
bf.WriteUint16(16 + uint16(serverIdx))
bf.WriteUint16(si.Unk2)
bf.WriteUint16(uint16(len(si.Channels)))
@@ -103,7 +70,7 @@ func makeHeader(data []byte, respType string, entryCount uint16, key byte) []byt
return bf.Data()
}
func makeResp(servers []ServerInfo) []byte {
func makeResp(servers []config.EntranceServerInfo) []byte {
rawServerData := encodeServerInfo(servers)
bf := byteframe.NewByteFrame()