245 lines
4.7 KiB
Go
245 lines
4.7 KiB
Go
package raknet
|
|
|
|
import (
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type SessionState int
|
|
|
|
const (
|
|
StateHandshaking SessionState = iota
|
|
StateConnected
|
|
StateDisconnected
|
|
)
|
|
|
|
type Session struct {
|
|
mu sync.Mutex
|
|
Addr *net.UDPAddr
|
|
GUID int64
|
|
MTU uint16
|
|
conn *net.UDPConn
|
|
|
|
state SessionState
|
|
|
|
sendSeq uint32
|
|
recvSeq uint32
|
|
msgIndex uint32
|
|
|
|
orderIndex [MaxChannels]uint32
|
|
orderNext [MaxChannels]uint32
|
|
orderQueues [MaxChannels]map[uint32][]byte
|
|
|
|
recvSeqs map[uint32]bool
|
|
pendingACK []uint32
|
|
pendingNACK []uint32
|
|
|
|
splitMap map[uint16]*splitBuf
|
|
|
|
recoveryMap map[uint32]*Datagram
|
|
|
|
lastRecv time.Time
|
|
connTime time.Time
|
|
}
|
|
|
|
type splitBuf struct {
|
|
count uint32
|
|
parts map[uint32][]byte
|
|
}
|
|
|
|
func NewSession(conn *net.UDPConn, addr *net.UDPAddr, guid int64, mtu uint16) *Session {
|
|
s := &Session{
|
|
conn: conn,
|
|
Addr: addr,
|
|
GUID: guid,
|
|
MTU: mtu,
|
|
state: StateHandshaking,
|
|
recvSeqs: make(map[uint32]bool),
|
|
splitMap: make(map[uint16]*splitBuf),
|
|
recoveryMap: make(map[uint32]*Datagram),
|
|
lastRecv: time.Now(),
|
|
}
|
|
for i := 0; i < MaxChannels; i++ {
|
|
s.orderQueues[i] = make(map[uint32][]byte)
|
|
}
|
|
return s
|
|
}
|
|
|
|
func (s *Session) IsConnected() bool {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
return s.state == StateConnected
|
|
}
|
|
|
|
func (s *Session) IsDisconnected() bool {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
return s.state == StateDisconnected
|
|
}
|
|
|
|
func (s *Session) SetConnected() {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
s.state = StateConnected
|
|
s.connTime = time.Now()
|
|
}
|
|
|
|
func (s *Session) Timestamp() int64 {
|
|
if s.connTime.IsZero() {
|
|
return 0
|
|
}
|
|
return time.Since(s.connTime).Milliseconds()
|
|
}
|
|
|
|
func (s *Session) HandleACK(data []byte) {
|
|
nums, err := DecodeACKNACK(data)
|
|
if err != nil {
|
|
return
|
|
}
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
for _, n := range nums {
|
|
delete(s.recoveryMap, n)
|
|
}
|
|
}
|
|
|
|
func (s *Session) HandleNACK(data []byte) {
|
|
nums, err := DecodeACKNACK(data)
|
|
if err != nil {
|
|
return
|
|
}
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
for _, n := range nums {
|
|
dg, ok := s.recoveryMap[n]
|
|
if ok {
|
|
s.conn.WriteToUDP(dg.Encode(), s.Addr)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *Session) FlushACKs() {
|
|
s.mu.Lock()
|
|
acks := s.pendingACK
|
|
nacks := s.pendingNACK
|
|
s.pendingACK = nil
|
|
s.pendingNACK = nil
|
|
s.mu.Unlock()
|
|
|
|
if len(acks) > 0 {
|
|
s.conn.WriteToUDP(EncodeACK(acks), s.Addr)
|
|
}
|
|
if len(nacks) > 0 {
|
|
s.conn.WriteToUDP(EncodeNACK(nacks), s.Addr)
|
|
}
|
|
}
|
|
|
|
func (s *Session) Send(payload []byte, reliability Reliability, orderChannel byte) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
maxPayload := int(s.MTU) - 4 - 3
|
|
if reliability.IsReliable() {
|
|
maxPayload -= 3
|
|
}
|
|
if reliability.IsOrdered() || reliability.IsSequenced() {
|
|
maxPayload -= 4
|
|
}
|
|
if maxPayload < 100 {
|
|
maxPayload = 100
|
|
}
|
|
|
|
if len(payload) <= maxPayload {
|
|
f := &Frame{Reliability: reliability, Payload: payload}
|
|
if reliability.IsReliable() {
|
|
f.MessageIndex = s.msgIndex
|
|
s.msgIndex++
|
|
}
|
|
if reliability.IsOrdered() {
|
|
f.OrderIndex = s.orderIndex[orderChannel]
|
|
f.OrderChannel = orderChannel
|
|
s.orderIndex[orderChannel]++
|
|
}
|
|
s.sendDatagram([]*Frame{f})
|
|
return
|
|
}
|
|
|
|
splitID := s.splitID()
|
|
count := (len(payload) + maxPayload - 1) / maxPayload
|
|
orderIdx := s.orderIndex[orderChannel]
|
|
if reliability.IsOrdered() {
|
|
s.orderIndex[orderChannel]++
|
|
}
|
|
for i := 0; i < count; i++ {
|
|
start := i * maxPayload
|
|
end := start + maxPayload
|
|
if end > len(payload) {
|
|
end = len(payload)
|
|
}
|
|
f := &Frame{
|
|
Reliability: reliability,
|
|
Split: true,
|
|
SplitCount: uint32(count),
|
|
SplitID: splitID,
|
|
SplitIndex: uint32(i),
|
|
Payload: make([]byte, end-start),
|
|
}
|
|
copy(f.Payload, payload[start:end])
|
|
if reliability.IsReliable() {
|
|
f.MessageIndex = s.msgIndex
|
|
s.msgIndex++
|
|
}
|
|
if reliability.IsOrdered() {
|
|
f.OrderIndex = orderIdx
|
|
f.OrderChannel = orderChannel
|
|
}
|
|
s.sendDatagram([]*Frame{f})
|
|
}
|
|
}
|
|
|
|
func (s *Session) splitID() uint16 {
|
|
id := uint16(s.msgIndex & 0xffff)
|
|
return id
|
|
}
|
|
|
|
func (s *Session) sendDatagram(frames []*Frame) {
|
|
seq := s.sendSeq
|
|
s.sendSeq++
|
|
dg := &Datagram{SeqNum: seq, Frames: frames}
|
|
pkt := dg.Encode()
|
|
s.conn.WriteToUDP(pkt, s.Addr)
|
|
|
|
hasReliable := false
|
|
for _, f := range frames {
|
|
if f.Reliability.IsReliable() {
|
|
hasReliable = true
|
|
break
|
|
}
|
|
}
|
|
if hasReliable {
|
|
s.recoveryMap[seq] = dg
|
|
}
|
|
}
|
|
|
|
func (s *Session) SendRaw(data []byte) {
|
|
s.conn.WriteToUDP(data, s.Addr)
|
|
}
|
|
|
|
func (s *Session) TimedOut() bool {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
return time.Since(s.lastRecv) > SessionTimeout
|
|
}
|
|
|
|
func (s *Session) Disconnect() {
|
|
s.mu.Lock()
|
|
s.state = StateDisconnected
|
|
seq := s.sendSeq
|
|
s.sendSeq++
|
|
s.mu.Unlock()
|
|
f := &Frame{Reliability: Unreliable, Payload: []byte{0x15}}
|
|
dg := &Datagram{SeqNum: seq, Frames: []*Frame{f}}
|
|
s.conn.WriteToUDP(dg.Encode(), s.Addr)
|
|
}
|