nex-go/server.go
2018-10-02 17:02:36 -04:00

230 lines
5.7 KiB
Go

package nex
import (
"encoding/hex"
"fmt"
"math/rand"
"net"
"time"
)
// Settings is a port of the settings handler in Kinnay's NintendoClients repo
type Settings struct {
PrudpTransport int
PrudpVersion int
PrudpStreamType int
PrudpFragmentSize int
PrudpResendTimeout float32
PrudpPingTimeout int
PrudpSilenceTimeout float32
PrudpCompression int
PrudpV0SignatureVersion int
PrudpV0FlagsVersion int
PrudpV0ChecksumVersion int
KerberosKeySize int
KerberosKeyDerivation int
IntSize int
Version int
AccessKey string
}
// NewSettings returns a set of default server settings
func NewSettings() Settings {
// Defaults
return Settings{
PrudpTransport: 0,
PrudpVersion: 1,
PrudpStreamType: 10,
PrudpFragmentSize: 1300,
PrudpResendTimeout: 1.5,
PrudpPingTimeout: 4,
PrudpSilenceTimeout: 7.5,
PrudpCompression: 0,
PrudpV0SignatureVersion: 0,
PrudpV0FlagsVersion: 1,
PrudpV0ChecksumVersion: 1,
KerberosKeySize: 32,
KerberosKeyDerivation: 0,
IntSize: 4,
Version: 0,
}
}
// Server represents generic NEX server
type Server struct {
_UDPServer *net.UDPConn
Settings Settings
CompressPacket func([]byte) []byte
DecompressPacket func([]byte) []byte
Clients map[string]*Client
Handlers map[string]func(*Client, *Packet)
}
// NewServer returns a new NEX server
func NewServer(settings Settings) *Server {
server := &Server{
Settings: settings,
Handlers: make(map[string]func(*Client, *Packet)),
Clients: make(map[string]*Client),
}
if settings.PrudpCompression == 0 {
compression := DummyCompression{}
server.CompressPacket = compression.Compress
} else {
compression := ZLibCompression{}
server.CompressPacket = compression.Compress
}
return server
}
// Listen starts a NEX server on a given port
func (server *Server) Listen(port string) {
protocol := "udp"
address, _ := net.ResolveUDPAddr(protocol, port)
UDPServer, _ := net.ListenUDP(protocol, address)
server._UDPServer = UDPServer
fmt.Println("NEX server listening on port", port)
for {
readPacket(server)
}
}
// On defines a datagram event handler
func (server *Server) On(event string, handler func(*Client, *Packet)) {
server.Handlers[event] = handler
}
// Kick removes a client from the server
func (server *Server) Kick(client Client) {
discriminator := client._UDPConn.String()
if _, ok := server.Clients[discriminator]; ok {
delete(server.Clients, discriminator)
fmt.Println("Kicked user", discriminator)
}
}
// Acknowledge creates an acknowledgement packet based on the input packet
func (server *Server) Acknowledge(Packet *Packet) {
ack := NewPacket(Packet.Sender)
if Packet.Type == Types["Syn"] {
Packet.Sender.ServerConnectionSignature = Packet.Signature
ack.SetSignature(Packet.Signature)
}
if Packet.Type == Types["Connect"] {
Packet.Sender.ClientConnectionSignature = Packet.Signature
ack.SetSignature(Packet.Signature)
}
ack.SetVersion(Packet.Version)
ack.SetDestination(Packet.Source)
ack.SetSource(Packet.Destination)
ack.SetType(Packet.Type)
ack.AddFlag(Flags["Ack"])
ack.FragmentID = Packet.FragmentID
ack.SequenceID = Packet.SequenceID
server.SendRaw(Packet.Sender._UDPConn, ack.Bytes())
}
// Send writes data to client
func (server *Server) Send(client *Client, packet *Packet) {
data := packet.Payload
times := len(data) / server.Settings.PrudpFragmentSize
fragmentID := 1
for i := 0; i <= times; i++ {
if len(data) < server.Settings.PrudpFragmentSize {
packet.SetPayload(data)
server.SendFragment(client, packet, 0)
} else {
packet.SetPayload(data[:server.Settings.PrudpFragmentSize])
server.SendFragment(client, packet, fragmentID)
data = data[server.Settings.PrudpFragmentSize:]
fragmentID++
}
}
}
// SendFragment sends a packet fragment to the client
func (server *Server) SendFragment(client *Client, packet *Packet, fragmentID int) {
data := packet.Payload
packet.SetPayload(server.CompressPacket(data))
packet.SequenceID = client.SequenceIDOut.Increment()
server.SendRaw(client._UDPConn, packet.Bytes())
}
// SendRaw writes raw packet data to the client socket
func (server *Server) SendRaw(conn *net.UDPAddr, data []byte) {
server._UDPServer.WriteToUDP(data, conn)
}
func readPacket(server *Server) {
var buffer [64000]byte
len, addr, _ := server._UDPServer.ReadFromUDP(buffer[0:])
discriminator := addr.String()
if _, ok := server.Clients[discriminator]; !ok {
newClient := NewClient(addr, server)
newClient.SignatureBase = sum([]byte(server.Settings.AccessKey))
newClient.SignatureKey = hex.EncodeToString(MD5Hash([]byte(server.Settings.AccessKey)))
server.Clients[discriminator] = &newClient
}
client := server.Clients[discriminator]
data := buffer[0:len]
Packet := NewPacket(client)
Packet.FromBytes(data)
if server.Handlers["Packet"] != nil {
go server.Handlers["Packet"](client, &Packet)
}
switch Packet.Type {
case 0:
handler := server.Handlers["Syn"]
if handler != nil {
go handler(client, &Packet)
}
case 1:
rand.Seed(time.Now().UnixNano())
client.SessionID = rand.Intn(0xFF)
handler := server.Handlers["Connect"]
if handler != nil {
go handler(client, &Packet)
}
case 2:
handler := server.Handlers["Data"]
if handler != nil {
go handler(client, &Packet)
}
case 3:
handler := server.Handlers["Disconnect"]
if handler != nil {
go handler(client, &Packet)
}
case 4:
handler := server.Handlers["Ping"]
if handler != nil {
go handler(client, &Packet)
}
default:
fmt.Println("UNKNOWN TYPE", Packet.Type)
}
}