package gameserver import ( "bytes" "encoding/binary" "testing" kcplib "gac-hkrpg-go/pkg/kcp" ) // A single UDP datagram can contain multiple complete KCP messages. Each // handler must receive its own payload even when the receive buffer is reused. func TestConsumeKeepsBatchedPacketBodiesIndependent(t *testing.T) { const firstID, secondID uint16 = 65000, 65001 for _, id := range []uint16{firstID, secondID} { if _, ok := rawHandlers[id]; ok { t.Fatalf("test handler %d already registered", id) } if _, ok := typedHandlers[id]; ok { t.Fatalf("test handler %d already registered", id) } if _, ok := dummyResponses[id]; ok { t.Fatalf("test command %d has dummy response", id) } } got := map[uint16]string{} rawHandlers[firstID] = func(_ *Session, body []byte) error { got[firstID] = string(body); return nil } rawHandlers[secondID] = func(_ *Session, body []byte) error { got[secondID] = string(body); return nil } defer delete(rawHandlers, firstID) defer delete(rawHandlers, secondID) const conv, token uint32 = 77, 88 segment := func(seq uint32, frame []byte) []byte { out := make([]byte, kcplib.Overhead+len(frame)) binary.LittleEndian.PutUint32(out[0:], conv) binary.LittleEndian.PutUint32(out[4:], token) out[8] = kcplib.CmdPush binary.LittleEndian.PutUint16(out[10:], 128) binary.LittleEndian.PutUint32(out[16:], seq) binary.LittleEndian.PutUint32(out[24:], uint32(len(frame))) copy(out[kcplib.Overhead:], frame) return out } first := (&NetPacket{CmdType: firstID, Body: []byte("first")}).Encode() second := (&NetPacket{CmdType: secondID, Body: []byte("other")}).Encode() datagram := append(segment(0, first), segment(1, second)...) s := NewSession(func([]byte) error { return nil }, conv, token) defer s.Close() if err := s.Consume(datagram); err != nil { t.Fatal(err) } if got[firstID] != "first" || got[secondID] != "other" { t.Fatalf("batched payloads corrupted: %#v", got) } } func TestConsumeOutOfOrderFragmentsAndFollowingPacket(t *testing.T) { const firstID, secondID uint16 = 65002, 65003 for _, id := range []uint16{firstID, secondID} { if _, ok := rawHandlers[id]; ok { t.Fatalf("test handler %d already registered", id) } if _, ok := typedHandlers[id]; ok { t.Fatalf("test handler %d already registered", id) } if _, ok := dummyResponses[id]; ok { t.Fatalf("test command %d has dummy response", id) } } got := map[uint16][]byte{} rawHandlers[firstID] = func(_ *Session, body []byte) error { got[firstID] = append([]byte(nil), body...); return nil } rawHandlers[secondID] = func(_ *Session, body []byte) error { got[secondID] = append([]byte(nil), body...); return nil } defer delete(rawHandlers, firstID) defer delete(rawHandlers, secondID) const conv, token uint32 = 77, 88 segment := func(seq uint32, fragment byte, frame []byte) []byte { out := make([]byte, kcplib.Overhead+len(frame)) binary.LittleEndian.PutUint32(out[0:], conv) binary.LittleEndian.PutUint32(out[4:], token) out[8], out[9] = kcplib.CmdPush, fragment binary.LittleEndian.PutUint16(out[10:], 128) binary.LittleEndian.PutUint32(out[16:], seq) binary.LittleEndian.PutUint32(out[24:], uint32(len(frame))) copy(out[kcplib.Overhead:], frame) return out } payload := bytes.Repeat([]byte("first-fragment-"), 120) frame := (&NetPacket{CmdType: firstID, Body: payload}).Encode() following := (&NetPacket{CmdType: secondID, Body: []byte("next")}).Encode() s := NewSession(func([]byte) error { return nil }, conv, token) defer s.Close() if err := s.Consume(segment(1, 0, frame[800:])); err != nil { t.Fatal(err) } if len(got) != 0 { t.Fatal("dispatched an incomplete out-of-order message") } combined := append(segment(0, 1, frame[:800]), segment(2, 0, following)...) if err := s.Consume(combined); err != nil { t.Fatal(err) } if !bytes.Equal(got[firstID], payload) || string(got[secondID]) != "next" { t.Fatal("fragmented or following packet body was corrupted") } }