mirror of
https://github.com/PretendoNetwork/nex-go.git
synced 2025-04-02 11:02:14 -04:00
230 lines
5.7 KiB
Go
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)
|
|
}
|
|
}
|