mirror of
https://github.com/PretendoNetwork/nex-go.git
synced 2025-04-02 11:02:14 -04:00
Compare commits
109 commits
Author | SHA1 | Date | |
---|---|---|---|
|
519e786544 | ||
|
4a55120377 | ||
|
4cb8b49c51 | ||
|
397ebe4144 | ||
|
f34f86f7b7 | ||
|
6ec50052cd | ||
|
a7fb52ae81 | ||
|
7159d9120d | ||
|
be12609cc1 | ||
|
9c8f7bd62e | ||
|
340b851e5f | ||
|
10faf0a38a | ||
|
9a3d0bcbb1 | ||
|
9e5e0332b5 | ||
|
fbe2b91e75 | ||
|
461d2f2846 | ||
|
5dda129a6f | ||
|
6f3f3d1b4a | ||
|
eee4fc9e88 | ||
|
2797330f2f | ||
|
359eb435c2 | ||
|
337fdcdbc4 | ||
|
d3b7a0175f | ||
|
fb784e4e4f | ||
|
b36470e680 | ||
|
37d01ad3d7 | ||
|
a035545093 | ||
|
feee2dd599 | ||
|
4fab7a2c86 | ||
|
4fbbe2da25 | ||
|
27cdd4c7e3 | ||
|
d633dca018 | ||
|
22d2698cef | ||
|
ad488be838 | ||
|
abfca9a10d | ||
|
c214a2a7a5 | ||
|
4fefb05f86 | ||
|
348c37df5d | ||
|
a0107a982a | ||
|
182418f999 | ||
|
2309a6d409 | ||
|
293c20d6be | ||
|
0502cc64b1 | ||
|
9997fb672a | ||
|
0d8506dd52 | ||
|
680abd3cb0 | ||
|
18380590ab | ||
|
2b6ea02749 | ||
|
83e5157350 | ||
|
b57adeb748 | ||
|
090cfb5568 | ||
|
a4b341dab5 | ||
|
1bee30dc05 | ||
|
a1ca41e880 | ||
|
88c51d6f3a | ||
|
2c2dd7a3bd | ||
|
9373fd8723 | ||
|
7f5a097a92 | ||
|
de3610deaf | ||
|
d19d660033 | ||
|
2c48000d5f | ||
|
d668caac02 | ||
|
8271f9acfb | ||
|
1258086480 | ||
|
7dafc92ffc | ||
|
0e3840107f | ||
|
ab4dbfcd52 | ||
|
bfe9c9c702 | ||
|
f7b8a549ba | ||
|
a638798255 | ||
|
832bd19751 | ||
|
99acae026c | ||
|
3a90361903 | ||
|
b0c937dbe4 | ||
|
1f8c5ffeba | ||
|
943f141096 | ||
|
8d306f43f4 | ||
|
daceca2dfb | ||
|
446a730e4c | ||
|
885121a678 | ||
|
c1aa55c3ba | ||
|
3c28fb75de | ||
|
feca72a672 | ||
|
502b6011c6 | ||
|
d5feb922f4 | ||
|
7998d1469d | ||
|
2a775ebf35 | ||
|
7bc3579294 | ||
|
ae41e77566 | ||
|
4f59501d31 | ||
|
a597a37d41 | ||
|
f66fae8827 | ||
|
07dda5708e | ||
|
75c402b7ee | ||
|
24d542506c | ||
|
26c9159397 | ||
|
363cc7b5bf | ||
|
8e0b1a2be4 | ||
|
fabfca0c4b | ||
|
cc2b19e552 | ||
|
44581f1302 | ||
|
2514f63383 | ||
|
8a848c57ba | ||
|
f8b1187ad7 | ||
|
bd05699837 | ||
|
2b0f14b8f1 | ||
|
a76ede10d3 | ||
|
1dc17555f0 | ||
|
4315467d6a |
80 changed files with 2715 additions and 2501 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -23,6 +23,7 @@ go.work.sum
|
|||
|
||||
# custom
|
||||
.vscode
|
||||
.idea
|
||||
.env
|
||||
build
|
||||
log
|
||||
|
|
|
@ -10,15 +10,15 @@ import "github.com/PretendoNetwork/nex-go/v2/types"
|
|||
// guest account, an account which represents the authentication server, and one which represents
|
||||
// the secure server. See https://nintendo-wiki.pretendo.network/docs/nex/kerberos for more information.
|
||||
type Account struct {
|
||||
PID *types.PID // * The PID of the account. PIDs are unique IDs per account. NEX PIDs start at 1800000000 and decrement with each new account.
|
||||
Username string // * The username for the account. For NEX user accounts this is the same as the accounts PID.
|
||||
Password string // * The password for the account. For NEX accounts this is always 16 characters long using seemingly any ASCII character
|
||||
PID types.PID // * The PID of the account. PIDs are unique IDs per account. NEX PIDs start at 1800000000 and decrement with each new account.
|
||||
Username string // * The username for the account. For NEX user accounts this is the same as the accounts PID.
|
||||
Password string // * The password for the account. For NEX accounts this is always 16 characters long using seemingly any ASCII character
|
||||
}
|
||||
|
||||
// NewAccount returns a new instance of Account.
|
||||
// This does not register an account, only creates a new
|
||||
// struct instance.
|
||||
func NewAccount(pid *types.PID, username, password string) *Account {
|
||||
func NewAccount(pid types.PID, username, password string) *Account {
|
||||
return &Account{
|
||||
PID: pid,
|
||||
Username: username,
|
||||
|
|
|
@ -68,8 +68,8 @@ func (bsi *ByteStreamIn) Read(length uint64) ([]byte, error) {
|
|||
return bsi.ReadBytesNext(int64(length)), nil
|
||||
}
|
||||
|
||||
// ReadPrimitiveUInt8 reads a uint8
|
||||
func (bsi *ByteStreamIn) ReadPrimitiveUInt8() (uint8, error) {
|
||||
// ReadUInt8 reads a uint8
|
||||
func (bsi *ByteStreamIn) ReadUInt8() (uint8, error) {
|
||||
if bsi.Remaining() < 1 {
|
||||
return 0, errors.New("Not enough data to read uint8")
|
||||
}
|
||||
|
@ -77,8 +77,8 @@ func (bsi *ByteStreamIn) ReadPrimitiveUInt8() (uint8, error) {
|
|||
return uint8(bsi.ReadByteNext()), nil
|
||||
}
|
||||
|
||||
// ReadPrimitiveUInt16LE reads a Little-Endian encoded uint16
|
||||
func (bsi *ByteStreamIn) ReadPrimitiveUInt16LE() (uint16, error) {
|
||||
// ReadUInt16LE reads a Little-Endian encoded uint16
|
||||
func (bsi *ByteStreamIn) ReadUInt16LE() (uint16, error) {
|
||||
if bsi.Remaining() < 2 {
|
||||
return 0, errors.New("Not enough data to read uint16")
|
||||
}
|
||||
|
@ -86,8 +86,8 @@ func (bsi *ByteStreamIn) ReadPrimitiveUInt16LE() (uint16, error) {
|
|||
return bsi.ReadU16LENext(1)[0], nil
|
||||
}
|
||||
|
||||
// ReadPrimitiveUInt32LE reads a Little-Endian encoded uint32
|
||||
func (bsi *ByteStreamIn) ReadPrimitiveUInt32LE() (uint32, error) {
|
||||
// ReadUInt32LE reads a Little-Endian encoded uint32
|
||||
func (bsi *ByteStreamIn) ReadUInt32LE() (uint32, error) {
|
||||
if bsi.Remaining() < 4 {
|
||||
return 0, errors.New("Not enough data to read uint32")
|
||||
}
|
||||
|
@ -95,8 +95,8 @@ func (bsi *ByteStreamIn) ReadPrimitiveUInt32LE() (uint32, error) {
|
|||
return bsi.ReadU32LENext(1)[0], nil
|
||||
}
|
||||
|
||||
// ReadPrimitiveUInt64LE reads a Little-Endian encoded uint64
|
||||
func (bsi *ByteStreamIn) ReadPrimitiveUInt64LE() (uint64, error) {
|
||||
// ReadUInt64LE reads a Little-Endian encoded uint64
|
||||
func (bsi *ByteStreamIn) ReadUInt64LE() (uint64, error) {
|
||||
if bsi.Remaining() < 8 {
|
||||
return 0, errors.New("Not enough data to read uint64")
|
||||
}
|
||||
|
@ -104,8 +104,8 @@ func (bsi *ByteStreamIn) ReadPrimitiveUInt64LE() (uint64, error) {
|
|||
return bsi.ReadU64LENext(1)[0], nil
|
||||
}
|
||||
|
||||
// ReadPrimitiveInt8 reads a uint8
|
||||
func (bsi *ByteStreamIn) ReadPrimitiveInt8() (int8, error) {
|
||||
// ReadInt8 reads a uint8
|
||||
func (bsi *ByteStreamIn) ReadInt8() (int8, error) {
|
||||
if bsi.Remaining() < 1 {
|
||||
return 0, errors.New("Not enough data to read int8")
|
||||
}
|
||||
|
@ -113,8 +113,8 @@ func (bsi *ByteStreamIn) ReadPrimitiveInt8() (int8, error) {
|
|||
return int8(bsi.ReadByteNext()), nil
|
||||
}
|
||||
|
||||
// ReadPrimitiveInt16LE reads a Little-Endian encoded int16
|
||||
func (bsi *ByteStreamIn) ReadPrimitiveInt16LE() (int16, error) {
|
||||
// ReadInt16LE reads a Little-Endian encoded int16
|
||||
func (bsi *ByteStreamIn) ReadInt16LE() (int16, error) {
|
||||
if bsi.Remaining() < 2 {
|
||||
return 0, errors.New("Not enough data to read int16")
|
||||
}
|
||||
|
@ -122,8 +122,8 @@ func (bsi *ByteStreamIn) ReadPrimitiveInt16LE() (int16, error) {
|
|||
return int16(bsi.ReadU16LENext(1)[0]), nil
|
||||
}
|
||||
|
||||
// ReadPrimitiveInt32LE reads a Little-Endian encoded int32
|
||||
func (bsi *ByteStreamIn) ReadPrimitiveInt32LE() (int32, error) {
|
||||
// ReadInt32LE reads a Little-Endian encoded int32
|
||||
func (bsi *ByteStreamIn) ReadInt32LE() (int32, error) {
|
||||
if bsi.Remaining() < 4 {
|
||||
return 0, errors.New("Not enough data to read int32")
|
||||
}
|
||||
|
@ -131,8 +131,8 @@ func (bsi *ByteStreamIn) ReadPrimitiveInt32LE() (int32, error) {
|
|||
return int32(bsi.ReadU32LENext(1)[0]), nil
|
||||
}
|
||||
|
||||
// ReadPrimitiveInt64LE reads a Little-Endian encoded int64
|
||||
func (bsi *ByteStreamIn) ReadPrimitiveInt64LE() (int64, error) {
|
||||
// ReadInt64LE reads a Little-Endian encoded int64
|
||||
func (bsi *ByteStreamIn) ReadInt64LE() (int64, error) {
|
||||
if bsi.Remaining() < 8 {
|
||||
return 0, errors.New("Not enough data to read int64")
|
||||
}
|
||||
|
@ -140,8 +140,8 @@ func (bsi *ByteStreamIn) ReadPrimitiveInt64LE() (int64, error) {
|
|||
return int64(bsi.ReadU64LENext(1)[0]), nil
|
||||
}
|
||||
|
||||
// ReadPrimitiveFloat32LE reads a Little-Endian encoded float32
|
||||
func (bsi *ByteStreamIn) ReadPrimitiveFloat32LE() (float32, error) {
|
||||
// ReadFloat32LE reads a Little-Endian encoded float32
|
||||
func (bsi *ByteStreamIn) ReadFloat32LE() (float32, error) {
|
||||
if bsi.Remaining() < 4 {
|
||||
return 0, errors.New("Not enough data to read float32")
|
||||
}
|
||||
|
@ -149,8 +149,8 @@ func (bsi *ByteStreamIn) ReadPrimitiveFloat32LE() (float32, error) {
|
|||
return bsi.ReadF32LENext(1)[0], nil
|
||||
}
|
||||
|
||||
// ReadPrimitiveFloat64LE reads a Little-Endian encoded float64
|
||||
func (bsi *ByteStreamIn) ReadPrimitiveFloat64LE() (float64, error) {
|
||||
// ReadFloat64LE reads a Little-Endian encoded float64
|
||||
func (bsi *ByteStreamIn) ReadFloat64LE() (float64, error) {
|
||||
if bsi.Remaining() < 8 {
|
||||
return 0, errors.New("Not enough data to read float64")
|
||||
}
|
||||
|
@ -158,8 +158,8 @@ func (bsi *ByteStreamIn) ReadPrimitiveFloat64LE() (float64, error) {
|
|||
return bsi.ReadF64LENext(1)[0], nil
|
||||
}
|
||||
|
||||
// ReadPrimitiveBool reads a bool
|
||||
func (bsi *ByteStreamIn) ReadPrimitiveBool() (bool, error) {
|
||||
// ReadBool reads a bool
|
||||
func (bsi *ByteStreamIn) ReadBool() (bool, error) {
|
||||
if bsi.Remaining() < 1 {
|
||||
return false, errors.New("Not enough data to read bool")
|
||||
}
|
||||
|
|
|
@ -56,68 +56,68 @@ func (bso *ByteStreamOut) Write(data []byte) {
|
|||
bso.WriteBytesNext(data)
|
||||
}
|
||||
|
||||
// WritePrimitiveUInt8 writes a uint8
|
||||
func (bso *ByteStreamOut) WritePrimitiveUInt8(u8 uint8) {
|
||||
// WriteUInt8 writes a uint8
|
||||
func (bso *ByteStreamOut) WriteUInt8(u8 uint8) {
|
||||
bso.Grow(1)
|
||||
bso.WriteByteNext(byte(u8))
|
||||
}
|
||||
|
||||
// WritePrimitiveUInt16LE writes a uint16 as LE
|
||||
func (bso *ByteStreamOut) WritePrimitiveUInt16LE(u16 uint16) {
|
||||
// WriteUInt16LE writes a uint16 as LE
|
||||
func (bso *ByteStreamOut) WriteUInt16LE(u16 uint16) {
|
||||
bso.Grow(2)
|
||||
bso.WriteU16LENext([]uint16{u16})
|
||||
}
|
||||
|
||||
// WritePrimitiveUInt32LE writes a uint32 as LE
|
||||
func (bso *ByteStreamOut) WritePrimitiveUInt32LE(u32 uint32) {
|
||||
// WriteUInt32LE writes a uint32 as LE
|
||||
func (bso *ByteStreamOut) WriteUInt32LE(u32 uint32) {
|
||||
bso.Grow(4)
|
||||
bso.WriteU32LENext([]uint32{u32})
|
||||
}
|
||||
|
||||
// WritePrimitiveUInt64LE writes a uint64 as LE
|
||||
func (bso *ByteStreamOut) WritePrimitiveUInt64LE(u64 uint64) {
|
||||
// WriteUInt64LE writes a uint64 as LE
|
||||
func (bso *ByteStreamOut) WriteUInt64LE(u64 uint64) {
|
||||
bso.Grow(8)
|
||||
bso.WriteU64LENext([]uint64{u64})
|
||||
}
|
||||
|
||||
// WritePrimitiveInt8 writes a int8
|
||||
func (bso *ByteStreamOut) WritePrimitiveInt8(s8 int8) {
|
||||
// WriteInt8 writes a int8
|
||||
func (bso *ByteStreamOut) WriteInt8(s8 int8) {
|
||||
bso.Grow(1)
|
||||
bso.WriteByteNext(byte(s8))
|
||||
}
|
||||
|
||||
// WritePrimitiveInt16LE writes a uint16 as LE
|
||||
func (bso *ByteStreamOut) WritePrimitiveInt16LE(s16 int16) {
|
||||
// WriteInt16LE writes a uint16 as LE
|
||||
func (bso *ByteStreamOut) WriteInt16LE(s16 int16) {
|
||||
bso.Grow(2)
|
||||
bso.WriteU16LENext([]uint16{uint16(s16)})
|
||||
}
|
||||
|
||||
// WritePrimitiveInt32LE writes a int32 as LE
|
||||
func (bso *ByteStreamOut) WritePrimitiveInt32LE(s32 int32) {
|
||||
// WriteInt32LE writes a int32 as LE
|
||||
func (bso *ByteStreamOut) WriteInt32LE(s32 int32) {
|
||||
bso.Grow(4)
|
||||
bso.WriteU32LENext([]uint32{uint32(s32)})
|
||||
}
|
||||
|
||||
// WritePrimitiveInt64LE writes a int64 as LE
|
||||
func (bso *ByteStreamOut) WritePrimitiveInt64LE(s64 int64) {
|
||||
// WriteInt64LE writes a int64 as LE
|
||||
func (bso *ByteStreamOut) WriteInt64LE(s64 int64) {
|
||||
bso.Grow(8)
|
||||
bso.WriteU64LENext([]uint64{uint64(s64)})
|
||||
}
|
||||
|
||||
// WritePrimitiveFloat32LE writes a float32 as LE
|
||||
func (bso *ByteStreamOut) WritePrimitiveFloat32LE(f32 float32) {
|
||||
// WriteFloat32LE writes a float32 as LE
|
||||
func (bso *ByteStreamOut) WriteFloat32LE(f32 float32) {
|
||||
bso.Grow(4)
|
||||
bso.WriteF32LENext([]float32{f32})
|
||||
}
|
||||
|
||||
// WritePrimitiveFloat64LE writes a float64 as LE
|
||||
func (bso *ByteStreamOut) WritePrimitiveFloat64LE(f64 float64) {
|
||||
// WriteFloat64LE writes a float64 as LE
|
||||
func (bso *ByteStreamOut) WriteFloat64LE(f64 float64) {
|
||||
bso.Grow(8)
|
||||
bso.WriteF64LENext([]float64{f64})
|
||||
}
|
||||
|
||||
// WritePrimitiveBool writes a bool
|
||||
func (bso *ByteStreamOut) WritePrimitiveBool(b bool) {
|
||||
// WriteBool writes a bool
|
||||
func (bso *ByteStreamOut) WriteBool(b bool) {
|
||||
var bVar uint8
|
||||
if b {
|
||||
bVar = 1
|
||||
|
|
|
@ -11,6 +11,6 @@ import (
|
|||
type ConnectionInterface interface {
|
||||
Endpoint() EndpointInterface
|
||||
Address() net.Addr
|
||||
PID() *types.PID
|
||||
SetPID(pid *types.PID)
|
||||
PID() types.PID
|
||||
SetPID(pid types.PID)
|
||||
}
|
||||
|
|
24
go.mod
24
go.mod
|
@ -1,23 +1,29 @@
|
|||
module github.com/PretendoNetwork/nex-go/v2
|
||||
|
||||
go 1.21
|
||||
go 1.22.0
|
||||
|
||||
toolchain go1.23.4
|
||||
|
||||
require (
|
||||
github.com/PretendoNetwork/plogger-go v1.0.4
|
||||
github.com/lxzan/gws v1.8.3
|
||||
github.com/lxzan/gws v1.8.8
|
||||
github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/superwhiskers/crunch/v3 v3.5.7
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
||||
golang.org/x/mod v0.17.0
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8
|
||||
golang.org/x/mod v0.22.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dolthub/maphash v0.1.0 // indirect
|
||||
github.com/fatih/color v1.17.0 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/jwalton/go-supportscolor v1.2.0 // indirect
|
||||
github.com/klauspost/compress v1.17.8 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/term v0.20.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/term v0.28.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
36
go.sum
36
go.sum
|
@ -4,19 +4,18 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dolthub/maphash v0.1.0 h1:bsQ7JsF4FkkWyrP3oCnFJgrCUAFbFf3kOl4L/QxPDyQ=
|
||||
github.com/dolthub/maphash v0.1.0/go.mod h1:gkg4Ch4CdCDu5h6PMriVLawB7koZ+5ijb9puGMV50a4=
|
||||
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/jwalton/go-supportscolor v1.2.0 h1:g6Ha4u7Vm3LIsQ5wmeBpS4gazu0UP1DRDE8y6bre4H8=
|
||||
github.com/jwalton/go-supportscolor v1.2.0/go.mod h1:hFVUAZV2cWg+WFFC4v8pT2X/S2qUUBYMioBD9AINXGs=
|
||||
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
||||
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/lxzan/gws v1.8.3 h1:umX2VLhXj7oVV4Sd2gCuqzrzpVWtqkKMy0tjHBBxXg0=
|
||||
github.com/lxzan/gws v1.8.3/go.mod h1:FcGeRMB7HwGuTvMLR24ku0Zx0p6RXqeKASeMc4VYgi4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/lxzan/gws v1.8.8 h1:st193ZG8qN8sSw8/g/UituFhs7etmKzS7jUqhijg5wM=
|
||||
github.com/lxzan/gws v1.8.8/go.mod h1:FcGeRMB7HwGuTvMLR24ku0Zx0p6RXqeKASeMc4VYgi4=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
@ -27,18 +26,19 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
|
|||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/superwhiskers/crunch/v3 v3.5.7 h1:N9RLxaR65C36i26BUIpzPXGy2f6pQ7wisu2bawbKNqg=
|
||||
github.com/superwhiskers/crunch/v3 v3.5.7/go.mod h1:4ub2EKgF1MAhTjoOCTU4b9uLMsAweHEa89aRrfAypXA=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA=
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
type HPPClient struct {
|
||||
address *net.TCPAddr
|
||||
endpoint *HPPServer
|
||||
pid *types.PID
|
||||
pid types.PID
|
||||
}
|
||||
|
||||
// Endpoint returns the server the client is connecting to
|
||||
|
@ -24,12 +24,12 @@ func (c *HPPClient) Address() net.Addr {
|
|||
}
|
||||
|
||||
// PID returns the clients NEX PID
|
||||
func (c *HPPClient) PID() *types.PID {
|
||||
func (c *HPPClient) PID() types.PID {
|
||||
return c.pid
|
||||
}
|
||||
|
||||
// SetPID sets the clients NEX PID
|
||||
func (c *HPPClient) SetPID(pid *types.PID) {
|
||||
func (c *HPPClient) SetPID(pid types.PID) {
|
||||
c.pid = pid
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ type HPPServer struct {
|
|||
dataHandlers []func(packet PacketInterface)
|
||||
errorEventHandlers []func(err *Error)
|
||||
byteStreamSettings *ByteStreamSettings
|
||||
AccountDetailsByPID func(pid *types.PID) (*Account, *Error)
|
||||
AccountDetailsByPID func(pid types.PID) (*Account, *Error)
|
||||
AccountDetailsByUsername func(username string) (*Account, *Error)
|
||||
useVerboseRMC bool
|
||||
}
|
||||
|
|
8
init.go
8
init.go
|
@ -10,10 +10,10 @@ var logger = plogger.NewLogger()
|
|||
func init() {
|
||||
initResultCodes()
|
||||
|
||||
types.RegisterVariantType(1, types.NewPrimitiveS64(0))
|
||||
types.RegisterVariantType(2, types.NewPrimitiveF64(0))
|
||||
types.RegisterVariantType(3, types.NewPrimitiveBool(false))
|
||||
types.RegisterVariantType(1, types.NewInt64(0))
|
||||
types.RegisterVariantType(2, types.NewDouble(0))
|
||||
types.RegisterVariantType(3, types.NewBool(false))
|
||||
types.RegisterVariantType(4, types.NewString(""))
|
||||
types.RegisterVariantType(5, types.NewDateTime(0))
|
||||
types.RegisterVariantType(6, types.NewPrimitiveU64(0))
|
||||
types.RegisterVariantType(6, types.NewUInt64(0))
|
||||
}
|
||||
|
|
23
kerberos.go
23
kerberos.go
|
@ -69,8 +69,8 @@ func NewKerberosEncryption(key []byte) *KerberosEncryption {
|
|||
// KerberosTicket represents a ticket granting a user access to a secure server
|
||||
type KerberosTicket struct {
|
||||
SessionKey []byte
|
||||
TargetPID *types.PID
|
||||
InternalData *types.Buffer
|
||||
TargetPID types.PID
|
||||
InternalData types.Buffer
|
||||
}
|
||||
|
||||
// Encrypt writes the ticket data to the provided stream and returns the encrypted byte slice
|
||||
|
@ -94,8 +94,8 @@ func NewKerberosTicket() *KerberosTicket {
|
|||
// KerberosTicketInternalData holds the internal data for a kerberos ticket to be processed by the server
|
||||
type KerberosTicketInternalData struct {
|
||||
Server *PRUDPServer // TODO - Remove this dependency and make a settings struct
|
||||
Issued *types.DateTime
|
||||
SourcePID *types.PID
|
||||
Issued types.DateTime
|
||||
SourcePID types.PID
|
||||
SessionKey []byte
|
||||
}
|
||||
|
||||
|
@ -152,10 +152,10 @@ func (ti *KerberosTicketInternalData) Decrypt(stream *ByteStreamIn, key []byte)
|
|||
return fmt.Errorf("Failed to read Kerberos ticket internal data. %s", err.Error())
|
||||
}
|
||||
|
||||
hash := md5.Sum(append(key, ticketKey.Value...))
|
||||
hash := md5.Sum(append(key, ticketKey...))
|
||||
key = hash[:]
|
||||
|
||||
stream = NewByteStreamIn(data.Value, stream.LibraryVersions, stream.Settings)
|
||||
stream = NewByteStreamIn(data, stream.LibraryVersions, stream.Settings)
|
||||
}
|
||||
|
||||
encryption := NewKerberosEncryption(key)
|
||||
|
@ -190,12 +190,15 @@ func NewKerberosTicketInternalData(server *PRUDPServer) *KerberosTicketInternalD
|
|||
}
|
||||
|
||||
// DeriveKerberosKey derives a users kerberos encryption key based on their PID and password
|
||||
func DeriveKerberosKey(pid *types.PID, password []byte) []byte {
|
||||
func DeriveKerberosKey(pid types.PID, password []byte) []byte {
|
||||
iterationCount := int(65000 + pid%1024)
|
||||
key := password
|
||||
hash := make([]byte, md5.Size)
|
||||
|
||||
for i := 0; i < 65000+int(pid.Value())%1024; i++ {
|
||||
hash := md5.Sum(key)
|
||||
key = hash[:]
|
||||
for i := 0; i < iterationCount; i++ {
|
||||
sum := md5.Sum(key)
|
||||
copy(hash, sum[:])
|
||||
key = hash
|
||||
}
|
||||
|
||||
return key
|
||||
|
|
16
kerberos_test.go
Normal file
16
kerberos_test.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
package nex
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDeriveGuestKey(t *testing.T) {
|
||||
pid := types.NewPID(100)
|
||||
password := []byte("MMQea3n!fsik")
|
||||
result := DeriveKerberosKey(pid, password)
|
||||
assert.Equal(t, "9ef318f0a170fb46aab595bf9644f9e1", hex.EncodeToString(result))
|
||||
}
|
16
mutex_map.go
16
mutex_map.go
|
@ -26,6 +26,22 @@ func (m *MutexMap[K, V]) Get(key K) (V, bool) {
|
|||
return value, ok
|
||||
}
|
||||
|
||||
// GetOrSetDefault returns the value for the given key if it exists. If it does not exist, it creates a default
|
||||
// with the provided function and sets it for that key
|
||||
func (m *MutexMap[K, V]) GetOrSetDefault(key K, createDefault func() V) V {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
value, ok := m.real[key]
|
||||
|
||||
if !ok {
|
||||
value = createDefault()
|
||||
m.real[key] = value
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
// Has checks if a key exists in the map
|
||||
func (m *MutexMap[K, V]) Has(key K) bool {
|
||||
m.RLock()
|
||||
|
|
44
packet_dispatch_queue.go
Normal file
44
packet_dispatch_queue.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
package nex
|
||||
|
||||
// PacketDispatchQueue is an implementation of rdv::PacketDispatchQueue.
|
||||
// PacketDispatchQueue is used to sequence incoming packets.
|
||||
// In the original library each virtual connection stream only uses a single PacketDispatchQueue, but starting
|
||||
// in PRUDPv1 NEX virtual connections may have multiple reliable substreams and thus multiple PacketDispatchQueues.
|
||||
type PacketDispatchQueue struct {
|
||||
queue map[uint16]PRUDPPacketInterface
|
||||
nextExpectedSequenceId *Counter[uint16]
|
||||
}
|
||||
|
||||
// Queue adds a packet to the queue to be dispatched
|
||||
func (pdq *PacketDispatchQueue) Queue(packet PRUDPPacketInterface) {
|
||||
pdq.queue[packet.SequenceID()] = packet
|
||||
}
|
||||
|
||||
// GetNextToDispatch returns the next packet to be dispatched, nil if there are no packets
|
||||
// and a boolean indicating whether anything was returned.
|
||||
func (pdq *PacketDispatchQueue) GetNextToDispatch() (PRUDPPacketInterface, bool) {
|
||||
if packet, ok := pdq.queue[pdq.nextExpectedSequenceId.Value]; ok {
|
||||
return packet, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Dispatched removes a packet from the queue to be dispatched.
|
||||
func (pdq *PacketDispatchQueue) Dispatched(packet PRUDPPacketInterface) {
|
||||
pdq.nextExpectedSequenceId.Next()
|
||||
delete(pdq.queue, packet.SequenceID())
|
||||
}
|
||||
|
||||
// Purge clears the queue of all pending packets.
|
||||
func (pdq *PacketDispatchQueue) Purge() {
|
||||
clear(pdq.queue)
|
||||
}
|
||||
|
||||
// NewPacketDispatchQueue initializes a new PacketDispatchQueue with a starting counter value.
|
||||
func NewPacketDispatchQueue() *PacketDispatchQueue {
|
||||
return &PacketDispatchQueue{
|
||||
queue: make(map[uint16]PRUDPPacketInterface),
|
||||
nextExpectedSequenceId: NewCounter[uint16](2), // * First DATA packet from a client will always be 2 as the CONNECT packet is assigned 1
|
||||
}
|
||||
}
|
63
packet_dispatch_queue_test.go
Normal file
63
packet_dispatch_queue_test.go
Normal file
|
@ -0,0 +1,63 @@
|
|||
package nex
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestReorderPackets(t *testing.T) {
|
||||
pdq := NewPacketDispatchQueue()
|
||||
|
||||
packet1 := makePacket(2)
|
||||
packet2 := makePacket(3)
|
||||
packet3 := makePacket(4)
|
||||
|
||||
pdq.Queue(packet2)
|
||||
pdq.Queue(packet3)
|
||||
pdq.Queue(packet1)
|
||||
|
||||
result, ok := pdq.GetNextToDispatch()
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, packet1, result)
|
||||
pdq.Dispatched(packet1)
|
||||
|
||||
result, ok = pdq.GetNextToDispatch()
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, packet2, result)
|
||||
pdq.Dispatched(packet2)
|
||||
|
||||
result, ok = pdq.GetNextToDispatch()
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, packet3, result)
|
||||
pdq.Dispatched(packet3)
|
||||
|
||||
result, ok = pdq.GetNextToDispatch()
|
||||
assert.False(t, ok)
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestCallingInLoop(t *testing.T) {
|
||||
pdq := NewPacketDispatchQueue()
|
||||
|
||||
packet1 := makePacket(2)
|
||||
packet2 := makePacket(3)
|
||||
packet3 := makePacket(4)
|
||||
|
||||
pdq.Queue(packet2)
|
||||
pdq.Queue(packet3)
|
||||
pdq.Queue(packet1)
|
||||
|
||||
for nextPacket, ok := pdq.GetNextToDispatch(); ok; nextPacket, ok = pdq.GetNextToDispatch() {
|
||||
pdq.Dispatched(nextPacket)
|
||||
}
|
||||
|
||||
assert.Equal(t, uint16(5), pdq.nextExpectedSequenceId.Value)
|
||||
}
|
||||
|
||||
func makePacket(sequenceID uint16) PRUDPPacketInterface {
|
||||
packet, _ := NewPRUDPPacketV0(nil, nil, nil)
|
||||
packet.SetSequenceID(sequenceID)
|
||||
|
||||
return packet
|
||||
}
|
|
@ -3,6 +3,7 @@ package nex
|
|||
import (
|
||||
"crypto/md5"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2/constants"
|
||||
|
@ -16,24 +17,29 @@ type PRUDPConnection struct {
|
|||
Socket *SocketConnection // * The connections parent socket
|
||||
endpoint *PRUDPEndPoint // * The PRUDP endpoint the connection is connected to
|
||||
ConnectionState ConnectionState
|
||||
ID uint32 // * Connection ID
|
||||
SessionID uint8 // * Random value generated at the start of the session. Client and server IDs do not need to match
|
||||
ServerSessionID uint8 // * Random value generated at the start of the session. Client and server IDs do not need to match
|
||||
SessionKey []byte // * Secret key generated at the start of the session. Used for encrypting packets to the secure server
|
||||
pid *types.PID // * PID of the user
|
||||
DefaultPRUDPVersion int // * The PRUDP version the connection was established with. Used for sending PING packets
|
||||
StreamType constants.StreamType // * rdv::Stream::Type used in this connection
|
||||
StreamID uint8 // * rdv::Stream ID, also called the "port number", used in this connection. 0-15 on PRUDPv0/v1, and 0-31 on PRUDPLite
|
||||
StreamSettings *StreamSettings // * Settings for this virtual connection
|
||||
Signature []byte // * Connection signature for packets coming from the client, as seen by the server
|
||||
ServerConnectionSignature []byte // * Connection signature for packets coming from the server, as seen by the client
|
||||
UnreliablePacketBaseKey []byte // * The base key used for encrypting unreliable DATA packets
|
||||
slidingWindows *MutexMap[uint8, *SlidingWindow] // * Reliable packet substreams
|
||||
ID uint32 // * Connection ID
|
||||
SessionID uint8 // * Random value generated at the start of the session. Client and server IDs do not need to match
|
||||
ServerSessionID uint8 // * Random value generated at the start of the session. Client and server IDs do not need to match
|
||||
SessionKey []byte // * Secret key generated at the start of the session. Used for encrypting packets to the secure server
|
||||
pid types.PID // * PID of the user
|
||||
DefaultPRUDPVersion int // * The PRUDP version the connection was established with. Used for sending PING packets
|
||||
StreamType constants.StreamType // * rdv::Stream::Type used in this connection
|
||||
StreamID uint8 // * rdv::Stream ID, also called the "port number", used in this connection. 0-15 on PRUDPv0/v1, and 0-31 on PRUDPLite
|
||||
StreamSettings *StreamSettings // * Settings for this virtual connection
|
||||
Signature []byte // * Connection signature for packets coming from the client, as seen by the server
|
||||
ServerConnectionSignature []byte // * Connection signature for packets coming from the server, as seen by the client
|
||||
UnreliablePacketBaseKey []byte // * The base key used for encrypting unreliable DATA packets
|
||||
rtt *RTT // * The round-trip transmission time of this connection
|
||||
slidingWindows *MutexMap[uint8, *SlidingWindow] // * Outbound reliable packet substreams
|
||||
packetDispatchQueues *MutexMap[uint8, *PacketDispatchQueue] // * Inbound reliable packet substreams
|
||||
incomingFragmentBuffers *MutexMap[uint8, []byte] // * Buffers which store the incoming payloads from fragmented DATA packets
|
||||
outgoingUnreliableSequenceIDCounter *Counter[uint16]
|
||||
outgoingPingSequenceIDCounter *Counter[uint16]
|
||||
lastSentPingTime time.Time
|
||||
heartbeatTimer *time.Timer
|
||||
pingKickTimer *time.Timer
|
||||
StationURLs *types.List[*types.StationURL]
|
||||
StationURLs types.List[types.StationURL]
|
||||
mutex *sync.Mutex
|
||||
}
|
||||
|
||||
// Endpoint returns the PRUDP endpoint the connections socket is connected to
|
||||
|
@ -47,19 +53,24 @@ func (pc *PRUDPConnection) Address() net.Addr {
|
|||
}
|
||||
|
||||
// PID returns the clients unique PID
|
||||
func (pc *PRUDPConnection) PID() *types.PID {
|
||||
func (pc *PRUDPConnection) PID() types.PID {
|
||||
return pc.pid
|
||||
}
|
||||
|
||||
// SetPID sets the clients unique PID
|
||||
func (pc *PRUDPConnection) SetPID(pid *types.PID) {
|
||||
func (pc *PRUDPConnection) SetPID(pid types.PID) {
|
||||
pc.pid = pid
|
||||
}
|
||||
|
||||
// reset resets the connection state to all zero values
|
||||
func (pc *PRUDPConnection) reset() {
|
||||
pc.ConnectionState = StateNotConnected
|
||||
pc.packetDispatchQueues.Clear(func(_ uint8, packetDispatchQueue *PacketDispatchQueue) {
|
||||
packetDispatchQueue.Purge()
|
||||
})
|
||||
|
||||
pc.slidingWindows.Clear(func(_ uint8, slidingWindow *SlidingWindow) {
|
||||
slidingWindow.ResendScheduler.Stop()
|
||||
slidingWindow.TimeoutManager.Stop()
|
||||
})
|
||||
|
||||
pc.Signature = make([]byte, 0)
|
||||
|
@ -75,19 +86,10 @@ func (pc *PRUDPConnection) cleanup() {
|
|||
|
||||
pc.stopHeartbeatTimers()
|
||||
|
||||
pc.Socket.Connections.Delete(pc.SessionID)
|
||||
|
||||
pc.endpoint.emitConnectionEnded(pc)
|
||||
|
||||
if pc.Socket.Connections.Size() == 0 {
|
||||
// * No more PRUDP connections, assume the socket connection is also closed
|
||||
pc.endpoint.Server.Connections.Delete(pc.Socket.Address.String())
|
||||
// TODO - Is there any other cleanup that needs to happen here?
|
||||
// TODO - Should we add an event for when a socket closes too?
|
||||
}
|
||||
}
|
||||
|
||||
// InitializeSlidingWindows returns the InitializeSlidingWindows for the given substream
|
||||
// InitializeSlidingWindows initializes the SlidingWindows for all substreams
|
||||
func (pc *PRUDPConnection) InitializeSlidingWindows(maxSubstreamID uint8) {
|
||||
// * Nuke any existing SlidingWindows
|
||||
pc.slidingWindows = NewMutexMap[uint8, *SlidingWindow]()
|
||||
|
@ -97,11 +99,21 @@ func (pc *PRUDPConnection) InitializeSlidingWindows(maxSubstreamID uint8) {
|
|||
}
|
||||
}
|
||||
|
||||
// CreateSlidingWindow returns the CreateSlidingWindow for the given substream
|
||||
// InitializePacketDispatchQueues initializes the PacketDispatchQueues for all substreams
|
||||
func (pc *PRUDPConnection) InitializePacketDispatchQueues(maxSubstreamID uint8) {
|
||||
// * Nuke any existing PacketDispatchQueues
|
||||
pc.packetDispatchQueues = NewMutexMap[uint8, *PacketDispatchQueue]()
|
||||
|
||||
for i := 0; i < int(maxSubstreamID+1); i++ {
|
||||
pc.CreatePacketDispatchQueue(uint8(i))
|
||||
}
|
||||
}
|
||||
|
||||
// CreateSlidingWindow creates a new SlidingWindow for the given substream and returns it
|
||||
// if there is not a SlidingWindow for the given substream id it creates a new one
|
||||
func (pc *PRUDPConnection) CreateSlidingWindow(substreamID uint8) *SlidingWindow {
|
||||
slidingWindow := NewSlidingWindow()
|
||||
slidingWindow.incomingSequenceIDCounter = NewCounter[uint16](2) // * First DATA packet from the client has sequence ID 2
|
||||
slidingWindow.outgoingSequenceIDCounter = NewCounter[uint16](0) // * First DATA packet from the server has sequence ID 1 (start counter at 0 and is incremeneted)
|
||||
slidingWindow.sequenceIDCounter = NewCounter[uint16](0) // * First DATA packet from the server has sequence ID 1 (start counter at 0 and is incremeneted)
|
||||
slidingWindow.streamSettings = pc.StreamSettings.Copy()
|
||||
|
||||
pc.slidingWindows.Set(substreamID, slidingWindow)
|
||||
|
@ -123,6 +135,28 @@ func (pc *PRUDPConnection) SlidingWindow(substreamID uint8) *SlidingWindow {
|
|||
return slidingWindow
|
||||
}
|
||||
|
||||
// CreatePacketDispatchQueue creates a new PacketDispatchQueue for the given substream and returns it
|
||||
func (pc *PRUDPConnection) CreatePacketDispatchQueue(substreamID uint8) *PacketDispatchQueue {
|
||||
pdq := NewPacketDispatchQueue()
|
||||
pc.packetDispatchQueues.Set(substreamID, pdq)
|
||||
return pdq
|
||||
}
|
||||
|
||||
// PacketDispatchQueue returns the PacketDispatchQueue for the given substream
|
||||
// if there is not a PacketDispatchQueue for the given substream it creates a new one
|
||||
func (pc *PRUDPConnection) PacketDispatchQueue(substreamID uint8) *PacketDispatchQueue {
|
||||
packetDispatchQueue, ok := pc.packetDispatchQueues.Get(substreamID)
|
||||
if !ok {
|
||||
// * Fail-safe. The connection may not always have
|
||||
// * the correct number of substreams. See the
|
||||
// * comment in handleSocketMessage of PRUDPEndPoint
|
||||
// * for more details
|
||||
packetDispatchQueue = pc.CreatePacketDispatchQueue(substreamID)
|
||||
}
|
||||
|
||||
return packetDispatchQueue
|
||||
}
|
||||
|
||||
// setSessionKey sets the connection's session key and updates the SlidingWindows
|
||||
func (pc *PRUDPConnection) setSessionKey(sessionKey []byte) {
|
||||
pc.SessionKey = sessionKey
|
||||
|
@ -174,6 +208,41 @@ func (pc *PRUDPConnection) resetHeartbeat() {
|
|||
}
|
||||
}
|
||||
|
||||
// Lock locks the inner mutex for the Connection
|
||||
// This is used internally when reordering incoming fragmented packets to prevent
|
||||
// race conditions when multiple packets for the same fragmented message are processed at once
|
||||
func (pc *PRUDPConnection) Lock() {
|
||||
pc.mutex.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks the inner mutex for the Connection
|
||||
// This is used internally when reordering incoming fragmented packets to prevent
|
||||
// race conditions when multiple packets for the same fragmented message are processed at once
|
||||
func (pc *PRUDPConnection) Unlock() {
|
||||
pc.mutex.Unlock()
|
||||
}
|
||||
|
||||
// Gets the incoming fragment buffer for the given substream
|
||||
func (pc *PRUDPConnection) GetIncomingFragmentBuffer(substreamID uint8) []byte {
|
||||
buffer, ok := pc.incomingFragmentBuffers.Get(substreamID)
|
||||
if !ok {
|
||||
buffer = make([]byte, 0)
|
||||
pc.incomingFragmentBuffers.Set(substreamID, buffer)
|
||||
}
|
||||
|
||||
return buffer
|
||||
}
|
||||
|
||||
// Sets the incoming fragment buffer for a given substream
|
||||
func (pc *PRUDPConnection) SetIncomingFragmentBuffer(substreamID uint8, buffer []byte) {
|
||||
pc.incomingFragmentBuffers.Set(substreamID, buffer)
|
||||
}
|
||||
|
||||
// Clears the outgoing buffer for a given substream
|
||||
func (pc *PRUDPConnection) ClearOutgoingBuffer(substreamID uint8) {
|
||||
pc.incomingFragmentBuffers.Set(substreamID, make([]byte, 0))
|
||||
}
|
||||
|
||||
func (pc *PRUDPConnection) startHeartbeat() {
|
||||
endpoint := pc.endpoint
|
||||
|
||||
|
@ -192,9 +261,7 @@ func (pc *PRUDPConnection) startHeartbeat() {
|
|||
// * If the heartbeat still did not restart, assume the
|
||||
// * connection is dead and clean up
|
||||
pc.pingKickTimer = time.AfterFunc(maxSilenceTime, func() {
|
||||
pc.cleanup() // * "removed" event is dispatched here
|
||||
|
||||
endpoint.deleteConnectionByID(pc.ID)
|
||||
endpoint.cleanupConnection(pc)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -214,14 +281,16 @@ func NewPRUDPConnection(socket *SocketConnection) *PRUDPConnection {
|
|||
pc := &PRUDPConnection{
|
||||
Socket: socket,
|
||||
ConnectionState: StateNotConnected,
|
||||
rtt: NewRTT(),
|
||||
pid: types.NewPID(0),
|
||||
slidingWindows: NewMutexMap[uint8, *SlidingWindow](),
|
||||
packetDispatchQueues: NewMutexMap[uint8, *PacketDispatchQueue](),
|
||||
outgoingUnreliableSequenceIDCounter: NewCounter[uint16](1),
|
||||
outgoingPingSequenceIDCounter: NewCounter[uint16](0),
|
||||
StationURLs: types.NewList[*types.StationURL](),
|
||||
incomingFragmentBuffers: NewMutexMap[uint8, []byte](),
|
||||
StationURLs: types.NewList[types.StationURL](),
|
||||
mutex: &sync.Mutex{},
|
||||
}
|
||||
|
||||
pc.StationURLs.Type = types.NewStationURL("")
|
||||
|
||||
return pc
|
||||
}
|
||||
|
|
|
@ -19,21 +19,26 @@ import (
|
|||
// and secure servers. However the functionality of rdv::PRUDPEndPoint and nn::nex::SecureEndPoint is seemingly
|
||||
// identical. Rather than duplicate the logic from PRUDPEndpoint, a IsSecureEndpoint flag has been added instead.
|
||||
type PRUDPEndPoint struct {
|
||||
Server *PRUDPServer
|
||||
StreamID uint8
|
||||
DefaultStreamSettings *StreamSettings
|
||||
Connections *MutexMap[string, *PRUDPConnection]
|
||||
packetHandlers map[uint16]func(packet PRUDPPacketInterface)
|
||||
packetEventHandlers map[string][]func(packet PacketInterface)
|
||||
connectionEndedEventHandlers []func(connection *PRUDPConnection)
|
||||
errorEventHandlers []func(err *Error)
|
||||
ConnectionIDCounter *Counter[uint32]
|
||||
ServerAccount *Account
|
||||
AccountDetailsByPID func(pid *types.PID) (*Account, *Error)
|
||||
AccountDetailsByUsername func(username string) (*Account, *Error)
|
||||
IsSecureEndPoint bool
|
||||
Server *PRUDPServer
|
||||
StreamID uint8
|
||||
DefaultStreamSettings *StreamSettings
|
||||
Connections *MutexMap[string, *PRUDPConnection]
|
||||
packetHandlers map[uint16]func(packet PRUDPPacketInterface)
|
||||
packetEventHandlers map[string][]func(packet PacketInterface)
|
||||
connectionEndedEventHandlers []func(connection *PRUDPConnection)
|
||||
errorEventHandlers []func(err *Error)
|
||||
ConnectionIDCounter *Counter[uint32]
|
||||
ServerAccount *Account
|
||||
AccountDetailsByPID func(pid types.PID) (*Account, *Error)
|
||||
AccountDetailsByUsername func(username string) (*Account, *Error)
|
||||
IsSecureEndPoint bool
|
||||
CalcRetransmissionTimeoutCallback CalcRetransmissionTimeoutCallback
|
||||
}
|
||||
|
||||
// CalcRetransmissionTimeoutCallback is an optional callback which can be used to override the RTO calculation
|
||||
// for packets sent by this `PRUDPEndpoint`
|
||||
type CalcRetransmissionTimeoutCallback func(rtt float64, sendCount uint32) time.Duration
|
||||
|
||||
// RegisterServiceProtocol registers a NEX service with the endpoint
|
||||
func (pep *PRUDPEndPoint) RegisterServiceProtocol(protocol ServiceProtocol) {
|
||||
protocol.SetEndpoint(pep)
|
||||
|
@ -82,51 +87,63 @@ func (pep *PRUDPEndPoint) on(name string, handler func(packet PacketInterface))
|
|||
func (pep *PRUDPEndPoint) emit(name string, packet PRUDPPacketInterface) {
|
||||
if handlers, ok := pep.packetEventHandlers[name]; ok {
|
||||
for _, handler := range handlers {
|
||||
go handler(packet)
|
||||
handler(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pep *PRUDPEndPoint) emitConnectionEnded(connection *PRUDPConnection) {
|
||||
for _, handler := range pep.connectionEndedEventHandlers {
|
||||
go handler(connection)
|
||||
handler(connection)
|
||||
}
|
||||
}
|
||||
|
||||
// EmitError calls all the endpoints error event handlers with the provided error
|
||||
func (pep *PRUDPEndPoint) EmitError(err *Error) {
|
||||
for _, handler := range pep.errorEventHandlers {
|
||||
go handler(err)
|
||||
handler(err)
|
||||
}
|
||||
}
|
||||
|
||||
// deleteConnectionByID deletes the connection with the specified ID
|
||||
func (pep *PRUDPEndPoint) deleteConnectionByID(cid uint32) {
|
||||
pep.Connections.DeleteIf(func(key string, value *PRUDPConnection) bool {
|
||||
return value.ID == cid
|
||||
// cleanupConnection cleans up and deletes a connection from this endpoint. Will lock the Connections mutex - make sure
|
||||
// you don't hold it during a call, or this will deadlock
|
||||
func (pep *PRUDPEndPoint) cleanupConnection(connection *PRUDPConnection) {
|
||||
discriminator := fmt.Sprintf("%s-%d-%d", connection.Socket.Address.String(), connection.StreamType, connection.StreamID)
|
||||
|
||||
found := false
|
||||
pep.Connections.RunAndDelete(discriminator, func(key string, conn *PRUDPConnection) {
|
||||
found = true
|
||||
})
|
||||
|
||||
// * Probably this connection is on a different PRUDPEndPoint
|
||||
if !found {
|
||||
logger.Warningf("Tried to delete connection %v (ID %v) but it doesn't exist!", discriminator, connection.ID)
|
||||
}
|
||||
|
||||
// * We can't do this during RunAndDelete, since we hold the Connections mutex then
|
||||
// * This way we avoid any recursive locking
|
||||
connection.cleanup()
|
||||
}
|
||||
|
||||
func (pep *PRUDPEndPoint) processPacket(packet PRUDPPacketInterface, socket *SocketConnection) {
|
||||
streamType := packet.SourceVirtualPortStreamType()
|
||||
streamID := packet.SourceVirtualPortStreamID()
|
||||
discriminator := fmt.Sprintf("%s-%d-%d", socket.Address.String(), streamType, streamID)
|
||||
connection, ok := pep.Connections.Get(discriminator)
|
||||
|
||||
if !ok {
|
||||
connection = NewPRUDPConnection(socket)
|
||||
connection := pep.Connections.GetOrSetDefault(discriminator, func() *PRUDPConnection {
|
||||
connection := NewPRUDPConnection(socket)
|
||||
connection.endpoint = pep
|
||||
connection.ID = pep.ConnectionIDCounter.Next()
|
||||
connection.DefaultPRUDPVersion = packet.Version()
|
||||
connection.StreamType = streamType
|
||||
connection.StreamID = streamID
|
||||
connection.StreamSettings = pep.DefaultStreamSettings.Copy()
|
||||
return connection
|
||||
})
|
||||
|
||||
pep.Connections.Set(discriminator, connection)
|
||||
}
|
||||
connection.Lock()
|
||||
defer connection.Unlock()
|
||||
|
||||
packet.SetSender(connection)
|
||||
connection.resetHeartbeat()
|
||||
|
||||
if packet.HasFlag(constants.PacketFlagAck) || packet.HasFlag(constants.PacketFlagMultiAck) {
|
||||
pep.handleAcknowledgment(packet)
|
||||
|
@ -142,19 +159,26 @@ func (pep *PRUDPEndPoint) processPacket(packet PRUDPPacketInterface, socket *Soc
|
|||
|
||||
func (pep *PRUDPEndPoint) handleAcknowledgment(packet PRUDPPacketInterface) {
|
||||
connection := packet.Sender().(*PRUDPConnection)
|
||||
if connection.ConnectionState != StateConnected {
|
||||
// TODO - Log this?
|
||||
// * Connection is in a bad state, drop the packet and let it die
|
||||
|
||||
if connection.ConnectionState < StateConnected {
|
||||
return
|
||||
}
|
||||
|
||||
connection.resetHeartbeat()
|
||||
|
||||
if packet.HasFlag(constants.PacketFlagMultiAck) {
|
||||
pep.handleMultiAcknowledgment(packet)
|
||||
return
|
||||
}
|
||||
|
||||
slidingWindow := connection.SlidingWindow(packet.SubstreamID())
|
||||
slidingWindow.ResendScheduler.AcknowledgePacket(packet.SequenceID())
|
||||
if packet.Type() == constants.PingPacket {
|
||||
if packet.SequenceID() == connection.outgoingPingSequenceIDCounter.Value {
|
||||
connection.rtt.Adjust(time.Since(connection.lastSentPingTime))
|
||||
}
|
||||
} else {
|
||||
slidingWindow := connection.SlidingWindow(packet.SubstreamID())
|
||||
slidingWindow.TimeoutManager.AcknowledgePacket(packet.SequenceID())
|
||||
}
|
||||
}
|
||||
|
||||
func (pep *PRUDPEndPoint) handleMultiAcknowledgment(packet PRUDPPacketInterface) {
|
||||
|
@ -167,13 +191,13 @@ func (pep *PRUDPEndPoint) handleMultiAcknowledgment(packet PRUDPPacketInterface)
|
|||
if packet.SubstreamID() == 1 {
|
||||
// * New aggregate acknowledgment packets set this to 1
|
||||
// * and encode the real substream ID in in the payload
|
||||
substreamID, _ := stream.ReadPrimitiveUInt8()
|
||||
additionalIDsCount, _ := stream.ReadPrimitiveUInt8()
|
||||
baseSequenceID, _ = stream.ReadPrimitiveUInt16LE()
|
||||
substreamID, _ := stream.ReadUInt8()
|
||||
additionalIDsCount, _ := stream.ReadUInt8()
|
||||
baseSequenceID, _ = stream.ReadUInt16LE()
|
||||
slidingWindow = connection.SlidingWindow(substreamID)
|
||||
|
||||
for i := 0; i < int(additionalIDsCount); i++ {
|
||||
additionalID, _ := stream.ReadPrimitiveUInt16LE()
|
||||
additionalID, _ := stream.ReadUInt16LE()
|
||||
sequenceIDs = append(sequenceIDs, additionalID)
|
||||
}
|
||||
} else {
|
||||
|
@ -184,14 +208,14 @@ func (pep *PRUDPEndPoint) handleMultiAcknowledgment(packet PRUDPPacketInterface)
|
|||
baseSequenceID = packet.SequenceID()
|
||||
|
||||
for stream.Remaining() > 0 {
|
||||
additionalID, _ := stream.ReadPrimitiveUInt16LE()
|
||||
additionalID, _ := stream.ReadUInt16LE()
|
||||
sequenceIDs = append(sequenceIDs, additionalID)
|
||||
}
|
||||
}
|
||||
|
||||
// * MutexMap.Each locks the mutex, can't remove while reading.
|
||||
// * Have to just loop again
|
||||
slidingWindow.ResendScheduler.packets.Each(func(sequenceID uint16, pending *PendingPacket) bool {
|
||||
slidingWindow.TimeoutManager.packets.Each(func(sequenceID uint16, pending PRUDPPacketInterface) bool {
|
||||
if sequenceID <= baseSequenceID && !slices.Contains(sequenceIDs, sequenceID) {
|
||||
sequenceIDs = append(sequenceIDs, sequenceID)
|
||||
}
|
||||
|
@ -201,18 +225,13 @@ func (pep *PRUDPEndPoint) handleMultiAcknowledgment(packet PRUDPPacketInterface)
|
|||
|
||||
// * Actually remove the packets from the pool
|
||||
for _, sequenceID := range sequenceIDs {
|
||||
slidingWindow.ResendScheduler.AcknowledgePacket(sequenceID)
|
||||
slidingWindow.TimeoutManager.AcknowledgePacket(sequenceID)
|
||||
}
|
||||
}
|
||||
|
||||
func (pep *PRUDPEndPoint) handleSyn(packet PRUDPPacketInterface) {
|
||||
connection := packet.Sender().(*PRUDPConnection)
|
||||
|
||||
if connection.ConnectionState != StateNotConnected {
|
||||
// TODO - Log this?
|
||||
// * Connection is in a bad state, drop the packet and let it die
|
||||
return
|
||||
}
|
||||
connection.resetHeartbeat()
|
||||
|
||||
var ack PRUDPPacketInterface
|
||||
|
||||
|
@ -259,12 +278,12 @@ func (pep *PRUDPEndPoint) handleSyn(packet PRUDPPacketInterface) {
|
|||
func (pep *PRUDPEndPoint) handleConnect(packet PRUDPPacketInterface) {
|
||||
connection := packet.Sender().(*PRUDPConnection)
|
||||
|
||||
if connection.ConnectionState != StateConnecting {
|
||||
// TODO - Log this?
|
||||
// * Connection is in a bad state, drop the packet and let it die
|
||||
if connection.ConnectionState < StateConnecting {
|
||||
return
|
||||
}
|
||||
|
||||
connection.resetHeartbeat()
|
||||
|
||||
var ack PRUDPPacketInterface
|
||||
|
||||
if packet.Version() == 2 {
|
||||
|
@ -306,9 +325,11 @@ func (pep *PRUDPEndPoint) handleConnect(packet PRUDPPacketInterface) {
|
|||
ack.supportedFunctions = packet.(*PRUDPPacketV1).supportedFunctions
|
||||
|
||||
connection.InitializeSlidingWindows(ack.maximumSubstreamID)
|
||||
connection.InitializePacketDispatchQueues(ack.maximumSubstreamID)
|
||||
connection.outgoingUnreliableSequenceIDCounter = NewCounter[uint16](packet.(*PRUDPPacketV1).initialUnreliableSequenceID)
|
||||
} else {
|
||||
connection.InitializeSlidingWindows(0)
|
||||
connection.InitializePacketDispatchQueues(0)
|
||||
}
|
||||
|
||||
payload := make([]byte, 0)
|
||||
|
@ -385,12 +406,12 @@ func (pep *PRUDPEndPoint) handleConnect(packet PRUDPPacketInterface) {
|
|||
func (pep *PRUDPEndPoint) handleData(packet PRUDPPacketInterface) {
|
||||
connection := packet.Sender().(*PRUDPConnection)
|
||||
|
||||
if connection.ConnectionState != StateConnected {
|
||||
// TODO - Log this?
|
||||
// * Connection is in a bad state, drop the packet and let it die
|
||||
if connection.ConnectionState < StateConnected {
|
||||
return
|
||||
}
|
||||
|
||||
connection.resetHeartbeat()
|
||||
|
||||
if packet.HasFlag(constants.PacketFlagReliable) {
|
||||
pep.handleReliable(packet)
|
||||
} else {
|
||||
|
@ -400,7 +421,6 @@ func (pep *PRUDPEndPoint) handleData(packet PRUDPPacketInterface) {
|
|||
|
||||
func (pep *PRUDPEndPoint) handleDisconnect(packet PRUDPPacketInterface) {
|
||||
// TODO - Should we check the state here, or just let the connection disconnect at any time?
|
||||
// TODO - Should we bother to set the connections state here? It's being destroyed anyway
|
||||
|
||||
if packet.HasFlag(constants.PacketFlagNeedsAck) {
|
||||
pep.acknowledgePacket(packet)
|
||||
|
@ -410,47 +430,60 @@ func (pep *PRUDPEndPoint) handleDisconnect(packet PRUDPPacketInterface) {
|
|||
streamID := packet.SourceVirtualPortStreamID()
|
||||
discriminator := fmt.Sprintf("%s-%d-%d", packet.Sender().Address().String(), streamType, streamID)
|
||||
if connection, ok := pep.Connections.Get(discriminator); ok {
|
||||
connection.cleanup()
|
||||
pep.Connections.Delete(discriminator)
|
||||
pep.cleanupConnection(connection)
|
||||
}
|
||||
|
||||
pep.emit("disconnect", packet)
|
||||
}
|
||||
|
||||
func (pep *PRUDPEndPoint) handlePing(packet PRUDPPacketInterface) {
|
||||
connection := packet.Sender().(*PRUDPConnection)
|
||||
|
||||
if connection.ConnectionState < StateConnected {
|
||||
return
|
||||
}
|
||||
|
||||
connection.resetHeartbeat()
|
||||
|
||||
if packet.HasFlag(constants.PacketFlagNeedsAck) {
|
||||
pep.acknowledgePacket(packet)
|
||||
}
|
||||
|
||||
if packet.HasFlag(constants.PacketFlagReliable) {
|
||||
substreamID := packet.SubstreamID()
|
||||
packetDispatchQueue := connection.PacketDispatchQueue(substreamID)
|
||||
packetDispatchQueue.Queue(packet)
|
||||
}
|
||||
}
|
||||
|
||||
func (pep *PRUDPEndPoint) readKerberosTicket(payload []byte) ([]byte, *types.PID, uint32, error) {
|
||||
func (pep *PRUDPEndPoint) readKerberosTicket(payload []byte) ([]byte, types.PID, uint32, error) {
|
||||
stream := NewByteStreamIn(payload, pep.Server.LibraryVersions, pep.ByteStreamSettings())
|
||||
|
||||
ticketData := types.NewBuffer(nil)
|
||||
if err := ticketData.ExtractFrom(stream); err != nil {
|
||||
return nil, nil, 0, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
requestData := types.NewBuffer(nil)
|
||||
if err := requestData.ExtractFrom(stream); err != nil {
|
||||
return nil, nil, 0, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
// * Sanity checks
|
||||
serverAccount, _ := pep.AccountDetailsByUsername(pep.ServerAccount.Username)
|
||||
if serverAccount == nil {
|
||||
return nil, nil, 0, errors.New("Failed to find endpoint server account")
|
||||
return nil, 0, 0, errors.New("Failed to find endpoint server account")
|
||||
}
|
||||
|
||||
if serverAccount.Password != pep.ServerAccount.Password {
|
||||
return nil, nil, 0, errors.New("Password for endpoint server account does not match the records from AccountDetailsByUsername")
|
||||
return nil, 0, 0, errors.New("Password for endpoint server account does not match the records from AccountDetailsByUsername")
|
||||
}
|
||||
|
||||
serverKey := DeriveKerberosKey(serverAccount.PID, []byte(serverAccount.Password))
|
||||
|
||||
ticket := NewKerberosTicketInternalData(pep.Server)
|
||||
if err := ticket.Decrypt(NewByteStreamIn(ticketData.Value, pep.Server.LibraryVersions, pep.ByteStreamSettings()), serverKey); err != nil {
|
||||
return nil, nil, 0, err
|
||||
if err := ticket.Decrypt(NewByteStreamIn(ticketData, pep.Server.LibraryVersions, pep.ByteStreamSettings()), serverKey); err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
ticketTime := ticket.Issued.Standard()
|
||||
|
@ -458,32 +491,36 @@ func (pep *PRUDPEndPoint) readKerberosTicket(payload []byte) ([]byte, *types.PID
|
|||
|
||||
timeLimit := ticketTime.Add(time.Minute * 2)
|
||||
if serverTime.After(timeLimit) {
|
||||
return nil, nil, 0, errors.New("Kerberos ticket expired")
|
||||
return nil, 0, 0, errors.New("Kerberos ticket expired")
|
||||
}
|
||||
|
||||
sessionKey := ticket.SessionKey
|
||||
kerberos := NewKerberosEncryption(sessionKey)
|
||||
|
||||
decryptedRequestData, err := kerberos.Decrypt(requestData.Value)
|
||||
decryptedRequestData, err := kerberos.Decrypt(requestData)
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
checkDataStream := NewByteStreamIn(decryptedRequestData, pep.Server.LibraryVersions, pep.ByteStreamSettings())
|
||||
|
||||
userPID := types.NewPID(0)
|
||||
if err := userPID.ExtractFrom(checkDataStream); err != nil {
|
||||
return nil, nil, 0, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
_, err = checkDataStream.ReadPrimitiveUInt32LE() // * CID of secure server station url
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
if userPID != ticket.SourcePID {
|
||||
return nil, 0, 0, errors.New("User PID and ticket source PID mismatch")
|
||||
}
|
||||
|
||||
responseCheck, err := checkDataStream.ReadPrimitiveUInt32LE()
|
||||
_, err = checkDataStream.ReadUInt32LE() // * CID of secure server station url
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
responseCheck, err := checkDataStream.ReadUInt32LE()
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
|
||||
return sessionKey, userPID, responseCheck, nil
|
||||
|
@ -526,17 +563,20 @@ func (pep *PRUDPEndPoint) handleReliable(packet PRUDPPacketInterface) {
|
|||
|
||||
connection := packet.Sender().(*PRUDPConnection)
|
||||
|
||||
slidingWindow := packet.Sender().(*PRUDPConnection).SlidingWindow(packet.SubstreamID())
|
||||
substreamID := packet.SubstreamID()
|
||||
|
||||
for _, pendingPacket := range slidingWindow.Update(packet) {
|
||||
if packet.Type() == constants.DataPacket {
|
||||
packetDispatchQueue := connection.PacketDispatchQueue(substreamID)
|
||||
packetDispatchQueue.Queue(packet)
|
||||
|
||||
for nextPacket, ok := packetDispatchQueue.GetNextToDispatch(); ok; nextPacket, ok = packetDispatchQueue.GetNextToDispatch() {
|
||||
if nextPacket.Type() == constants.DataPacket {
|
||||
var decryptedPayload []byte
|
||||
|
||||
if packet.Version() != 2 {
|
||||
decryptedPayload = pendingPacket.decryptPayload()
|
||||
if nextPacket.Version() != 2 {
|
||||
decryptedPayload = nextPacket.decryptPayload()
|
||||
} else {
|
||||
// * PRUDPLite does not encrypt payloads
|
||||
decryptedPayload = pendingPacket.Payload()
|
||||
decryptedPayload = nextPacket.Payload()
|
||||
}
|
||||
|
||||
decompressedPayload, err := connection.StreamSettings.CompressionAlgorithm.Decompress(decryptedPayload)
|
||||
|
@ -544,23 +584,26 @@ func (pep *PRUDPEndPoint) handleReliable(packet PRUDPPacketInterface) {
|
|||
logger.Error(err.Error())
|
||||
}
|
||||
|
||||
payload := slidingWindow.AddFragment(decompressedPayload)
|
||||
incomingFragmentBuffer := connection.GetIncomingFragmentBuffer(substreamID)
|
||||
incomingFragmentBuffer = append(incomingFragmentBuffer, decompressedPayload...)
|
||||
connection.SetIncomingFragmentBuffer(substreamID, incomingFragmentBuffer)
|
||||
|
||||
if packet.getFragmentID() == 0 {
|
||||
if nextPacket.getFragmentID() == 0 {
|
||||
message := NewRMCMessage(pep)
|
||||
err := message.FromBytes(payload)
|
||||
err := message.FromBytes(incomingFragmentBuffer)
|
||||
if err != nil {
|
||||
// TODO - Should this return the error too?
|
||||
logger.Error(err.Error())
|
||||
}
|
||||
|
||||
slidingWindow.ResetFragmentedPayload()
|
||||
nextPacket.SetRMCMessage(message)
|
||||
connection.ClearOutgoingBuffer(substreamID)
|
||||
|
||||
packet.SetRMCMessage(message)
|
||||
|
||||
pep.emit("data", packet)
|
||||
pep.emit("data", nextPacket)
|
||||
}
|
||||
}
|
||||
|
||||
packetDispatchQueue.Dispatched(nextPacket)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -669,7 +712,7 @@ func (pep *PRUDPEndPoint) FindConnectionByPID(pid uint64) *PRUDPConnection {
|
|||
var connection *PRUDPConnection
|
||||
|
||||
pep.Connections.Each(func(discriminator string, pc *PRUDPConnection) bool {
|
||||
if pc.pid.Value() == pid {
|
||||
if uint64(pc.pid) == pid && pc.ConnectionState == StateConnected {
|
||||
connection = pc
|
||||
return true
|
||||
}
|
||||
|
@ -680,6 +723,39 @@ func (pep *PRUDPEndPoint) FindConnectionByPID(pid uint64) *PRUDPConnection {
|
|||
return connection
|
||||
}
|
||||
|
||||
// ComputeRetransmitTimeout computes the RTO (Retransmit timeout) for a given packet
|
||||
func (pep *PRUDPEndPoint) ComputeRetransmitTimeout(packet PRUDPPacketInterface) time.Duration {
|
||||
connection := packet.Sender().(*PRUDPConnection)
|
||||
rtt := connection.rtt
|
||||
|
||||
if callback := pep.CalcRetransmissionTimeoutCallback; callback != nil {
|
||||
rttAverage := rtt.GetRTTSmoothedAvg()
|
||||
rttDeviation := rtt.GetRTTSmoothedDev()
|
||||
return callback(rttAverage+rttDeviation*4.0, packet.SendCount())
|
||||
}
|
||||
|
||||
var retransmitTimeBase int64
|
||||
if packet.Type() == constants.SynPacket {
|
||||
retransmitTimeBase = int64(pep.DefaultStreamSettings.SynInitialRTT)
|
||||
} else {
|
||||
retransmitTimeBase = int64(pep.DefaultStreamSettings.InitialRTT)
|
||||
if rtt.Initialized() {
|
||||
retransmitTimeBase = int64(rtt.Average()/time.Millisecond) / 8
|
||||
}
|
||||
}
|
||||
|
||||
retransmitTimeBaseMultiplier := packet.SendCount()
|
||||
|
||||
var retransmitMultiplier float64
|
||||
if packet.SendCount() < pep.DefaultStreamSettings.ExtraRetransmitTimeoutTrigger {
|
||||
retransmitMultiplier = float64(pep.DefaultStreamSettings.RetransmitTimeoutMultiplier)
|
||||
} else {
|
||||
retransmitMultiplier = float64(pep.DefaultStreamSettings.ExtraRetransmitTimeoutMultiplier)
|
||||
}
|
||||
|
||||
return time.Duration(float64(retransmitTimeBase*int64(retransmitTimeBaseMultiplier))*retransmitMultiplier) * time.Millisecond
|
||||
}
|
||||
|
||||
// AccessKey returns the servers sandbox access key
|
||||
func (pep *PRUDPEndPoint) AccessKey() string {
|
||||
return pep.Server.AccessKey
|
||||
|
|
|
@ -2,6 +2,7 @@ package nex
|
|||
|
||||
import (
|
||||
"crypto/rc4"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2/constants"
|
||||
)
|
||||
|
@ -24,6 +25,9 @@ type PRUDPPacket struct {
|
|||
fragmentID uint8
|
||||
payload []byte
|
||||
message *RMCMessage
|
||||
sendCount uint32
|
||||
sentAt time.Time
|
||||
timeout *Timeout
|
||||
}
|
||||
|
||||
// SetSender sets the Client who sent the packet
|
||||
|
@ -184,6 +188,32 @@ func (p *PRUDPPacket) SetRMCMessage(message *RMCMessage) {
|
|||
p.message = message
|
||||
}
|
||||
|
||||
// SendCount returns the number of times this packet has been sent
|
||||
func (p *PRUDPPacket) SendCount() uint32 {
|
||||
return p.sendCount
|
||||
}
|
||||
|
||||
func (p *PRUDPPacket) incrementSendCount() {
|
||||
p.sendCount++
|
||||
}
|
||||
|
||||
// SentAt returns the latest time that this packet has been sent
|
||||
func (p *PRUDPPacket) SentAt() time.Time {
|
||||
return p.sentAt
|
||||
}
|
||||
|
||||
func (p *PRUDPPacket) setSentAt(time time.Time) {
|
||||
p.sentAt = time
|
||||
}
|
||||
|
||||
func (p *PRUDPPacket) getTimeout() *Timeout {
|
||||
return p.timeout
|
||||
}
|
||||
|
||||
func (p *PRUDPPacket) setTimeout(timeout *Timeout) {
|
||||
p.timeout = timeout
|
||||
}
|
||||
|
||||
func (p *PRUDPPacket) processUnreliableCrypto() []byte {
|
||||
// * Since unreliable DATA packets can come in out of
|
||||
// * order, each packet uses a dedicated RC4 stream
|
||||
|
|
|
@ -2,6 +2,7 @@ package nex
|
|||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2/constants"
|
||||
)
|
||||
|
@ -36,6 +37,12 @@ type PRUDPPacketInterface interface {
|
|||
SetPayload(payload []byte)
|
||||
RMCMessage() *RMCMessage
|
||||
SetRMCMessage(message *RMCMessage)
|
||||
SendCount() uint32
|
||||
incrementSendCount()
|
||||
SentAt() time.Time
|
||||
setSentAt(time time.Time)
|
||||
getTimeout() *Timeout
|
||||
setTimeout(timeout *Timeout)
|
||||
decode() error
|
||||
setSignature(signature []byte)
|
||||
calculateConnectionSignature(addr net.Addr) ([]byte, error)
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
|
@ -117,7 +118,7 @@ func (p *PRUDPPacketLite) Version() int {
|
|||
|
||||
// decode parses the packets data
|
||||
func (p *PRUDPPacketLite) decode() error {
|
||||
magic, err := p.readStream.ReadPrimitiveUInt8()
|
||||
magic, err := p.readStream.ReadUInt8()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read PRUDPLite magic. %s", err.Error())
|
||||
}
|
||||
|
@ -126,17 +127,17 @@ func (p *PRUDPPacketLite) decode() error {
|
|||
return fmt.Errorf("Invalid PRUDPLite magic. Expected 0x80, got 0x%x", magic)
|
||||
}
|
||||
|
||||
p.optionsLength, err = p.readStream.ReadPrimitiveUInt8()
|
||||
p.optionsLength, err = p.readStream.ReadUInt8()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to decode PRUDPLite options length. %s", err.Error())
|
||||
}
|
||||
|
||||
payloadLength, err := p.readStream.ReadPrimitiveUInt16LE()
|
||||
payloadLength, err := p.readStream.ReadUInt16LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to decode PRUDPLite payload length. %s", err.Error())
|
||||
}
|
||||
|
||||
streamTypes, err := p.readStream.ReadPrimitiveUInt8()
|
||||
streamTypes, err := p.readStream.ReadUInt8()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to decode PRUDPLite virtual ports stream types. %s", err.Error())
|
||||
}
|
||||
|
@ -144,22 +145,22 @@ func (p *PRUDPPacketLite) decode() error {
|
|||
p.sourceVirtualPortStreamType = constants.StreamType(streamTypes >> 4)
|
||||
p.destinationVirtualPortStreamType = constants.StreamType(streamTypes & 0xF)
|
||||
|
||||
p.sourceVirtualPortStreamID, err = p.readStream.ReadPrimitiveUInt8()
|
||||
p.sourceVirtualPortStreamID, err = p.readStream.ReadUInt8()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to decode PRUDPLite virtual source port. %s", err.Error())
|
||||
}
|
||||
|
||||
p.destinationVirtualPortStreamID, err = p.readStream.ReadPrimitiveUInt8()
|
||||
p.destinationVirtualPortStreamID, err = p.readStream.ReadUInt8()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to decode PRUDPLite virtual destination port. %s", err.Error())
|
||||
}
|
||||
|
||||
p.fragmentID, err = p.readStream.ReadPrimitiveUInt8()
|
||||
p.fragmentID, err = p.readStream.ReadUInt8()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to decode PRUDPLite fragment ID. %s", err.Error())
|
||||
}
|
||||
|
||||
typeAndFlags, err := p.readStream.ReadPrimitiveUInt16LE()
|
||||
typeAndFlags, err := p.readStream.ReadUInt16LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read PRUDPLite type and flags. %s", err.Error())
|
||||
}
|
||||
|
@ -167,7 +168,7 @@ func (p *PRUDPPacketLite) decode() error {
|
|||
p.flags = typeAndFlags >> 4
|
||||
p.packetType = typeAndFlags & 0xF
|
||||
|
||||
p.sequenceID, err = p.readStream.ReadPrimitiveUInt16LE()
|
||||
p.sequenceID, err = p.readStream.ReadUInt16LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to decode PRUDPLite sequence ID. %s", err.Error())
|
||||
}
|
||||
|
@ -177,6 +178,10 @@ func (p *PRUDPPacketLite) decode() error {
|
|||
return fmt.Errorf("Failed to decode PRUDPLite options. %s", err.Error())
|
||||
}
|
||||
|
||||
if p.readStream.Remaining() < uint64(payloadLength) {
|
||||
return errors.New("Failed to read PRUDPLite payload. Not have enough data")
|
||||
}
|
||||
|
||||
p.payload = p.readStream.ReadBytesNext(int64(payloadLength))
|
||||
|
||||
return nil
|
||||
|
@ -188,15 +193,15 @@ func (p *PRUDPPacketLite) Bytes() []byte {
|
|||
|
||||
stream := NewByteStreamOut(p.server.LibraryVersions, p.server.ByteStreamSettings)
|
||||
|
||||
stream.WritePrimitiveUInt8(0x80)
|
||||
stream.WritePrimitiveUInt8(uint8(len(options)))
|
||||
stream.WritePrimitiveUInt16LE(uint16(len(p.payload)))
|
||||
stream.WritePrimitiveUInt8(uint8((p.sourceVirtualPortStreamType << 4) | p.destinationVirtualPortStreamType))
|
||||
stream.WritePrimitiveUInt8(p.sourceVirtualPortStreamID)
|
||||
stream.WritePrimitiveUInt8(p.destinationVirtualPortStreamID)
|
||||
stream.WritePrimitiveUInt8(p.fragmentID)
|
||||
stream.WritePrimitiveUInt16LE(p.packetType | (p.flags << 4))
|
||||
stream.WritePrimitiveUInt16LE(p.sequenceID)
|
||||
stream.WriteUInt8(0x80)
|
||||
stream.WriteUInt8(uint8(len(options)))
|
||||
stream.WriteUInt16LE(uint16(len(p.payload)))
|
||||
stream.WriteUInt8(uint8((p.sourceVirtualPortStreamType << 4) | p.destinationVirtualPortStreamType))
|
||||
stream.WriteUInt8(p.sourceVirtualPortStreamID)
|
||||
stream.WriteUInt8(p.destinationVirtualPortStreamID)
|
||||
stream.WriteUInt8(p.fragmentID)
|
||||
stream.WriteUInt16LE(p.packetType | (p.flags << 4))
|
||||
stream.WriteUInt16LE(p.sequenceID)
|
||||
|
||||
stream.Grow(int64(len(options)))
|
||||
stream.WriteBytesNext(options)
|
||||
|
@ -208,52 +213,64 @@ func (p *PRUDPPacketLite) Bytes() []byte {
|
|||
}
|
||||
|
||||
func (p *PRUDPPacketLite) decodeOptions() error {
|
||||
if p.readStream.Remaining() < uint64(p.optionsLength) {
|
||||
return errors.New("Not have enough data")
|
||||
}
|
||||
|
||||
data := p.readStream.ReadBytesNext(int64(p.optionsLength))
|
||||
optionsStream := NewByteStreamIn(data, p.server.LibraryVersions, p.server.ByteStreamSettings)
|
||||
|
||||
for optionsStream.Remaining() > 0 {
|
||||
optionID, err := optionsStream.ReadPrimitiveUInt8()
|
||||
optionID, err := optionsStream.ReadUInt8()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
optionSize, err := optionsStream.ReadPrimitiveUInt8() // * Options size. We already know the size based on the ID, though
|
||||
optionSize, err := optionsStream.ReadUInt8() // * Options size. We already know the size based on the ID, though
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.packetType == constants.SynPacket || p.packetType == constants.ConnectPacket {
|
||||
if optionID == 0 {
|
||||
p.supportedFunctions, err = optionsStream.ReadPrimitiveUInt32LE()
|
||||
p.supportedFunctions, err = optionsStream.ReadUInt32LE()
|
||||
|
||||
p.minorVersion = p.supportedFunctions & 0xFF
|
||||
p.supportedFunctions = p.supportedFunctions >> 8
|
||||
}
|
||||
|
||||
if optionID == 1 {
|
||||
p.connectionSignature = optionsStream.ReadBytesNext(int64(optionSize))
|
||||
if optionsStream.Remaining() < uint64(optionSize) {
|
||||
err = errors.New("Failed to read connection signature. Not have enough data")
|
||||
} else {
|
||||
p.connectionSignature = optionsStream.ReadBytesNext(int64(optionSize))
|
||||
}
|
||||
}
|
||||
|
||||
if optionID == 4 {
|
||||
p.maximumSubstreamID, err = optionsStream.ReadPrimitiveUInt8()
|
||||
p.maximumSubstreamID, err = optionsStream.ReadUInt8()
|
||||
}
|
||||
}
|
||||
|
||||
if p.packetType == constants.ConnectPacket {
|
||||
if optionID == 3 {
|
||||
p.initialUnreliableSequenceID, err = optionsStream.ReadPrimitiveUInt16LE()
|
||||
p.initialUnreliableSequenceID, err = optionsStream.ReadUInt16LE()
|
||||
}
|
||||
}
|
||||
|
||||
if p.packetType == constants.DataPacket {
|
||||
if optionID == 2 {
|
||||
p.fragmentID, err = optionsStream.ReadPrimitiveUInt8()
|
||||
p.fragmentID, err = optionsStream.ReadUInt8()
|
||||
}
|
||||
}
|
||||
|
||||
if p.packetType == constants.ConnectPacket && !p.HasFlag(constants.PacketFlagAck) {
|
||||
if optionID == 0x80 {
|
||||
p.liteSignature = optionsStream.ReadBytesNext(int64(optionSize))
|
||||
if optionsStream.Remaining() < uint64(optionSize) {
|
||||
err = errors.New("Failed to read lite signature. Not have enough data")
|
||||
} else {
|
||||
p.liteSignature = optionsStream.ReadBytesNext(int64(optionSize))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -272,20 +289,20 @@ func (p *PRUDPPacketLite) encodeOptions() []byte {
|
|||
optionsStream := NewByteStreamOut(p.server.LibraryVersions, p.server.ByteStreamSettings)
|
||||
|
||||
if p.packetType == constants.SynPacket || p.packetType == constants.ConnectPacket {
|
||||
optionsStream.WritePrimitiveUInt8(0)
|
||||
optionsStream.WritePrimitiveUInt8(4)
|
||||
optionsStream.WritePrimitiveUInt32LE(p.minorVersion | (p.supportedFunctions << 8))
|
||||
optionsStream.WriteUInt8(0)
|
||||
optionsStream.WriteUInt8(4)
|
||||
optionsStream.WriteUInt32LE(p.minorVersion | (p.supportedFunctions << 8))
|
||||
|
||||
if p.packetType == constants.SynPacket && p.HasFlag(constants.PacketFlagAck) {
|
||||
optionsStream.WritePrimitiveUInt8(1)
|
||||
optionsStream.WritePrimitiveUInt8(16)
|
||||
optionsStream.WriteUInt8(1)
|
||||
optionsStream.WriteUInt8(16)
|
||||
optionsStream.Grow(16)
|
||||
optionsStream.WriteBytesNext(p.connectionSignature)
|
||||
}
|
||||
|
||||
if p.packetType == constants.ConnectPacket && !p.HasFlag(constants.PacketFlagAck) {
|
||||
optionsStream.WritePrimitiveUInt8(1)
|
||||
optionsStream.WritePrimitiveUInt8(16)
|
||||
optionsStream.WriteUInt8(1)
|
||||
optionsStream.WriteUInt8(16)
|
||||
optionsStream.Grow(16)
|
||||
optionsStream.WriteBytesNext(p.liteSignature)
|
||||
}
|
||||
|
|
|
@ -68,14 +68,14 @@ func (p *PRUDPPacketV0) decode() error {
|
|||
server := p.server
|
||||
start := p.readStream.ByteOffset()
|
||||
|
||||
source, err := p.readStream.ReadPrimitiveUInt8()
|
||||
source, err := p.readStream.ReadUInt8()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read PRUDPv0 source. %s", err.Error())
|
||||
}
|
||||
|
||||
p.sourceVirtualPort = VirtualPort(source)
|
||||
|
||||
destination, err := p.readStream.ReadPrimitiveUInt8()
|
||||
destination, err := p.readStream.ReadUInt8()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read PRUDPv0 destination. %s", err.Error())
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ func (p *PRUDPPacketV0) decode() error {
|
|||
p.destinationVirtualPort = VirtualPort(destination)
|
||||
|
||||
if server.PRUDPV0Settings.IsQuazalMode {
|
||||
typeAndFlags, err := p.readStream.ReadPrimitiveUInt8()
|
||||
typeAndFlags, err := p.readStream.ReadUInt8()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read PRUDPv0 type and flags. %s", err.Error())
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ func (p *PRUDPPacketV0) decode() error {
|
|||
p.flags = uint16(typeAndFlags >> 3)
|
||||
p.packetType = uint16(typeAndFlags & 7)
|
||||
} else {
|
||||
typeAndFlags, err := p.readStream.ReadPrimitiveUInt16LE()
|
||||
typeAndFlags, err := p.readStream.ReadUInt16LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read PRUDPv0 type and flags. %s", err.Error())
|
||||
}
|
||||
|
@ -100,14 +100,14 @@ func (p *PRUDPPacketV0) decode() error {
|
|||
p.packetType = typeAndFlags & 0xF
|
||||
}
|
||||
|
||||
p.sessionID, err = p.readStream.ReadPrimitiveUInt8()
|
||||
p.sessionID, err = p.readStream.ReadUInt8()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read PRUDPv0 session ID. %s", err.Error())
|
||||
}
|
||||
|
||||
p.signature = p.readStream.ReadBytesNext(4)
|
||||
|
||||
p.sequenceID, err = p.readStream.ReadPrimitiveUInt16LE()
|
||||
p.sequenceID, err = p.readStream.ReadUInt16LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read PRUDPv0 sequence ID. %s", err.Error())
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ func (p *PRUDPPacketV0) decode() error {
|
|||
return errors.New("Failed to read PRUDPv0 fragment ID. Not have enough data")
|
||||
}
|
||||
|
||||
p.fragmentID, err = p.readStream.ReadPrimitiveUInt8()
|
||||
p.fragmentID, err = p.readStream.ReadUInt8()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read PRUDPv0 fragment ID. %s", err.Error())
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ func (p *PRUDPPacketV0) decode() error {
|
|||
return errors.New("Failed to read PRUDPv0 payload size. Not have enough data")
|
||||
}
|
||||
|
||||
payloadSize, err = p.readStream.ReadPrimitiveUInt16LE()
|
||||
payloadSize, err = p.readStream.ReadUInt16LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read PRUDPv0 payload size. %s", err.Error())
|
||||
}
|
||||
|
@ -169,9 +169,9 @@ func (p *PRUDPPacketV0) decode() error {
|
|||
var checksumU8 uint8
|
||||
|
||||
if server.PRUDPV0Settings.UseEnhancedChecksum {
|
||||
checksum, err = p.readStream.ReadPrimitiveUInt32LE()
|
||||
checksum, err = p.readStream.ReadUInt32LE()
|
||||
} else {
|
||||
checksumU8, err = p.readStream.ReadPrimitiveUInt8()
|
||||
checksumU8, err = p.readStream.ReadUInt8()
|
||||
checksum = uint32(checksumU8)
|
||||
}
|
||||
|
||||
|
@ -193,19 +193,19 @@ func (p *PRUDPPacketV0) Bytes() []byte {
|
|||
server := p.server
|
||||
stream := NewByteStreamOut(server.LibraryVersions, server.ByteStreamSettings)
|
||||
|
||||
stream.WritePrimitiveUInt8(uint8(p.sourceVirtualPort))
|
||||
stream.WritePrimitiveUInt8(uint8(p.destinationVirtualPort))
|
||||
stream.WriteUInt8(uint8(p.sourceVirtualPort))
|
||||
stream.WriteUInt8(uint8(p.destinationVirtualPort))
|
||||
|
||||
if server.PRUDPV0Settings.IsQuazalMode {
|
||||
stream.WritePrimitiveUInt8(uint8(p.packetType | (p.flags << 3)))
|
||||
stream.WriteUInt8(uint8(p.packetType | (p.flags << 3)))
|
||||
} else {
|
||||
stream.WritePrimitiveUInt16LE(p.packetType | (p.flags << 4))
|
||||
stream.WriteUInt16LE(p.packetType | (p.flags << 4))
|
||||
}
|
||||
|
||||
stream.WritePrimitiveUInt8(p.sessionID)
|
||||
stream.WriteUInt8(p.sessionID)
|
||||
stream.Grow(int64(len(p.signature)))
|
||||
stream.WriteBytesNext(p.signature)
|
||||
stream.WritePrimitiveUInt16LE(p.sequenceID)
|
||||
stream.WriteUInt16LE(p.sequenceID)
|
||||
|
||||
if p.packetType == constants.SynPacket || p.packetType == constants.ConnectPacket {
|
||||
stream.Grow(int64(len(p.connectionSignature)))
|
||||
|
@ -213,11 +213,11 @@ func (p *PRUDPPacketV0) Bytes() []byte {
|
|||
}
|
||||
|
||||
if p.packetType == constants.DataPacket {
|
||||
stream.WritePrimitiveUInt8(p.fragmentID)
|
||||
stream.WriteUInt8(p.fragmentID)
|
||||
}
|
||||
|
||||
if p.HasFlag(constants.PacketFlagHasSize) {
|
||||
stream.WritePrimitiveUInt16LE(uint16(len(p.payload)))
|
||||
stream.WriteUInt16LE(uint16(len(p.payload)))
|
||||
}
|
||||
|
||||
if len(p.payload) > 0 {
|
||||
|
@ -228,9 +228,9 @@ func (p *PRUDPPacketV0) Bytes() []byte {
|
|||
checksum := p.server.PRUDPV0Settings.ChecksumCalculator(p, stream.Bytes())
|
||||
|
||||
if server.PRUDPV0Settings.UseEnhancedChecksum {
|
||||
stream.WritePrimitiveUInt32LE(checksum)
|
||||
stream.WriteUInt32LE(checksum)
|
||||
} else {
|
||||
stream.WritePrimitiveUInt8(uint8(checksum))
|
||||
stream.WriteUInt8(uint8(checksum))
|
||||
}
|
||||
|
||||
return stream.Bytes()
|
||||
|
|
|
@ -89,6 +89,10 @@ func (p *PRUDPPacketV1) decode() error {
|
|||
return fmt.Errorf("Failed to decode PRUDPv1 header. %s", err.Error())
|
||||
}
|
||||
|
||||
if p.readStream.Remaining() < 16 {
|
||||
return errors.New("Failed to read PRUDPv1 signature. Not have enough data")
|
||||
}
|
||||
|
||||
p.signature = p.readStream.ReadBytesNext(16)
|
||||
|
||||
err = p.decodeOptions()
|
||||
|
@ -96,6 +100,10 @@ func (p *PRUDPPacketV1) decode() error {
|
|||
return fmt.Errorf("Failed to decode PRUDPv1 options. %s", err.Error())
|
||||
}
|
||||
|
||||
if p.readStream.Remaining() < uint64(p.payloadLength) {
|
||||
return errors.New("Failed to read PRUDPv1 payload. Not have enough data")
|
||||
}
|
||||
|
||||
p.payload = p.readStream.ReadBytesNext(int64(p.payloadLength))
|
||||
|
||||
return nil
|
||||
|
@ -134,7 +142,7 @@ func (p *PRUDPPacketV1) decodeHeader() error {
|
|||
return errors.New("Failed to read PRUDPv1 magic. Not have enough data")
|
||||
}
|
||||
|
||||
version, err := p.readStream.ReadPrimitiveUInt8()
|
||||
version, err := p.readStream.ReadUInt8()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to decode PRUDPv1 version. %s", err.Error())
|
||||
}
|
||||
|
@ -143,24 +151,24 @@ func (p *PRUDPPacketV1) decodeHeader() error {
|
|||
return fmt.Errorf("Invalid PRUDPv1 version. Expected 1, got %d", version)
|
||||
}
|
||||
|
||||
p.optionsLength, err = p.readStream.ReadPrimitiveUInt8()
|
||||
p.optionsLength, err = p.readStream.ReadUInt8()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to decode PRUDPv1 options length. %s", err.Error())
|
||||
}
|
||||
|
||||
p.payloadLength, err = p.readStream.ReadPrimitiveUInt16LE()
|
||||
p.payloadLength, err = p.readStream.ReadUInt16LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to decode PRUDPv1 payload length. %s", err.Error())
|
||||
}
|
||||
|
||||
source, err := p.readStream.ReadPrimitiveUInt8()
|
||||
source, err := p.readStream.ReadUInt8()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read PRUDPv1 source. %s", err.Error())
|
||||
}
|
||||
|
||||
p.sourceVirtualPort = VirtualPort(source)
|
||||
|
||||
destination, err := p.readStream.ReadPrimitiveUInt8()
|
||||
destination, err := p.readStream.ReadUInt8()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read PRUDPv1 destination. %s", err.Error())
|
||||
}
|
||||
|
@ -168,7 +176,7 @@ func (p *PRUDPPacketV1) decodeHeader() error {
|
|||
p.destinationVirtualPort = VirtualPort(destination)
|
||||
|
||||
// TODO - Does QRV also encode it this way in PRUDPv1?
|
||||
typeAndFlags, err := p.readStream.ReadPrimitiveUInt16LE()
|
||||
typeAndFlags, err := p.readStream.ReadUInt16LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read PRUDPv1 type and flags. %s", err.Error())
|
||||
}
|
||||
|
@ -176,17 +184,17 @@ func (p *PRUDPPacketV1) decodeHeader() error {
|
|||
p.flags = typeAndFlags >> 4
|
||||
p.packetType = typeAndFlags & 0xF
|
||||
|
||||
p.sessionID, err = p.readStream.ReadPrimitiveUInt8()
|
||||
p.sessionID, err = p.readStream.ReadUInt8()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read PRUDPv1 session ID. %s", err.Error())
|
||||
}
|
||||
|
||||
p.substreamID, err = p.readStream.ReadPrimitiveUInt8()
|
||||
p.substreamID, err = p.readStream.ReadUInt8()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read PRUDPv1 substream ID. %s", err.Error())
|
||||
}
|
||||
|
||||
p.sequenceID, err = p.readStream.ReadPrimitiveUInt16LE()
|
||||
p.sequenceID, err = p.readStream.ReadUInt16LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read PRUDPv1 sequence ID. %s", err.Error())
|
||||
}
|
||||
|
@ -197,60 +205,68 @@ func (p *PRUDPPacketV1) decodeHeader() error {
|
|||
func (p *PRUDPPacketV1) encodeHeader() []byte {
|
||||
stream := NewByteStreamOut(p.server.LibraryVersions, p.server.ByteStreamSettings)
|
||||
|
||||
stream.WritePrimitiveUInt8(1) // * Version
|
||||
stream.WritePrimitiveUInt8(p.optionsLength)
|
||||
stream.WritePrimitiveUInt16LE(uint16(len(p.payload)))
|
||||
stream.WritePrimitiveUInt8(uint8(p.sourceVirtualPort))
|
||||
stream.WritePrimitiveUInt8(uint8(p.destinationVirtualPort))
|
||||
stream.WritePrimitiveUInt16LE(p.packetType | (p.flags << 4)) // TODO - Does QRV also encode it this way in PRUDPv1?
|
||||
stream.WritePrimitiveUInt8(p.sessionID)
|
||||
stream.WritePrimitiveUInt8(p.substreamID)
|
||||
stream.WritePrimitiveUInt16LE(p.sequenceID)
|
||||
stream.WriteUInt8(1) // * Version
|
||||
stream.WriteUInt8(p.optionsLength)
|
||||
stream.WriteUInt16LE(uint16(len(p.payload)))
|
||||
stream.WriteUInt8(uint8(p.sourceVirtualPort))
|
||||
stream.WriteUInt8(uint8(p.destinationVirtualPort))
|
||||
stream.WriteUInt16LE(p.packetType | (p.flags << 4)) // TODO - Does QRV also encode it this way in PRUDPv1?
|
||||
stream.WriteUInt8(p.sessionID)
|
||||
stream.WriteUInt8(p.substreamID)
|
||||
stream.WriteUInt16LE(p.sequenceID)
|
||||
|
||||
return stream.Bytes()
|
||||
}
|
||||
|
||||
func (p *PRUDPPacketV1) decodeOptions() error {
|
||||
if p.readStream.Remaining() < uint64(p.optionsLength) {
|
||||
return errors.New("Not have enough data")
|
||||
}
|
||||
|
||||
data := p.readStream.ReadBytesNext(int64(p.optionsLength))
|
||||
optionsStream := NewByteStreamIn(data, p.server.LibraryVersions, p.server.ByteStreamSettings)
|
||||
|
||||
for optionsStream.Remaining() > 0 {
|
||||
optionID, err := optionsStream.ReadPrimitiveUInt8()
|
||||
optionID, err := optionsStream.ReadUInt8()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = optionsStream.ReadPrimitiveUInt8() // * Options size. We already know the size based on the ID, though
|
||||
_, err = optionsStream.ReadUInt8() // * Options size. We already know the size based on the ID, though
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.packetType == constants.SynPacket || p.packetType == constants.ConnectPacket {
|
||||
if optionID == 0 {
|
||||
p.supportedFunctions, err = optionsStream.ReadPrimitiveUInt32LE()
|
||||
p.supportedFunctions, err = optionsStream.ReadUInt32LE()
|
||||
|
||||
p.minorVersion = p.supportedFunctions & 0xFF
|
||||
p.supportedFunctions = p.supportedFunctions >> 8
|
||||
}
|
||||
|
||||
if optionID == 1 {
|
||||
p.connectionSignature = optionsStream.ReadBytesNext(16)
|
||||
if optionsStream.Remaining() < 16 {
|
||||
err = errors.New("Not have enough data")
|
||||
} else {
|
||||
p.connectionSignature = optionsStream.ReadBytesNext(16)
|
||||
}
|
||||
}
|
||||
|
||||
if optionID == 4 {
|
||||
p.maximumSubstreamID, err = optionsStream.ReadPrimitiveUInt8()
|
||||
p.maximumSubstreamID, err = optionsStream.ReadUInt8()
|
||||
}
|
||||
}
|
||||
|
||||
if p.packetType == constants.ConnectPacket {
|
||||
if optionID == 3 {
|
||||
p.initialUnreliableSequenceID, err = optionsStream.ReadPrimitiveUInt16LE()
|
||||
p.initialUnreliableSequenceID, err = optionsStream.ReadUInt16LE()
|
||||
}
|
||||
}
|
||||
|
||||
if p.packetType == constants.DataPacket {
|
||||
if optionID == 2 {
|
||||
p.fragmentID, err = optionsStream.ReadPrimitiveUInt8()
|
||||
p.fragmentID, err = optionsStream.ReadUInt8()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,12 +285,12 @@ func (p *PRUDPPacketV1) encodeOptions() []byte {
|
|||
optionsStream := NewByteStreamOut(p.server.LibraryVersions, p.server.ByteStreamSettings)
|
||||
|
||||
if p.packetType == constants.SynPacket || p.packetType == constants.ConnectPacket {
|
||||
optionsStream.WritePrimitiveUInt8(0)
|
||||
optionsStream.WritePrimitiveUInt8(4)
|
||||
optionsStream.WritePrimitiveUInt32LE(p.minorVersion | (p.supportedFunctions << 8))
|
||||
optionsStream.WriteUInt8(0)
|
||||
optionsStream.WriteUInt8(4)
|
||||
optionsStream.WriteUInt32LE(p.minorVersion | (p.supportedFunctions << 8))
|
||||
|
||||
optionsStream.WritePrimitiveUInt8(1)
|
||||
optionsStream.WritePrimitiveUInt8(16)
|
||||
optionsStream.WriteUInt8(1)
|
||||
optionsStream.WriteUInt8(16)
|
||||
optionsStream.Grow(16)
|
||||
optionsStream.WriteBytesNext(p.connectionSignature)
|
||||
|
||||
|
@ -287,20 +303,20 @@ func (p *PRUDPPacketV1) encodeOptions() []byte {
|
|||
// * parsed, though, order REALLY doesn't matter.
|
||||
// * NintendoClients expects option 3 before 4, though
|
||||
if p.packetType == constants.ConnectPacket {
|
||||
optionsStream.WritePrimitiveUInt8(3)
|
||||
optionsStream.WritePrimitiveUInt8(2)
|
||||
optionsStream.WritePrimitiveUInt16LE(p.initialUnreliableSequenceID)
|
||||
optionsStream.WriteUInt8(3)
|
||||
optionsStream.WriteUInt8(2)
|
||||
optionsStream.WriteUInt16LE(p.initialUnreliableSequenceID)
|
||||
}
|
||||
|
||||
optionsStream.WritePrimitiveUInt8(4)
|
||||
optionsStream.WritePrimitiveUInt8(1)
|
||||
optionsStream.WritePrimitiveUInt8(p.maximumSubstreamID)
|
||||
optionsStream.WriteUInt8(4)
|
||||
optionsStream.WriteUInt8(1)
|
||||
optionsStream.WriteUInt8(p.maximumSubstreamID)
|
||||
}
|
||||
|
||||
if p.packetType == constants.DataPacket {
|
||||
optionsStream.WritePrimitiveUInt8(2)
|
||||
optionsStream.WritePrimitiveUInt8(1)
|
||||
optionsStream.WritePrimitiveUInt8(p.fragmentID)
|
||||
optionsStream.WriteUInt8(2)
|
||||
optionsStream.WriteUInt8(1)
|
||||
optionsStream.WriteUInt8(p.fragmentID)
|
||||
}
|
||||
|
||||
return optionsStream.Bytes()
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2/constants"
|
||||
"github.com/lxzan/gws"
|
||||
|
@ -16,7 +17,6 @@ type PRUDPServer struct {
|
|||
udpSocket *net.UDPConn
|
||||
websocketServer *WebSocketServer
|
||||
Endpoints *MutexMap[uint8, *PRUDPEndPoint]
|
||||
Connections *MutexMap[string, *SocketConnection]
|
||||
SupportedFunctions uint32
|
||||
AccessKey string
|
||||
KerberosTicketVersion int
|
||||
|
@ -73,15 +73,16 @@ func (ps *PRUDPServer) ListenUDP(port int) {
|
|||
|
||||
func (ps *PRUDPServer) listenDatagram(quit chan struct{}) {
|
||||
var err error
|
||||
buffer := make([]byte, 64000)
|
||||
|
||||
for err == nil {
|
||||
buffer := make([]byte, 64000)
|
||||
var read int
|
||||
var addr *net.UDPAddr
|
||||
|
||||
read, addr, err = ps.udpSocket.ReadFromUDP(buffer)
|
||||
if err == nil {
|
||||
packetData := buffer[:read]
|
||||
packetData := make([]byte, read)
|
||||
copy(packetData, buffer[:read])
|
||||
|
||||
err = ps.handleSocketMessage(packetData, addr, nil)
|
||||
}
|
||||
|
@ -126,6 +127,11 @@ func (ps *PRUDPServer) initPRUDPv1ConnectionSignatureKey() {
|
|||
}
|
||||
|
||||
func (ps *PRUDPServer) handleSocketMessage(packetData []byte, address net.Addr, webSocketConnection *gws.Conn) error {
|
||||
// * Check that the message is long enough for initial parsing
|
||||
if len(packetData) < 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
readStream := NewByteStreamIn(packetData, ps.LibraryVersions, ps.ByteStreamSettings)
|
||||
|
||||
var packets []PRUDPPacketInterface
|
||||
|
@ -192,13 +198,7 @@ func (ps *PRUDPServer) processPacket(packet PRUDPPacketInterface, address net.Ad
|
|||
return
|
||||
}
|
||||
|
||||
discriminator := address.String()
|
||||
socket, ok := ps.Connections.Get(discriminator)
|
||||
if !ok {
|
||||
socket = NewSocketConnection(ps, address, webSocketConnection)
|
||||
ps.Connections.Set(discriminator, socket)
|
||||
}
|
||||
|
||||
socket := NewSocketConnection(ps, address, webSocketConnection)
|
||||
endpoint.processPacket(packet, socket)
|
||||
}
|
||||
|
||||
|
@ -222,6 +222,14 @@ func (ps *PRUDPServer) Send(packet PacketInterface) {
|
|||
}
|
||||
|
||||
ps.sendPacket(packet)
|
||||
|
||||
// * This delay is here to prevent the server from overloading the client with too many packets.
|
||||
// * The 16ms (1/60th of a second) value is chosen based on testing with the friends server and is a good balance between
|
||||
// * Not being too slow and also not dropping any packets because we've overloaded the client. This may be because it
|
||||
// * roughly matches the framerate that most games target (60fps)
|
||||
if i < fragments {
|
||||
time.Sleep(16 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -242,6 +250,7 @@ func (ps *PRUDPServer) sendPacket(packet PRUDPPacketInterface) {
|
|||
packetCopy.SetSequenceID(connection.outgoingUnreliableSequenceIDCounter.Next())
|
||||
} else if packetCopy.Type() == constants.PingPacket {
|
||||
packetCopy.SetSequenceID(connection.outgoingPingSequenceIDCounter.Next())
|
||||
connection.lastSentPingTime = time.Now()
|
||||
} else {
|
||||
packetCopy.SetSequenceID(0)
|
||||
}
|
||||
|
@ -279,9 +288,12 @@ func (ps *PRUDPServer) sendPacket(packet PRUDPPacketInterface) {
|
|||
packetCopy.setSignature(packetCopy.calculateSignature(connection.SessionKey, connection.ServerConnectionSignature))
|
||||
}
|
||||
|
||||
packetCopy.incrementSendCount()
|
||||
packetCopy.setSentAt(time.Now())
|
||||
|
||||
if packetCopy.HasFlag(constants.PacketFlagReliable) && packetCopy.HasFlag(constants.PacketFlagNeedsAck) {
|
||||
slidingWindow := connection.SlidingWindow(packetCopy.SubstreamID())
|
||||
slidingWindow.ResendScheduler.AddPacket(packetCopy)
|
||||
slidingWindow.TimeoutManager.SchedulePacketTimeout(packetCopy)
|
||||
}
|
||||
|
||||
ps.sendRaw(packetCopy.Sender().(*PRUDPConnection).Socket, packetCopy.Bytes())
|
||||
|
@ -324,7 +336,6 @@ func (ps *PRUDPServer) SetFragmentSize(fragmentSize int) {
|
|||
func NewPRUDPServer() *PRUDPServer {
|
||||
return &PRUDPServer{
|
||||
Endpoints: NewMutexMap[uint8, *PRUDPEndPoint](),
|
||||
Connections: NewMutexMap[string, *SocketConnection](),
|
||||
SessionKeyLength: 32,
|
||||
FragmentSize: 1300,
|
||||
LibraryVersions: NewLibraryVersions(),
|
||||
|
|
|
@ -1,148 +0,0 @@
|
|||
package nex
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// TODO - REMOVE THIS ENTIRELY AND REPLACE IT WITH AN IMPLEMENTATION OF rdv::Timeout AND rdv::TimeoutManager AND USE MORE STREAM SETTINGS!
|
||||
|
||||
// PendingPacket represends a packet scheduled to be resent
|
||||
type PendingPacket struct {
|
||||
packet PRUDPPacketInterface
|
||||
lastSendTime time.Time
|
||||
resendCount uint32
|
||||
isAcknowledged bool
|
||||
interval time.Duration
|
||||
ticker *time.Ticker
|
||||
rs *ResendScheduler
|
||||
}
|
||||
|
||||
func (pi *PendingPacket) startResendTimer() {
|
||||
pi.lastSendTime = time.Now()
|
||||
pi.ticker = time.NewTicker(pi.interval)
|
||||
|
||||
for range pi.ticker.C {
|
||||
finished := false
|
||||
|
||||
if pi.isAcknowledged {
|
||||
pi.ticker.Stop()
|
||||
pi.rs.packets.Delete(pi.packet.SequenceID())
|
||||
finished = true
|
||||
} else {
|
||||
finished = pi.rs.resendPacket(pi)
|
||||
}
|
||||
|
||||
if finished {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ResendScheduler manages the resending of reliable PRUDP packets
|
||||
type ResendScheduler struct {
|
||||
packets *MutexMap[uint16, *PendingPacket]
|
||||
}
|
||||
|
||||
// Stop kills the resend scheduler and stops all pending packets
|
||||
func (rs *ResendScheduler) Stop() {
|
||||
stillPending := make([]uint16, rs.packets.Size())
|
||||
|
||||
rs.packets.Each(func(sequenceID uint16, packet *PendingPacket) bool {
|
||||
if !packet.isAcknowledged {
|
||||
stillPending = append(stillPending, sequenceID)
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
for _, sequenceID := range stillPending {
|
||||
if pendingPacket, ok := rs.packets.Get(sequenceID); ok {
|
||||
pendingPacket.isAcknowledged = true // * Prevent an edge case where the ticker is already being processed
|
||||
|
||||
if pendingPacket.ticker != nil {
|
||||
// * This should never happen, but popped up in CTGP-7 testing?
|
||||
// * Did the GC clear this before we called it?
|
||||
pendingPacket.ticker.Stop()
|
||||
}
|
||||
|
||||
rs.packets.Delete(sequenceID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddPacket adds a packet to the scheduler and begins it's timer
|
||||
func (rs *ResendScheduler) AddPacket(packet PRUDPPacketInterface) {
|
||||
connection := packet.Sender().(*PRUDPConnection)
|
||||
slidingWindow := connection.SlidingWindow(packet.SubstreamID())
|
||||
|
||||
pendingPacket := &PendingPacket{
|
||||
packet: packet,
|
||||
rs: rs,
|
||||
// TODO: This may not be accurate, needs more research
|
||||
interval: time.Duration(slidingWindow.streamSettings.KeepAliveTimeout) * time.Millisecond,
|
||||
}
|
||||
|
||||
rs.packets.Set(packet.SequenceID(), pendingPacket)
|
||||
|
||||
go pendingPacket.startResendTimer()
|
||||
}
|
||||
|
||||
// AcknowledgePacket marks a pending packet as acknowledged. It will be ignored at the next resend attempt
|
||||
func (rs *ResendScheduler) AcknowledgePacket(sequenceID uint16) {
|
||||
if pendingPacket, ok := rs.packets.Get(sequenceID); ok {
|
||||
pendingPacket.isAcknowledged = true
|
||||
}
|
||||
}
|
||||
|
||||
func (rs *ResendScheduler) resendPacket(pendingPacket *PendingPacket) bool {
|
||||
if pendingPacket.isAcknowledged {
|
||||
// * Prevent a race condition where resendPacket may be called
|
||||
// * at the same time a packet is acknowledged
|
||||
return false
|
||||
}
|
||||
|
||||
packet := pendingPacket.packet
|
||||
connection := packet.Sender().(*PRUDPConnection)
|
||||
slidingWindow := connection.SlidingWindow(packet.SubstreamID())
|
||||
|
||||
if pendingPacket.resendCount >= slidingWindow.streamSettings.MaxPacketRetransmissions {
|
||||
// * The maximum resend count has been reached, consider the connection dead.
|
||||
pendingPacket.ticker.Stop()
|
||||
rs.packets.Delete(packet.SequenceID())
|
||||
connection.cleanup() // * "removed" event is dispatched here
|
||||
|
||||
connection.endpoint.deleteConnectionByID(connection.ID)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// TODO: This may not be accurate, needs more research
|
||||
if time.Since(pendingPacket.lastSendTime) >= time.Duration(slidingWindow.streamSettings.KeepAliveTimeout)*time.Millisecond {
|
||||
// * Resend the packet to the connection
|
||||
server := connection.endpoint.Server
|
||||
data := packet.Bytes()
|
||||
server.sendRaw(connection.Socket, data)
|
||||
|
||||
pendingPacket.resendCount++
|
||||
|
||||
var retransmitTimeoutMultiplier float32
|
||||
if pendingPacket.resendCount < slidingWindow.streamSettings.ExtraRestransmitTimeoutTrigger {
|
||||
retransmitTimeoutMultiplier = slidingWindow.streamSettings.RetransmitTimeoutMultiplier
|
||||
} else {
|
||||
retransmitTimeoutMultiplier = slidingWindow.streamSettings.ExtraRetransmitTimeoutMultiplier
|
||||
}
|
||||
pendingPacket.interval += time.Duration(uint32(float32(slidingWindow.streamSettings.KeepAliveTimeout)*retransmitTimeoutMultiplier)) * time.Millisecond
|
||||
|
||||
pendingPacket.ticker.Reset(pendingPacket.interval)
|
||||
pendingPacket.lastSendTime = time.Now()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// NewResendScheduler creates a new ResendScheduler
|
||||
func NewResendScheduler() *ResendScheduler {
|
||||
return &ResendScheduler{
|
||||
packets: NewMutexMap[uint16, *PendingPacket](),
|
||||
}
|
||||
}
|
105
rmc_message.go
105
rmc_message.go
|
@ -9,18 +9,18 @@ import (
|
|||
|
||||
// RMCMessage represents a message in the RMC (Remote Method Call) protocol
|
||||
type RMCMessage struct {
|
||||
Endpoint EndpointInterface
|
||||
IsRequest bool // * Indicates if the message is a request message (true) or response message (false)
|
||||
IsSuccess bool // * Indicates if the message is a success message (true) for a response message
|
||||
IsHPP bool // * Indicates if the message is an HPP message
|
||||
ProtocolID uint16 // * Protocol ID of the message. Only present in "packed" variations
|
||||
ProtocolName *types.String // * Protocol name of the message. Only present in "verbose" variations
|
||||
CallID uint32 // * Call ID associated with the message
|
||||
MethodID uint32 // * Method ID in the requested protocol. Only present in "packed" variations
|
||||
MethodName *types.String // * Method name in the requested protocol. Only present in "verbose" variations
|
||||
ErrorCode uint32 // * Error code for a response message
|
||||
ClassVersionContainer *types.ClassVersionContainer // * Contains version info for Structures in the request. Only present in "verbose" variations
|
||||
Parameters []byte // * Input for the method
|
||||
Endpoint EndpointInterface
|
||||
IsRequest bool // * Indicates if the message is a request message (true) or response message (false)
|
||||
IsSuccess bool // * Indicates if the message is a success message (true) for a response message
|
||||
IsHPP bool // * Indicates if the message is an HPP message
|
||||
ProtocolID uint16 // * Protocol ID of the message. Only present in "packed" variations
|
||||
ProtocolName types.String // * Protocol name of the message. Only present in "verbose" variations
|
||||
CallID uint32 // * Call ID associated with the message
|
||||
MethodID uint32 // * Method ID in the requested protocol. Only present in "packed" variations
|
||||
MethodName types.String // * Method name in the requested protocol. Only present in "verbose" variations
|
||||
ErrorCode uint32 // * Error code for a response message
|
||||
VersionContainer *types.ClassVersionContainer // * Contains version info for Structures in the request. Only present in "verbose" variations. Pointer to allow for nil checks
|
||||
Parameters []byte // * Input for the method
|
||||
// TODO - Verbose messages suffix response method names with "*". Should we have a "HasResponsePointer" sort of field?
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ func (rmc *RMCMessage) FromBytes(data []byte) error {
|
|||
func (rmc *RMCMessage) decodePacked(data []byte) error {
|
||||
stream := NewByteStreamIn(data, rmc.Endpoint.LibraryVersions(), rmc.Endpoint.ByteStreamSettings())
|
||||
|
||||
length, err := stream.ReadPrimitiveUInt32LE()
|
||||
length, err := stream.ReadUInt32LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read RMC Message size. %s", err.Error())
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ func (rmc *RMCMessage) decodePacked(data []byte) error {
|
|||
return errors.New("RMC Message has unexpected size")
|
||||
}
|
||||
|
||||
protocolID, err := stream.ReadPrimitiveUInt8()
|
||||
protocolID, err := stream.ReadUInt8()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read RMC Message protocol ID. %s", err.Error())
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ func (rmc *RMCMessage) decodePacked(data []byte) error {
|
|||
rmc.ProtocolID = uint16(protocolID & ^byte(0x80))
|
||||
|
||||
if rmc.ProtocolID == 0x7F {
|
||||
rmc.ProtocolID, err = stream.ReadPrimitiveUInt16LE()
|
||||
rmc.ProtocolID, err = stream.ReadUInt16LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read RMC Message extended protocol ID. %s", err.Error())
|
||||
}
|
||||
|
@ -82,12 +82,12 @@ func (rmc *RMCMessage) decodePacked(data []byte) error {
|
|||
|
||||
if protocolID&0x80 != 0 {
|
||||
rmc.IsRequest = true
|
||||
rmc.CallID, err = stream.ReadPrimitiveUInt32LE()
|
||||
rmc.CallID, err = stream.ReadUInt32LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read RMC Message (request) call ID. %s", err.Error())
|
||||
}
|
||||
|
||||
rmc.MethodID, err = stream.ReadPrimitiveUInt32LE()
|
||||
rmc.MethodID, err = stream.ReadUInt32LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read RMC Message (request) method ID. %s", err.Error())
|
||||
}
|
||||
|
@ -95,18 +95,18 @@ func (rmc *RMCMessage) decodePacked(data []byte) error {
|
|||
rmc.Parameters = stream.ReadRemaining()
|
||||
} else {
|
||||
rmc.IsRequest = false
|
||||
rmc.IsSuccess, err = stream.ReadPrimitiveBool()
|
||||
rmc.IsSuccess, err = stream.ReadBool()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read RMC Message (response) error check. %s", err.Error())
|
||||
}
|
||||
|
||||
if rmc.IsSuccess {
|
||||
rmc.CallID, err = stream.ReadPrimitiveUInt32LE()
|
||||
rmc.CallID, err = stream.ReadUInt32LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read RMC Message (response) call ID. %s", err.Error())
|
||||
}
|
||||
|
||||
rmc.MethodID, err = stream.ReadPrimitiveUInt32LE()
|
||||
rmc.MethodID, err = stream.ReadUInt32LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read RMC Message (response) method ID. %s", err.Error())
|
||||
}
|
||||
|
@ -115,12 +115,12 @@ func (rmc *RMCMessage) decodePacked(data []byte) error {
|
|||
rmc.Parameters = stream.ReadRemaining()
|
||||
|
||||
} else {
|
||||
rmc.ErrorCode, err = stream.ReadPrimitiveUInt32LE()
|
||||
rmc.ErrorCode, err = stream.ReadUInt32LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read RMC Message (response) error code. %s", err.Error())
|
||||
}
|
||||
|
||||
rmc.CallID, err = stream.ReadPrimitiveUInt32LE()
|
||||
rmc.CallID, err = stream.ReadUInt32LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read RMC Message (response) call ID. %s", err.Error())
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ func (rmc *RMCMessage) decodePacked(data []byte) error {
|
|||
func (rmc *RMCMessage) decodeVerbose(data []byte) error {
|
||||
stream := NewByteStreamIn(data, rmc.Endpoint.LibraryVersions(), rmc.Endpoint.ByteStreamSettings())
|
||||
|
||||
length, err := stream.ReadPrimitiveUInt32LE()
|
||||
length, err := stream.ReadUInt32LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read RMC Message size. %s", err.Error())
|
||||
}
|
||||
|
@ -148,13 +148,13 @@ func (rmc *RMCMessage) decodeVerbose(data []byte) error {
|
|||
return fmt.Errorf("Failed to read RMC Message protocol name. %s", err.Error())
|
||||
}
|
||||
|
||||
rmc.IsRequest, err = stream.ReadPrimitiveBool()
|
||||
rmc.IsRequest, err = stream.ReadBool()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read RMC Message \"is request\" bool. %s", err.Error())
|
||||
}
|
||||
|
||||
if rmc.IsRequest {
|
||||
rmc.CallID, err = stream.ReadPrimitiveUInt32LE()
|
||||
rmc.CallID, err = stream.ReadUInt32LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read RMC Message (request) call ID. %s", err.Error())
|
||||
}
|
||||
|
@ -164,20 +164,21 @@ func (rmc *RMCMessage) decodeVerbose(data []byte) error {
|
|||
return fmt.Errorf("Failed to read RMC Message (request) method name. %s", err.Error())
|
||||
}
|
||||
|
||||
rmc.ClassVersionContainer = types.NewClassVersionContainer()
|
||||
if err := rmc.ClassVersionContainer.ExtractFrom(stream); err != nil {
|
||||
versionContainer := types.NewClassVersionContainer()
|
||||
if err := versionContainer.ExtractFrom(stream); err != nil {
|
||||
return fmt.Errorf("Failed to read RMC Message ClassVersionContainer. %s", err.Error())
|
||||
}
|
||||
|
||||
rmc.VersionContainer = &versionContainer
|
||||
rmc.Parameters = stream.ReadRemaining()
|
||||
} else {
|
||||
rmc.IsSuccess, err = stream.ReadPrimitiveBool()
|
||||
rmc.IsSuccess, err = stream.ReadBool()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read RMC Message (response) error check. %s", err.Error())
|
||||
}
|
||||
|
||||
if rmc.IsSuccess {
|
||||
rmc.CallID, err = stream.ReadPrimitiveUInt32LE()
|
||||
rmc.CallID, err = stream.ReadUInt32LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read RMC Message (response) call ID. %s", err.Error())
|
||||
}
|
||||
|
@ -190,12 +191,12 @@ func (rmc *RMCMessage) decodeVerbose(data []byte) error {
|
|||
rmc.Parameters = stream.ReadRemaining()
|
||||
|
||||
} else {
|
||||
rmc.ErrorCode, err = stream.ReadPrimitiveUInt32LE()
|
||||
rmc.ErrorCode, err = stream.ReadUInt32LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read RMC Message (response) error code. %s", err.Error())
|
||||
}
|
||||
|
||||
rmc.CallID, err = stream.ReadPrimitiveUInt32LE()
|
||||
rmc.CallID, err = stream.ReadUInt32LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read RMC Message (response) call ID. %s", err.Error())
|
||||
}
|
||||
|
@ -228,35 +229,35 @@ func (rmc *RMCMessage) encodePacked() []byte {
|
|||
// * do it for accuracy.
|
||||
if !rmc.IsHPP || (rmc.IsHPP && rmc.IsRequest) {
|
||||
if rmc.ProtocolID < 0x80 {
|
||||
stream.WritePrimitiveUInt8(uint8(rmc.ProtocolID | protocolIDFlag))
|
||||
stream.WriteUInt8(uint8(rmc.ProtocolID | protocolIDFlag))
|
||||
} else {
|
||||
stream.WritePrimitiveUInt8(uint8(0x7F | protocolIDFlag))
|
||||
stream.WritePrimitiveUInt16LE(rmc.ProtocolID)
|
||||
stream.WriteUInt8(uint8(0x7F | protocolIDFlag))
|
||||
stream.WriteUInt16LE(rmc.ProtocolID)
|
||||
}
|
||||
}
|
||||
|
||||
if rmc.IsRequest {
|
||||
stream.WritePrimitiveUInt32LE(rmc.CallID)
|
||||
stream.WritePrimitiveUInt32LE(rmc.MethodID)
|
||||
stream.WriteUInt32LE(rmc.CallID)
|
||||
stream.WriteUInt32LE(rmc.MethodID)
|
||||
|
||||
if rmc.Parameters != nil && len(rmc.Parameters) > 0 {
|
||||
stream.Grow(int64(len(rmc.Parameters)))
|
||||
stream.WriteBytesNext(rmc.Parameters)
|
||||
}
|
||||
} else {
|
||||
stream.WritePrimitiveBool(rmc.IsSuccess)
|
||||
stream.WriteBool(rmc.IsSuccess)
|
||||
|
||||
if rmc.IsSuccess {
|
||||
stream.WritePrimitiveUInt32LE(rmc.CallID)
|
||||
stream.WritePrimitiveUInt32LE(rmc.MethodID | 0x8000)
|
||||
stream.WriteUInt32LE(rmc.CallID)
|
||||
stream.WriteUInt32LE(rmc.MethodID | 0x8000)
|
||||
|
||||
if rmc.Parameters != nil && len(rmc.Parameters) > 0 {
|
||||
stream.Grow(int64(len(rmc.Parameters)))
|
||||
stream.WriteBytesNext(rmc.Parameters)
|
||||
}
|
||||
} else {
|
||||
stream.WritePrimitiveUInt32LE(uint32(rmc.ErrorCode))
|
||||
stream.WritePrimitiveUInt32LE(rmc.CallID)
|
||||
stream.WriteUInt32LE(uint32(rmc.ErrorCode))
|
||||
stream.WriteUInt32LE(rmc.CallID)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,7 +265,7 @@ func (rmc *RMCMessage) encodePacked() []byte {
|
|||
|
||||
message := NewByteStreamOut(rmc.Endpoint.LibraryVersions(), rmc.Endpoint.ByteStreamSettings())
|
||||
|
||||
message.WritePrimitiveUInt32LE(uint32(len(serialized)))
|
||||
message.WriteUInt32LE(uint32(len(serialized)))
|
||||
message.Grow(int64(len(serialized)))
|
||||
message.WriteBytesNext(serialized)
|
||||
|
||||
|
@ -275,17 +276,17 @@ func (rmc *RMCMessage) encodeVerbose() []byte {
|
|||
stream := NewByteStreamOut(rmc.Endpoint.LibraryVersions(), rmc.Endpoint.ByteStreamSettings())
|
||||
|
||||
rmc.ProtocolName.WriteTo(stream)
|
||||
stream.WritePrimitiveBool(rmc.IsRequest)
|
||||
stream.WriteBool(rmc.IsRequest)
|
||||
|
||||
if rmc.IsRequest {
|
||||
stream.WritePrimitiveUInt32LE(rmc.CallID)
|
||||
stream.WriteUInt32LE(rmc.CallID)
|
||||
rmc.MethodName.WriteTo(stream)
|
||||
|
||||
if rmc.ClassVersionContainer != nil {
|
||||
rmc.ClassVersionContainer.WriteTo(stream)
|
||||
if rmc.VersionContainer != nil {
|
||||
rmc.VersionContainer.WriteTo(stream)
|
||||
} else {
|
||||
// * Fail safe. This is always present even if no structures are used
|
||||
stream.WritePrimitiveUInt32LE(0)
|
||||
stream.WriteUInt32LE(0)
|
||||
}
|
||||
|
||||
if rmc.Parameters != nil && len(rmc.Parameters) > 0 {
|
||||
|
@ -293,10 +294,10 @@ func (rmc *RMCMessage) encodeVerbose() []byte {
|
|||
stream.WriteBytesNext(rmc.Parameters)
|
||||
}
|
||||
} else {
|
||||
stream.WritePrimitiveBool(rmc.IsSuccess)
|
||||
stream.WriteBool(rmc.IsSuccess)
|
||||
|
||||
if rmc.IsSuccess {
|
||||
stream.WritePrimitiveUInt32LE(rmc.CallID)
|
||||
stream.WriteUInt32LE(rmc.CallID)
|
||||
rmc.MethodName.WriteTo(stream)
|
||||
|
||||
if rmc.Parameters != nil && len(rmc.Parameters) > 0 {
|
||||
|
@ -304,8 +305,8 @@ func (rmc *RMCMessage) encodeVerbose() []byte {
|
|||
stream.WriteBytesNext(rmc.Parameters)
|
||||
}
|
||||
} else {
|
||||
stream.WritePrimitiveUInt32LE(uint32(rmc.ErrorCode))
|
||||
stream.WritePrimitiveUInt32LE(rmc.CallID)
|
||||
stream.WriteUInt32LE(uint32(rmc.ErrorCode))
|
||||
stream.WriteUInt32LE(rmc.CallID)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -313,7 +314,7 @@ func (rmc *RMCMessage) encodeVerbose() []byte {
|
|||
|
||||
message := NewByteStreamOut(rmc.Endpoint.LibraryVersions(), rmc.Endpoint.ByteStreamSettings())
|
||||
|
||||
message.WritePrimitiveUInt32LE(uint32(len(serialized)))
|
||||
message.WriteUInt32LE(uint32(len(serialized)))
|
||||
message.Grow(int64(len(serialized)))
|
||||
message.WriteBytesNext(serialized)
|
||||
|
||||
|
|
66
rtt.go
Normal file
66
rtt.go
Normal file
|
@ -0,0 +1,66 @@
|
|||
package nex
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
alpha float64 = 1.0 / 8.0
|
||||
beta float64 = 1.0 / 4.0
|
||||
k float64 = 4.0
|
||||
)
|
||||
|
||||
// RTT is an implementation of rdv::RTT.
|
||||
// Used to calculate the average round trip time of reliable packets
|
||||
type RTT struct {
|
||||
sync.Mutex
|
||||
lastRTT float64
|
||||
average float64
|
||||
variance float64
|
||||
initialized bool
|
||||
}
|
||||
|
||||
// Adjust updates the average RTT with the new value
|
||||
func (rtt *RTT) Adjust(next time.Duration) {
|
||||
// * This calculation comes from the RFC6298 which defines RTT calculation for TCP packets
|
||||
rtt.Lock()
|
||||
if rtt.initialized {
|
||||
rtt.variance = (1.0-beta)*rtt.variance + beta*math.Abs(rtt.variance-float64(next))
|
||||
rtt.average = (1.0-alpha)*rtt.average + alpha*float64(next)
|
||||
} else {
|
||||
rtt.lastRTT = float64(next)
|
||||
rtt.variance = float64(next) / 2
|
||||
rtt.average = float64(next) + k*rtt.variance
|
||||
rtt.initialized = true
|
||||
}
|
||||
rtt.Unlock()
|
||||
}
|
||||
|
||||
// GetRTTSmoothedAvg returns the smoothed average of this RTT, it is used in calls to the custom
|
||||
// RTO calculation function set on `PRUDPEndpoint::SetCalcRetransmissionTimeoutCallback`
|
||||
func (rtt *RTT) GetRTTSmoothedAvg() float64 {
|
||||
return rtt.average / 16
|
||||
}
|
||||
|
||||
// GetRTTSmoothedDev returns the smoothed standard deviation of this RTT, it is used in calls to the custom
|
||||
// RTO calculation function set on `PRUDPEndpoint::SetCalcRetransmissionTimeoutCallback`
|
||||
func (rtt *RTT) GetRTTSmoothedDev() float64 {
|
||||
return rtt.variance / 8
|
||||
}
|
||||
|
||||
// Initialized returns a bool indicating whether this RTT has been initialized
|
||||
func (rtt *RTT) Initialized() bool {
|
||||
return rtt.initialized
|
||||
}
|
||||
|
||||
// GetRTO returns the current average
|
||||
func (rtt *RTT) Average() time.Duration {
|
||||
return time.Duration(rtt.average)
|
||||
}
|
||||
|
||||
// NewRTT returns a new RTT based on the first value
|
||||
func NewRTT() *RTT {
|
||||
return &RTT{}
|
||||
}
|
|
@ -1,34 +1,14 @@
|
|||
package nex
|
||||
|
||||
// SlidingWindow is an implementation of rdv::SlidingWindow.
|
||||
// SlidingWindow reorders pending reliable packets to ensure they are handled in the expected order.
|
||||
// In the original library each virtual connection stream only uses a single SlidingWindow, but starting
|
||||
// Currently this is a stub and does not reflect the interface and usage of rdv:SlidingWindow.
|
||||
// In the original library this is used to manage sequencing of outgoing packets.
|
||||
// each virtual connection stream only uses a single SlidingWindow, but starting
|
||||
// in PRUDPv1 with NEX virtual connections may have multiple reliable substreams and thus multiple SlidingWindows.
|
||||
type SlidingWindow struct {
|
||||
pendingPackets *MutexMap[uint16, PRUDPPacketInterface]
|
||||
incomingSequenceIDCounter *Counter[uint16]
|
||||
outgoingSequenceIDCounter *Counter[uint16]
|
||||
streamSettings *StreamSettings
|
||||
fragmentedPayload []byte
|
||||
ResendScheduler *ResendScheduler
|
||||
}
|
||||
|
||||
// Update adds an incoming packet to the list of known packets and returns a list of packets to be processed in order
|
||||
func (sw *SlidingWindow) Update(packet PRUDPPacketInterface) []PRUDPPacketInterface {
|
||||
packets := make([]PRUDPPacketInterface, 0)
|
||||
|
||||
if packet.SequenceID() >= sw.incomingSequenceIDCounter.Value && !sw.pendingPackets.Has(packet.SequenceID()) {
|
||||
sw.pendingPackets.Set(packet.SequenceID(), packet)
|
||||
|
||||
for sw.pendingPackets.Has(sw.incomingSequenceIDCounter.Value) {
|
||||
storedPacket, _ := sw.pendingPackets.Get(sw.incomingSequenceIDCounter.Value)
|
||||
packets = append(packets, storedPacket)
|
||||
sw.pendingPackets.Delete(sw.incomingSequenceIDCounter.Value)
|
||||
sw.incomingSequenceIDCounter.Next()
|
||||
}
|
||||
}
|
||||
|
||||
return packets
|
||||
sequenceIDCounter *Counter[uint16]
|
||||
streamSettings *StreamSettings
|
||||
TimeoutManager *TimeoutManager
|
||||
}
|
||||
|
||||
// SetCipherKey sets the reliable substreams RC4 cipher keys
|
||||
|
@ -38,7 +18,7 @@ func (sw *SlidingWindow) SetCipherKey(key []byte) {
|
|||
|
||||
// NextOutgoingSequenceID sets the reliable substreams RC4 cipher keys
|
||||
func (sw *SlidingWindow) NextOutgoingSequenceID() uint16 {
|
||||
return sw.outgoingSequenceIDCounter.Next()
|
||||
return sw.sequenceIDCounter.Next()
|
||||
}
|
||||
|
||||
// Decrypt decrypts the provided data with the substreams decipher
|
||||
|
@ -51,26 +31,11 @@ func (sw *SlidingWindow) Encrypt(data []byte) ([]byte, error) {
|
|||
return sw.streamSettings.EncryptionAlgorithm.Encrypt(data)
|
||||
}
|
||||
|
||||
// AddFragment adds the given fragment to the substreams fragmented payload
|
||||
// Returns the current fragmented payload
|
||||
func (sw *SlidingWindow) AddFragment(fragment []byte) []byte {
|
||||
sw.fragmentedPayload = append(sw.fragmentedPayload, fragment...)
|
||||
|
||||
return sw.fragmentedPayload
|
||||
}
|
||||
|
||||
// ResetFragmentedPayload resets the substreams fragmented payload
|
||||
func (sw *SlidingWindow) ResetFragmentedPayload() {
|
||||
sw.fragmentedPayload = make([]byte, 0)
|
||||
}
|
||||
|
||||
// NewSlidingWindow initializes a new SlidingWindow with a starting counter value.
|
||||
func NewSlidingWindow() *SlidingWindow {
|
||||
sw := &SlidingWindow{
|
||||
pendingPackets: NewMutexMap[uint16, PRUDPPacketInterface](),
|
||||
incomingSequenceIDCounter: NewCounter[uint16](0),
|
||||
outgoingSequenceIDCounter: NewCounter[uint16](0),
|
||||
ResendScheduler: NewResendScheduler(),
|
||||
sequenceIDCounter: NewCounter[uint16](0),
|
||||
TimeoutManager: NewTimeoutManager(),
|
||||
}
|
||||
|
||||
return sw
|
||||
|
|
|
@ -9,10 +9,9 @@ import (
|
|||
// SocketConnection represents a single open socket.
|
||||
// A single socket may have many PRUDP connections open on it.
|
||||
type SocketConnection struct {
|
||||
Server *PRUDPServer // * PRUDP server the socket is connected to
|
||||
Address net.Addr // * Sockets address
|
||||
WebSocketConnection *gws.Conn // * Only used in PRUDPLite
|
||||
Connections *MutexMap[uint8, *PRUDPConnection] // * Open PRUDP connections separated by rdv::Stream ID, also called "port number"
|
||||
Server *PRUDPServer // * PRUDP server the socket is connected to
|
||||
Address net.Addr // * Sockets address
|
||||
WebSocketConnection *gws.Conn // * Only used in PRUDPLite
|
||||
}
|
||||
|
||||
// NewSocketConnection creates a new SocketConnection
|
||||
|
@ -21,6 +20,5 @@ func NewSocketConnection(server *PRUDPServer, address net.Addr, webSocketConnect
|
|||
Server: server,
|
||||
Address: address,
|
||||
WebSocketConnection: webSocketConnection,
|
||||
Connections: NewMutexMap[uint8, *PRUDPConnection](),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,17 +12,18 @@ import (
|
|||
// The original library has more settings which are not present here as their use is unknown.
|
||||
// Not all values are used at this time, and only exist to future-proof for a later time.
|
||||
type StreamSettings struct {
|
||||
ExtraRestransmitTimeoutTrigger uint32 // * The number of times a packet can be retransmitted before ExtraRetransmitTimeoutMultiplier is used
|
||||
ExtraRetransmitTimeoutTrigger uint32 // * The number of times a packet can be retransmitted before ExtraRetransmitTimeoutMultiplier is used
|
||||
MaxPacketRetransmissions uint32 // * The number of times a packet can be retransmitted before the timeout time is checked
|
||||
KeepAliveTimeout uint32 // * Presumably the time a packet can be alive for without acknowledgement? Milliseconds?
|
||||
ChecksumBase uint32 // * Unused. The base value for PRUDPv0 checksum calculations
|
||||
FaultDetectionEnabled bool // * Unused. Presumably used to detect PIA faults?
|
||||
InitialRTT uint32 // * Unused. The connections initial RTT
|
||||
InitialRTT uint32 // * The initial connection RTT used for all non-SYN packets
|
||||
SynInitialRTT uint32 // * The initial connection RTT used for all SYN packets
|
||||
EncryptionAlgorithm encryption.Algorithm // * The encryption algorithm used for packet payloads
|
||||
ExtraRetransmitTimeoutMultiplier float32 // * Used as part of the RTO calculations when retransmitting a packet. Only used if ExtraRestransmitTimeoutTrigger has been reached
|
||||
WindowSize uint32 // * Unused. The max number of (reliable?) packets allowed in a SlidingWindow
|
||||
CompressionAlgorithm compression.Algorithm // * The compression algorithm used for packet payloads
|
||||
RTTRetransmit uint32 // * Unused. Unknown use
|
||||
RTTRetransmit uint32 // * This is the number of times that a retried packet will be included in RTT calculations if we receive an ACK packet for it
|
||||
RetransmitTimeoutMultiplier float32 // * Used as part of the RTO calculations when retransmitting a packet. Only used if ExtraRestransmitTimeoutTrigger has not been reached
|
||||
MaxSilenceTime uint32 // * Presumably the time a connection can go without any packets from the other side? Milliseconds?
|
||||
}
|
||||
|
@ -31,7 +32,7 @@ type StreamSettings struct {
|
|||
func (ss *StreamSettings) Copy() *StreamSettings {
|
||||
copied := NewStreamSettings()
|
||||
|
||||
copied.ExtraRestransmitTimeoutTrigger = ss.ExtraRestransmitTimeoutTrigger
|
||||
copied.ExtraRetransmitTimeoutTrigger = ss.ExtraRetransmitTimeoutTrigger
|
||||
copied.MaxPacketRetransmissions = ss.MaxPacketRetransmissions
|
||||
copied.KeepAliveTimeout = ss.KeepAliveTimeout
|
||||
copied.ChecksumBase = ss.ChecksumBase
|
||||
|
@ -50,21 +51,22 @@ func (ss *StreamSettings) Copy() *StreamSettings {
|
|||
|
||||
// NewStreamSettings returns a new instance of StreamSettings with default params
|
||||
func NewStreamSettings() *StreamSettings {
|
||||
// * Default values based on WATCH_DOGS. Not all values are used currently, and only
|
||||
// * Default values based on WATCH_DOGS other than where stated. Not all values are used currently, and only
|
||||
// * exist to mimic what is seen in that game. Many are planned for future use.
|
||||
return &StreamSettings{
|
||||
ExtraRestransmitTimeoutTrigger: 0x32,
|
||||
ExtraRetransmitTimeoutTrigger: 0x32,
|
||||
MaxPacketRetransmissions: 0x14,
|
||||
KeepAliveTimeout: 1000,
|
||||
ChecksumBase: 0,
|
||||
FaultDetectionEnabled: true,
|
||||
InitialRTT: 0xFA,
|
||||
InitialRTT: 0x2EE,
|
||||
SynInitialRTT: 0xFA,
|
||||
EncryptionAlgorithm: encryption.NewRC4Encryption(),
|
||||
ExtraRetransmitTimeoutMultiplier: 1.0,
|
||||
WindowSize: 8,
|
||||
CompressionAlgorithm: compression.NewDummyCompression(),
|
||||
RTTRetransmit: 0x32,
|
||||
RTTRetransmit: 2, // * This value is taken from Xenoblade Chronicles, WATCH_DOGS sets this to 0x32 but it is then ignored. Setting this to 2 matches the TCP spec by not using resent packets in RTT calculations.
|
||||
RetransmitTimeoutMultiplier: 1.25,
|
||||
MaxSilenceTime: 5000,
|
||||
MaxSilenceTime: 10000, // * This value is taken from Xenoblade Chronicles, WATCH_DOGS sets this to 5000.
|
||||
}
|
||||
}
|
||||
|
|
10
test/auth.go
10
test/auth.go
|
@ -2,6 +2,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/PretendoNetwork/nex-go/v2"
|
||||
|
@ -62,7 +63,7 @@ func login(packet nex.PRUDPPacketInterface) {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
sourceAccount, _ := accountDetailsByUsername(strUserName.Value)
|
||||
sourceAccount, _ := accountDetailsByUsername(string(strUserName))
|
||||
targetAccount, _ := accountDetailsByUsername(secureServerAccount.Username)
|
||||
|
||||
retval := types.NewQResultSuccess(0x00010001)
|
||||
|
@ -72,8 +73,7 @@ func login(packet nex.PRUDPPacketInterface) {
|
|||
strReturnMsg := types.NewString("Test Build")
|
||||
|
||||
pConnectionData.StationURL = types.NewStationURL("prudps:/address=192.168.1.98;port=60001;CID=1;PID=2;sid=1;stream=10;type=2")
|
||||
pConnectionData.SpecialProtocols = types.NewList[*types.PrimitiveU8]()
|
||||
pConnectionData.SpecialProtocols.Type = types.NewPrimitiveU8(0)
|
||||
pConnectionData.SpecialProtocols = types.NewList[types.UInt8]()
|
||||
pConnectionData.StationURLSpecialProtocols = types.NewStationURL("")
|
||||
pConnectionData.Time = types.NewDateTime(0).Now()
|
||||
|
||||
|
@ -106,6 +106,8 @@ func login(packet nex.PRUDPPacketInterface) {
|
|||
responsePacket.SetSubstreamID(packet.SubstreamID())
|
||||
responsePacket.SetPayload(response.Bytes())
|
||||
|
||||
fmt.Println(hex.EncodeToString(responsePacket.Payload()))
|
||||
|
||||
authServer.Send(responsePacket)
|
||||
}
|
||||
|
||||
|
@ -159,5 +161,7 @@ func requestTicket(packet nex.PRUDPPacketInterface) {
|
|||
responsePacket.SetSubstreamID(packet.SubstreamID())
|
||||
responsePacket.SetPayload(response.Bytes())
|
||||
|
||||
fmt.Println(hex.EncodeToString(responsePacket.Payload()))
|
||||
|
||||
authServer.Send(responsePacket)
|
||||
}
|
||||
|
|
10
test/hpp.go
10
test/hpp.go
|
@ -13,7 +13,7 @@ var hppServer *nex.HPPServer
|
|||
|
||||
type dataStoreGetNotificationURLParam struct {
|
||||
types.Structure
|
||||
PreviousURL *types.String
|
||||
PreviousURL types.String
|
||||
}
|
||||
|
||||
func (d *dataStoreGetNotificationURLParam) ExtractFrom(readable types.Readable) error {
|
||||
|
@ -33,10 +33,10 @@ func (d *dataStoreGetNotificationURLParam) ExtractFrom(readable types.Readable)
|
|||
|
||||
type dataStoreReqGetNotificationURLInfo struct {
|
||||
types.Structure
|
||||
URL *types.String
|
||||
Key *types.String
|
||||
Query *types.String
|
||||
RootCACert *types.Buffer
|
||||
URL types.String
|
||||
Key types.String
|
||||
Query types.String
|
||||
RootCACert types.Buffer
|
||||
}
|
||||
|
||||
func (d *dataStoreReqGetNotificationURLInfo) WriteTo(writable types.Writable) {
|
||||
|
|
|
@ -13,7 +13,7 @@ var authenticationServerAccount *nex.Account
|
|||
var secureServerAccount *nex.Account
|
||||
var testUserAccount *nex.Account
|
||||
|
||||
func accountDetailsByPID(pid *types.PID) (*nex.Account, *nex.Error) {
|
||||
func accountDetailsByPID(pid types.PID) (*nex.Account, *nex.Error) {
|
||||
if pid.Equals(authenticationServerAccount.PID) {
|
||||
return authenticationServerAccount, nil
|
||||
}
|
||||
|
|
|
@ -16,13 +16,13 @@ var secureEndpoint *nex.PRUDPEndPoint
|
|||
|
||||
type principalPreference struct {
|
||||
types.Structure
|
||||
*types.Data
|
||||
ShowOnlinePresence *types.PrimitiveBool
|
||||
ShowCurrentTitle *types.PrimitiveBool
|
||||
BlockFriendRequests *types.PrimitiveBool
|
||||
types.Data
|
||||
ShowOnlinePresence types.Bool
|
||||
ShowCurrentTitle types.Bool
|
||||
BlockFriendRequests types.Bool
|
||||
}
|
||||
|
||||
func (pp *principalPreference) WriteTo(writable types.Writable) {
|
||||
func (pp principalPreference) WriteTo(writable types.Writable) {
|
||||
pp.ShowOnlinePresence.WriteTo(writable)
|
||||
pp.ShowCurrentTitle.WriteTo(writable)
|
||||
pp.BlockFriendRequests.WriteTo(writable)
|
||||
|
@ -30,13 +30,13 @@ func (pp *principalPreference) WriteTo(writable types.Writable) {
|
|||
|
||||
type comment struct {
|
||||
types.Structure
|
||||
*types.Data
|
||||
Unknown *types.PrimitiveU8
|
||||
Contents *types.String
|
||||
LastChanged *types.DateTime
|
||||
types.Data
|
||||
Unknown types.UInt8
|
||||
Contents types.String
|
||||
LastChanged types.DateTime
|
||||
}
|
||||
|
||||
func (c *comment) WriteTo(writable types.Writable) {
|
||||
func (c comment) WriteTo(writable types.Writable) {
|
||||
c.Unknown.WriteTo(writable)
|
||||
c.Contents.WriteTo(writable)
|
||||
c.LastChanged.WriteTo(writable)
|
||||
|
@ -52,6 +52,7 @@ func startSecureServer() {
|
|||
secureEndpoint.AccountDetailsByPID = accountDetailsByPID
|
||||
secureEndpoint.AccountDetailsByUsername = accountDetailsByUsername
|
||||
secureEndpoint.ServerAccount = secureServerAccount
|
||||
secureEndpoint.IsSecureEndPoint = true
|
||||
|
||||
secureEndpoint.OnData(func(packet nex.PacketInterface) {
|
||||
if packet, ok := packet.(nex.PRUDPPacketInterface); ok {
|
||||
|
@ -96,18 +97,17 @@ func registerEx(packet nex.PRUDPPacketInterface) {
|
|||
|
||||
parametersStream := nex.NewByteStreamIn(parameters, secureEndpoint.LibraryVersions(), secureEndpoint.ByteStreamSettings())
|
||||
|
||||
vecMyURLs := types.NewList[*types.StationURL]()
|
||||
vecMyURLs.Type = types.NewStationURL("")
|
||||
vecMyURLs := types.NewList[types.StationURL]()
|
||||
if err := vecMyURLs.ExtractFrom(parametersStream); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
hCustomData := types.NewAnyDataHolder()
|
||||
hCustomData := types.NewDataHolder()
|
||||
if err := hCustomData.ExtractFrom(parametersStream); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
localStation, _ := vecMyURLs.Get(0)
|
||||
localStation := vecMyURLs[0]
|
||||
|
||||
address := packet.Sender().Address().(*net.UDPAddr).IP.String()
|
||||
|
||||
|
@ -115,12 +115,12 @@ func registerEx(packet nex.PRUDPPacketInterface) {
|
|||
localStation.SetPortNumber(uint16(packet.Sender().Address().(*net.UDPAddr).Port))
|
||||
|
||||
retval := types.NewQResultSuccess(0x00010001)
|
||||
localStationURL := types.NewString(localStation.EncodeToString())
|
||||
localStationURL := types.NewString(localStation.URL())
|
||||
|
||||
responseStream := nex.NewByteStreamOut(secureEndpoint.LibraryVersions(), secureEndpoint.ByteStreamSettings())
|
||||
|
||||
retval.WriteTo(responseStream)
|
||||
responseStream.WritePrimitiveUInt32LE(connection.ID)
|
||||
responseStream.WriteUInt32LE(connection.ID)
|
||||
localStationURL.WriteTo(responseStream)
|
||||
|
||||
response.IsSuccess = true
|
||||
|
@ -153,23 +153,23 @@ func updateAndGetAllInformation(packet nex.PRUDPPacketInterface) {
|
|||
|
||||
responseStream := nex.NewByteStreamOut(secureEndpoint.LibraryVersions(), secureEndpoint.ByteStreamSettings())
|
||||
|
||||
(&principalPreference{
|
||||
ShowOnlinePresence: types.NewPrimitiveBool(true),
|
||||
ShowCurrentTitle: types.NewPrimitiveBool(true),
|
||||
BlockFriendRequests: types.NewPrimitiveBool(false),
|
||||
(principalPreference{
|
||||
ShowOnlinePresence: types.NewBool(true),
|
||||
ShowCurrentTitle: types.NewBool(true),
|
||||
BlockFriendRequests: types.NewBool(false),
|
||||
}).WriteTo(responseStream)
|
||||
(&comment{
|
||||
Unknown: types.NewPrimitiveU8(0),
|
||||
(comment{
|
||||
Unknown: types.NewUInt8(0),
|
||||
Contents: types.NewString("Rewrite Test"),
|
||||
LastChanged: types.NewDateTime(0),
|
||||
}).WriteTo(responseStream)
|
||||
responseStream.WritePrimitiveUInt32LE(0) // * Stubbed empty list. responseStream.WriteListStructure(friendList)
|
||||
responseStream.WritePrimitiveUInt32LE(0) // * Stubbed empty list. responseStream.WriteListStructure(friendRequestsOut)
|
||||
responseStream.WritePrimitiveUInt32LE(0) // * Stubbed empty list. responseStream.WriteListStructure(friendRequestsIn)
|
||||
responseStream.WritePrimitiveUInt32LE(0) // * Stubbed empty list. responseStream.WriteListStructure(blockList)
|
||||
responseStream.WritePrimitiveBool(false) // * Unknown
|
||||
responseStream.WritePrimitiveUInt32LE(0) // * Stubbed empty list. responseStream.WriteListStructure(notifications)
|
||||
responseStream.WritePrimitiveBool(false) // * Unknown
|
||||
responseStream.WriteUInt32LE(0) // * Stubbed empty list. responseStream.WriteListStructure(friendList)
|
||||
responseStream.WriteUInt32LE(0) // * Stubbed empty list. responseStream.WriteListStructure(friendRequestsOut)
|
||||
responseStream.WriteUInt32LE(0) // * Stubbed empty list. responseStream.WriteListStructure(friendRequestsIn)
|
||||
responseStream.WriteUInt32LE(0) // * Stubbed empty list. responseStream.WriteListStructure(blockList)
|
||||
responseStream.WriteBool(false) // * Unknown
|
||||
responseStream.WriteUInt32LE(0) // * Stubbed empty list. responseStream.WriteListStructure(notifications)
|
||||
responseStream.WriteBool(false) // * Unknown
|
||||
|
||||
response.IsSuccess = true
|
||||
response.IsRequest = false
|
||||
|
@ -201,7 +201,7 @@ func checkSettingStatus(packet nex.PRUDPPacketInterface) {
|
|||
|
||||
responseStream := nex.NewByteStreamOut(secureEndpoint.LibraryVersions(), secureEndpoint.ByteStreamSettings())
|
||||
|
||||
responseStream.WritePrimitiveUInt8(0) // * Unknown
|
||||
responseStream.WriteUInt8(0) // * Unknown
|
||||
|
||||
response.IsSuccess = true
|
||||
response.IsRequest = false
|
||||
|
|
29
timeout.go
Normal file
29
timeout.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package nex
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Timeout is an implementation of rdv::Timeout.
|
||||
// Used to hold state related to resend timeouts on a packet
|
||||
type Timeout struct {
|
||||
timeout time.Duration
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
// SetRTO sets the timeout field on this instance
|
||||
func (t *Timeout) SetRTO(timeout time.Duration) {
|
||||
t.timeout = timeout
|
||||
}
|
||||
|
||||
// GetRTO gets the timeout field of this instance
|
||||
func (t *Timeout) RTO() time.Duration {
|
||||
return t.timeout
|
||||
}
|
||||
|
||||
// NewTimeout creates a new Timeout
|
||||
func NewTimeout() *Timeout {
|
||||
return &Timeout{}
|
||||
}
|
99
timeout_manager.go
Normal file
99
timeout_manager.go
Normal file
|
@ -0,0 +1,99 @@
|
|||
package nex
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TimeoutManager is an implementation of rdv::TimeoutManager and manages the resending of reliable PRUDP packets
|
||||
type TimeoutManager struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
packets *MutexMap[uint16, PRUDPPacketInterface]
|
||||
streamSettings *StreamSettings
|
||||
}
|
||||
|
||||
// SchedulePacketTimeout adds a packet to the scheduler and begins it's timer
|
||||
func (tm *TimeoutManager) SchedulePacketTimeout(packet PRUDPPacketInterface) {
|
||||
endpoint := packet.Sender().Endpoint().(*PRUDPEndPoint)
|
||||
|
||||
rto := endpoint.ComputeRetransmitTimeout(packet)
|
||||
ctx, cancel := context.WithTimeout(tm.ctx, rto)
|
||||
|
||||
timeout := NewTimeout()
|
||||
timeout.SetRTO(rto)
|
||||
timeout.ctx = ctx
|
||||
timeout.cancel = cancel
|
||||
packet.setTimeout(timeout)
|
||||
|
||||
tm.packets.Set(packet.SequenceID(), packet)
|
||||
go tm.start(packet)
|
||||
}
|
||||
|
||||
// AcknowledgePacket marks a pending packet as acknowledged. It will be ignored at the next resend attempt
|
||||
func (tm *TimeoutManager) AcknowledgePacket(sequenceID uint16) {
|
||||
// * Acknowledge the packet
|
||||
tm.packets.RunAndDelete(sequenceID, func(_ uint16, packet PRUDPPacketInterface) {
|
||||
// * Update the RTT on the connection if the packet hasn't been resent
|
||||
if packet.SendCount() >= tm.streamSettings.RTTRetransmit {
|
||||
rttm := time.Since(packet.SentAt())
|
||||
packet.Sender().(*PRUDPConnection).rtt.Adjust(rttm)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (tm *TimeoutManager) start(packet PRUDPPacketInterface) {
|
||||
<-packet.getTimeout().ctx.Done()
|
||||
|
||||
connection := packet.Sender().(*PRUDPConnection)
|
||||
|
||||
// * If the connection is closed stop trying to resend
|
||||
if connection.ConnectionState != StateConnected {
|
||||
return
|
||||
}
|
||||
|
||||
if tm.packets.Has(packet.SequenceID()) {
|
||||
endpoint := packet.Sender().Endpoint().(*PRUDPEndPoint)
|
||||
|
||||
// * This is `<` instead of `<=` for accuracy with observed behavior, even though we're comparing send count vs _resend_ max
|
||||
if packet.SendCount() < tm.streamSettings.MaxPacketRetransmissions {
|
||||
packet.incrementSendCount()
|
||||
packet.setSentAt(time.Now())
|
||||
rto := endpoint.ComputeRetransmitTimeout(packet)
|
||||
|
||||
ctx, cancel := context.WithTimeout(tm.ctx, rto)
|
||||
timeout := packet.getTimeout()
|
||||
timeout.timeout = rto
|
||||
timeout.ctx = ctx
|
||||
timeout.cancel = cancel
|
||||
|
||||
// * Schedule the packet to be resent
|
||||
go tm.start(packet)
|
||||
|
||||
// * Resend the packet to the connection
|
||||
server := connection.endpoint.Server
|
||||
data := packet.Bytes()
|
||||
server.sendRaw(connection.Socket, data)
|
||||
} else {
|
||||
// * Packet has been retried too many times, consider the connection dead
|
||||
endpoint.cleanupConnection(connection)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stop kills the resend scheduler and stops all pending packets
|
||||
func (tm *TimeoutManager) Stop() {
|
||||
tm.cancel()
|
||||
tm.packets.Clear(func(key uint16, value PRUDPPacketInterface) {})
|
||||
}
|
||||
|
||||
// NewTimeoutManager creates a new TimeoutManager
|
||||
func NewTimeoutManager() *TimeoutManager {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
return &TimeoutManager{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
packets: NewMutexMap[uint16, PRUDPPacketInterface](),
|
||||
streamSettings: NewStreamSettings(),
|
||||
}
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// AnyDataHolderObjects holds a mapping of RVTypes that are accessible in a AnyDataHolder
|
||||
var AnyDataHolderObjects = make(map[string]RVType)
|
||||
|
||||
// RegisterDataHolderType registers a RVType to be accessible in a AnyDataHolder
|
||||
func RegisterDataHolderType(name string, rvType RVType) {
|
||||
AnyDataHolderObjects[name] = rvType
|
||||
}
|
||||
|
||||
// AnyDataHolder is a class which can contain any Structure. The official type name and namespace is unknown.
|
||||
// These Structures usually inherit from at least one other Structure. Typically this base class is the empty
|
||||
// `Data` Structure, but this is not always the case. The contained Structures name & length are sent with the
|
||||
// Structure body, so the receiver can properly decode it.
|
||||
type AnyDataHolder struct {
|
||||
TypeName *String
|
||||
Length1 *PrimitiveU32 // Length of ObjectData + Length2
|
||||
Length2 *PrimitiveU32 // Length of ObjectData
|
||||
ObjectData RVType
|
||||
}
|
||||
|
||||
// WriteTo writes the AnyDataHolder to the given writable
|
||||
func (adh *AnyDataHolder) WriteTo(writable Writable) {
|
||||
contentWritable := writable.CopyNew()
|
||||
|
||||
adh.ObjectData.WriteTo(contentWritable)
|
||||
|
||||
objectData := contentWritable.Bytes()
|
||||
length1 := uint32(len(objectData) + 4)
|
||||
length2 := uint32(len(objectData))
|
||||
|
||||
adh.TypeName.WriteTo(writable)
|
||||
writable.WritePrimitiveUInt32LE(length1)
|
||||
writable.WritePrimitiveUInt32LE(length2)
|
||||
writable.Write(objectData)
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the AnyDataHolder from the given readable
|
||||
func (adh *AnyDataHolder) ExtractFrom(readable Readable) error {
|
||||
var err error
|
||||
|
||||
err = adh.TypeName.ExtractFrom(readable)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read AnyDataHolder type name. %s", err.Error())
|
||||
}
|
||||
|
||||
err = adh.Length1.ExtractFrom(readable)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read AnyDataHolder length 1. %s", err.Error())
|
||||
}
|
||||
|
||||
err = adh.Length2.ExtractFrom(readable)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read AnyDataHolder length 2. %s", err.Error())
|
||||
}
|
||||
|
||||
if _, ok := AnyDataHolderObjects[adh.TypeName.Value]; !ok {
|
||||
return fmt.Errorf("Unknown AnyDataHolder type: %s", adh.TypeName.Value)
|
||||
}
|
||||
|
||||
adh.ObjectData = AnyDataHolderObjects[adh.TypeName.Value].Copy()
|
||||
|
||||
if err := adh.ObjectData.ExtractFrom(readable); err != nil {
|
||||
return fmt.Errorf("Failed to read AnyDataHolder object data. %s", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a new copied instance of AnyDataHolder
|
||||
func (adh *AnyDataHolder) Copy() RVType {
|
||||
copied := NewAnyDataHolder()
|
||||
|
||||
copied.TypeName = adh.TypeName.Copy().(*String)
|
||||
copied.Length1 = adh.Length1.Copy().(*PrimitiveU32)
|
||||
copied.Length2 = adh.Length2.Copy().(*PrimitiveU32)
|
||||
copied.ObjectData = adh.ObjectData.Copy()
|
||||
|
||||
return copied
|
||||
}
|
||||
|
||||
// Equals checks if the passed Structure contains the same data as the current instance
|
||||
func (adh *AnyDataHolder) Equals(o RVType) bool {
|
||||
if _, ok := o.(*AnyDataHolder); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
other := o.(*AnyDataHolder)
|
||||
|
||||
if !adh.TypeName.Equals(other.TypeName) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !adh.Length1.Equals(other.Length1) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !adh.Length2.Equals(other.Length2) {
|
||||
return false
|
||||
}
|
||||
|
||||
return adh.ObjectData.Equals(other.ObjectData)
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (adh *AnyDataHolder) String() string {
|
||||
return adh.FormatToString(0)
|
||||
}
|
||||
|
||||
// FormatToString pretty-prints the struct data using the provided indentation level
|
||||
func (adh *AnyDataHolder) FormatToString(indentationLevel int) string {
|
||||
indentationValues := strings.Repeat("\t", indentationLevel+1)
|
||||
indentationEnd := strings.Repeat("\t", indentationLevel)
|
||||
|
||||
var b strings.Builder
|
||||
|
||||
b.WriteString("AnyDataHolder{\n")
|
||||
b.WriteString(fmt.Sprintf("%sTypeName: %s,\n", indentationValues, adh.TypeName))
|
||||
b.WriteString(fmt.Sprintf("%sLength1: %s,\n", indentationValues, adh.Length1))
|
||||
b.WriteString(fmt.Sprintf("%sLength2: %s,\n", indentationValues, adh.Length2))
|
||||
b.WriteString(fmt.Sprintf("%sObjectData: %s\n", indentationValues, adh.ObjectData))
|
||||
|
||||
b.WriteString(fmt.Sprintf("%s}", indentationEnd))
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// NewAnyDataHolder returns a new AnyDataHolder
|
||||
func NewAnyDataHolder() *AnyDataHolder {
|
||||
return &AnyDataHolder{
|
||||
TypeName: NewString(""),
|
||||
Length1: NewPrimitiveU32(0),
|
||||
Length2: NewPrimitiveU32(0),
|
||||
}
|
||||
}
|
142
types/any_object_holder.go
Normal file
142
types/any_object_holder.go
Normal file
|
@ -0,0 +1,142 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HoldableObject defines a common interface for types which can be placed in AnyObjectHolder
|
||||
type HoldableObject interface {
|
||||
RVType
|
||||
ObjectID() RVType // Returns the object identifier of the type
|
||||
}
|
||||
|
||||
// AnyObjectHolderObjects holds a mapping of RVTypes that are accessible in a AnyDataHolder
|
||||
var AnyObjectHolderObjects = make(map[RVType]HoldableObject)
|
||||
|
||||
// RegisterObjectHolderType registers a RVType to be accessible in a AnyDataHolder
|
||||
func RegisterObjectHolderType(rvType HoldableObject) {
|
||||
AnyObjectHolderObjects[rvType.ObjectID()] = rvType
|
||||
}
|
||||
|
||||
// AnyObjectHolder can hold a reference to any RVType which can be held
|
||||
type AnyObjectHolder[T HoldableObject] struct {
|
||||
Object T
|
||||
}
|
||||
|
||||
// WriteTo writes the AnyObjectHolder to the given writable
|
||||
func (aoh AnyObjectHolder[T]) WriteTo(writable Writable) {
|
||||
contentWritable := writable.CopyNew()
|
||||
|
||||
aoh.Object.WriteTo(contentWritable)
|
||||
|
||||
objectBuffer := NewBuffer(contentWritable.Bytes())
|
||||
|
||||
objectBufferLength := uint32(len(objectBuffer) + 4) // * Length of the Buffer
|
||||
|
||||
aoh.Object.ObjectID().WriteTo(writable)
|
||||
writable.WriteUInt32LE(objectBufferLength)
|
||||
objectBuffer.WriteTo(writable)
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the AnyObjectHolder from the given readable
|
||||
func (aoh *AnyObjectHolder[T]) ExtractFrom(readable Readable) error {
|
||||
var err error
|
||||
|
||||
// TODO - This assumes the identifier is a String
|
||||
identifier := NewString("")
|
||||
err = identifier.ExtractFrom(readable)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read AnyObjectHolder identifier. %s", err.Error())
|
||||
}
|
||||
|
||||
length := NewUInt32(0)
|
||||
err = length.ExtractFrom(readable)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read AnyObjectHolder length. %s", err.Error())
|
||||
}
|
||||
|
||||
// * This is technically a Buffer, but we can't instantiate a new Readable from here so interpret it as a UInt32 and the object data
|
||||
bufferLength := NewUInt32(0)
|
||||
err = bufferLength.ExtractFrom(readable)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read AnyObjectHolder buffer length. %s", err.Error())
|
||||
}
|
||||
|
||||
if _, ok := AnyObjectHolderObjects[identifier]; !ok {
|
||||
return fmt.Errorf("Unknown AnyObjectHolder identifier: %s", identifier)
|
||||
}
|
||||
|
||||
ptr := AnyObjectHolderObjects[identifier].CopyRef()
|
||||
|
||||
if err := ptr.ExtractFrom(readable); err != nil {
|
||||
return fmt.Errorf("Failed to read AnyObjectHolder object. %s", err.Error())
|
||||
}
|
||||
|
||||
var ok bool
|
||||
if aoh.Object, ok = ptr.Deref().(T); !ok {
|
||||
return fmt.Errorf("Input AnyObjectHolder object %s is invalid", identifier)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a new copied instance of AnyObjectHolder
|
||||
func (aoh AnyObjectHolder[T]) Copy() RVType {
|
||||
copied := NewAnyObjectHolder[T]()
|
||||
|
||||
copied.Object = aoh.Object.Copy().(T)
|
||||
|
||||
return copied
|
||||
}
|
||||
|
||||
// Equals checks if the passed Structure contains the same data as the current instance
|
||||
func (aoh AnyObjectHolder[T]) Equals(o RVType) bool {
|
||||
if _, ok := o.(AnyObjectHolder[T]); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
other := o.(AnyObjectHolder[T])
|
||||
|
||||
return aoh.Object.Equals(other.Object)
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the AnyObjectHolder
|
||||
// and returns a pointer to the new copy
|
||||
func (aoh AnyObjectHolder[T]) CopyRef() RVTypePtr {
|
||||
copied := aoh.Copy().(AnyObjectHolder[T])
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the AnyObjectHolder
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (aoh *AnyObjectHolder[T]) Deref() RVType {
|
||||
return *aoh
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (aoh AnyObjectHolder[T]) String() string {
|
||||
return aoh.FormatToString(0)
|
||||
}
|
||||
|
||||
// FormatToString pretty-prints the struct data using the provided indentation level
|
||||
func (aoh AnyObjectHolder[T]) FormatToString(indentationLevel int) string {
|
||||
indentationValues := strings.Repeat("\t", indentationLevel+1)
|
||||
indentationEnd := strings.Repeat("\t", indentationLevel)
|
||||
|
||||
var b strings.Builder
|
||||
|
||||
b.WriteString("AnyDataHolder{\n")
|
||||
b.WriteString(fmt.Sprintf("%sIdentifier: %s,\n", indentationValues, aoh.Object.ObjectID()))
|
||||
b.WriteString(fmt.Sprintf("%sObject: %s\n", indentationValues, aoh.Object))
|
||||
|
||||
b.WriteString(fmt.Sprintf("%s}", indentationEnd))
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// NewAnyObjectHolder returns a new AnyObjectHolder
|
||||
func NewAnyObjectHolder[T HoldableObject]() AnyObjectHolder[T] {
|
||||
return AnyObjectHolder[T]{}
|
||||
}
|
62
types/bool.go
Normal file
62
types/bool.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Bool is a type alias for the Go basic type bool for use as an RVType
|
||||
type Bool bool
|
||||
|
||||
// WriteTo writes the Bool to the given writable
|
||||
func (b Bool) WriteTo(writable Writable) {
|
||||
writable.WriteBool(bool(b))
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the Bool value from the given readable
|
||||
func (b *Bool) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadBool()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*b = Bool(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the Bool. Requires type assertion when used
|
||||
func (b Bool) Copy() RVType {
|
||||
return NewBool(bool(b))
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (b Bool) Equals(o RVType) bool {
|
||||
other, ok := o.(Bool)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return b == other
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the Bool
|
||||
// and returns a pointer to the new copy
|
||||
func (b Bool) CopyRef() RVTypePtr {
|
||||
copied := b.Copy().(Bool)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the Bool
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (b *Bool) Deref() RVType {
|
||||
return *b
|
||||
}
|
||||
|
||||
// String returns a string representation of the Bool
|
||||
func (b Bool) String() string {
|
||||
return fmt.Sprintf("%t", b)
|
||||
}
|
||||
|
||||
// NewBool returns a new Bool
|
||||
func NewBool(input bool) Bool {
|
||||
b := Bool(input)
|
||||
return b
|
||||
}
|
|
@ -2,30 +2,29 @@ package types
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Buffer is an implementation of rdv::Buffer.
|
||||
// Wraps a primitive Go byte slice.
|
||||
// Type alias of []byte.
|
||||
// Same as QBuffer but with a uint32 length field.
|
||||
type Buffer struct {
|
||||
Value []byte
|
||||
}
|
||||
type Buffer []byte
|
||||
|
||||
// WriteTo writes the Buffer to the given writable
|
||||
func (b *Buffer) WriteTo(writable Writable) {
|
||||
length := len(b.Value)
|
||||
func (b Buffer) WriteTo(writable Writable) {
|
||||
length := len(b)
|
||||
|
||||
writable.WritePrimitiveUInt32LE(uint32(length))
|
||||
writable.WriteUInt32LE(uint32(length))
|
||||
|
||||
if length > 0 {
|
||||
writable.Write(b.Value)
|
||||
writable.Write(b)
|
||||
}
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the Buffer from the given readable
|
||||
func (b *Buffer) ExtractFrom(readable Readable) error {
|
||||
length, err := readable.ReadPrimitiveUInt32LE()
|
||||
length, err := readable.ReadUInt32LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read NEX Buffer length. %s", err.Error())
|
||||
}
|
||||
|
@ -35,31 +34,47 @@ func (b *Buffer) ExtractFrom(readable Readable) error {
|
|||
return fmt.Errorf("Failed to read NEX Buffer data. %s", err.Error())
|
||||
}
|
||||
|
||||
b.Value = value
|
||||
|
||||
*b = Buffer(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the Buffer. Requires type assertion when used
|
||||
func (b *Buffer) Copy() RVType {
|
||||
return NewBuffer(b.Value)
|
||||
func (b Buffer) Copy() RVType {
|
||||
return NewBuffer(b)
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (b *Buffer) Equals(o RVType) bool {
|
||||
if _, ok := o.(*Buffer); !ok {
|
||||
func (b Buffer) Equals(o RVType) bool {
|
||||
if _, ok := o.(Buffer); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return bytes.Equal(b.Value, o.(*Buffer).Value)
|
||||
return bytes.Equal(b, o.(Buffer))
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the Buffer
|
||||
// and returns a pointer to the new copy
|
||||
func (b Buffer) CopyRef() RVTypePtr {
|
||||
copied := b.Copy().(Buffer)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the Buffer
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (b *Buffer) Deref() RVType {
|
||||
return *b
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (b *Buffer) String() string {
|
||||
return fmt.Sprintf("%x", b.Value)
|
||||
func (b Buffer) String() string {
|
||||
return hex.EncodeToString(b)
|
||||
}
|
||||
|
||||
// NewBuffer returns a new Buffer
|
||||
func NewBuffer(data []byte) *Buffer {
|
||||
return &Buffer{Value: data}
|
||||
func NewBuffer(input []byte) Buffer {
|
||||
b := make(Buffer, len(input))
|
||||
copy(b, input)
|
||||
|
||||
return b
|
||||
}
|
||||
|
|
|
@ -9,11 +9,11 @@ import (
|
|||
// Contains version info for Structures used in verbose RMC messages.
|
||||
type ClassVersionContainer struct {
|
||||
Structure
|
||||
ClassVersions *Map[*String, *PrimitiveU16]
|
||||
ClassVersions Map[String, UInt16] `json:"class_versions" db:"class_versions" bson:"class_versions" xml:"ClassVersions"`
|
||||
}
|
||||
|
||||
// WriteTo writes the ClassVersionContainer to the given writable
|
||||
func (cvc *ClassVersionContainer) WriteTo(writable Writable) {
|
||||
func (cvc ClassVersionContainer) WriteTo(writable Writable) {
|
||||
cvc.ClassVersions.WriteTo(writable)
|
||||
}
|
||||
|
||||
|
@ -23,29 +23,43 @@ func (cvc *ClassVersionContainer) ExtractFrom(readable Readable) error {
|
|||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the ClassVersionContainer. Requires type assertion when used
|
||||
func (cvc *ClassVersionContainer) Copy() RVType {
|
||||
func (cvc ClassVersionContainer) Copy() RVType {
|
||||
copied := NewClassVersionContainer()
|
||||
copied.ClassVersions = cvc.ClassVersions.Copy().(*Map[*String, *PrimitiveU16])
|
||||
copied.ClassVersions = cvc.ClassVersions.Copy().(Map[String, UInt16])
|
||||
|
||||
return copied
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (cvc *ClassVersionContainer) Equals(o RVType) bool {
|
||||
if _, ok := o.(*ClassVersionContainer); !ok {
|
||||
func (cvc ClassVersionContainer) Equals(o RVType) bool {
|
||||
if _, ok := o.(ClassVersionContainer); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return cvc.ClassVersions.Equals(o)
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the ClassVersionContainer
|
||||
// and returns a pointer to the new copy
|
||||
func (cvc ClassVersionContainer) CopyRef() RVTypePtr {
|
||||
copied := cvc.Copy().(ClassVersionContainer)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the ClassVersionContainer
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (cvc *ClassVersionContainer) Deref() RVType {
|
||||
return *cvc
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (cvc *ClassVersionContainer) String() string {
|
||||
func (cvc ClassVersionContainer) String() string {
|
||||
return cvc.FormatToString(0)
|
||||
}
|
||||
|
||||
// FormatToString pretty-prints the struct data using the provided indentation level
|
||||
func (cvc *ClassVersionContainer) FormatToString(indentationLevel int) string {
|
||||
func (cvc ClassVersionContainer) FormatToString(indentationLevel int) string {
|
||||
indentationValues := strings.Repeat("\t", indentationLevel+1)
|
||||
indentationEnd := strings.Repeat("\t", indentationLevel)
|
||||
|
||||
|
@ -60,13 +74,10 @@ func (cvc *ClassVersionContainer) FormatToString(indentationLevel int) string {
|
|||
}
|
||||
|
||||
// NewClassVersionContainer returns a new ClassVersionContainer
|
||||
func NewClassVersionContainer() *ClassVersionContainer {
|
||||
cvc := &ClassVersionContainer{
|
||||
ClassVersions: NewMap[*String, *PrimitiveU16](),
|
||||
func NewClassVersionContainer() ClassVersionContainer {
|
||||
cvc := ClassVersionContainer{
|
||||
ClassVersions: NewMap[String, UInt16](),
|
||||
}
|
||||
|
||||
cvc.ClassVersions.KeyType = NewString("")
|
||||
cvc.ClassVersions.ValueType = NewPrimitiveU16(0)
|
||||
|
||||
return cvc
|
||||
}
|
||||
|
|
|
@ -11,8 +11,18 @@ type Data struct {
|
|||
Structure
|
||||
}
|
||||
|
||||
// ObjectID returns the object identifier of the type
|
||||
func (d Data) ObjectID() RVType {
|
||||
return d.DataObjectID()
|
||||
}
|
||||
|
||||
// DataObjectID returns the object identifier of the type embedding Data
|
||||
func (d Data) DataObjectID() RVType {
|
||||
return NewString("Data")
|
||||
}
|
||||
|
||||
// WriteTo writes the Data to the given writable
|
||||
func (d *Data) WriteTo(writable Writable) {
|
||||
func (d Data) WriteTo(writable Writable) {
|
||||
d.WriteHeaderTo(writable, 0)
|
||||
}
|
||||
|
||||
|
@ -26,7 +36,7 @@ func (d *Data) ExtractFrom(readable Readable) error {
|
|||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the Data. Requires type assertion when used
|
||||
func (d *Data) Copy() RVType {
|
||||
func (d Data) Copy() RVType {
|
||||
copied := NewData()
|
||||
copied.StructureVersion = d.StructureVersion
|
||||
|
||||
|
@ -34,23 +44,37 @@ func (d *Data) Copy() RVType {
|
|||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (d *Data) Equals(o RVType) bool {
|
||||
if _, ok := o.(*Data); !ok {
|
||||
func (d Data) Equals(o RVType) bool {
|
||||
if _, ok := o.(Data); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
other := o.(*Data)
|
||||
other := o.(Data)
|
||||
|
||||
return d.StructureVersion == other.StructureVersion
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the Data
|
||||
// and returns a pointer to the new copy
|
||||
func (d Data) CopyRef() RVTypePtr {
|
||||
copied := d.Copy().(Data)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the Data
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (d *Data) Deref() RVType {
|
||||
return *d
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (d *Data) String() string {
|
||||
func (d Data) String() string {
|
||||
return d.FormatToString(0)
|
||||
}
|
||||
|
||||
// FormatToString pretty-prints the struct data using the provided indentation level
|
||||
func (d *Data) FormatToString(indentationLevel int) string {
|
||||
func (d Data) FormatToString(indentationLevel int) string {
|
||||
indentationValues := strings.Repeat("\t", indentationLevel+1)
|
||||
indentationEnd := strings.Repeat("\t", indentationLevel)
|
||||
|
||||
|
@ -64,6 +88,6 @@ func (d *Data) FormatToString(indentationLevel int) string {
|
|||
}
|
||||
|
||||
// NewData returns a new Data Structure
|
||||
func NewData() *Data {
|
||||
return &Data{}
|
||||
func NewData() Data {
|
||||
return Data{}
|
||||
}
|
||||
|
|
16
types/data_holder.go
Normal file
16
types/data_holder.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
package types
|
||||
|
||||
// DataInterface defines an interface to track types which have Data anywhere
|
||||
// in their parent tree.
|
||||
type DataInterface interface {
|
||||
HoldableObject
|
||||
DataObjectID() RVType // Returns the object identifier of the type embedding Data
|
||||
}
|
||||
|
||||
// DataHolder is an AnyObjectHolder for types which embed Data
|
||||
type DataHolder = AnyObjectHolder[DataInterface]
|
||||
|
||||
// NewDataHolder returns a new DataHolder
|
||||
func NewDataHolder() DataHolder {
|
||||
return DataHolder{}
|
||||
}
|
|
@ -7,51 +7,63 @@ import (
|
|||
)
|
||||
|
||||
// DateTime is an implementation of rdv::DateTime.
|
||||
// Type alias of uint64.
|
||||
// The underlying value is a uint64 bit field containing date and time information.
|
||||
type DateTime struct {
|
||||
value uint64
|
||||
}
|
||||
type DateTime uint64
|
||||
|
||||
// WriteTo writes the DateTime to the given writable
|
||||
func (dt *DateTime) WriteTo(writable Writable) {
|
||||
writable.WritePrimitiveUInt64LE(dt.value)
|
||||
func (dt DateTime) WriteTo(writable Writable) {
|
||||
writable.WriteUInt64LE(uint64(dt))
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the DateTime from the given readable
|
||||
func (dt *DateTime) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadPrimitiveUInt64LE()
|
||||
value, err := readable.ReadUInt64LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read DateTime value. %s", err.Error())
|
||||
}
|
||||
|
||||
dt.value = value
|
||||
|
||||
*dt = DateTime(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a new copied instance of DateTime
|
||||
func (dt *DateTime) Copy() RVType {
|
||||
return NewDateTime(dt.value)
|
||||
func (dt DateTime) Copy() RVType {
|
||||
return NewDateTime(uint64(dt))
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (dt *DateTime) Equals(o RVType) bool {
|
||||
if _, ok := o.(*DateTime); !ok {
|
||||
func (dt DateTime) Equals(o RVType) bool {
|
||||
if _, ok := o.(DateTime); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return dt.value == o.(*DateTime).value
|
||||
return dt == o.(DateTime)
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the DateTime
|
||||
// and returns a pointer to the new copy
|
||||
func (dt DateTime) CopyRef() RVTypePtr {
|
||||
copied := dt.Copy().(DateTime)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the DateTime
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (dt *DateTime) Deref() RVType {
|
||||
return *dt
|
||||
}
|
||||
|
||||
// Make initilizes a DateTime with the input data
|
||||
func (dt *DateTime) Make(year, month, day, hour, minute, second int) *DateTime {
|
||||
dt.value = uint64(second | (minute << 6) | (hour << 12) | (day << 17) | (month << 22) | (year << 26))
|
||||
func (dt *DateTime) Make(year, month, day, hour, minute, second int) DateTime {
|
||||
*dt = DateTime(second | (minute << 6) | (hour << 12) | (day << 17) | (month << 22) | (year << 26))
|
||||
|
||||
return dt
|
||||
return *dt
|
||||
}
|
||||
|
||||
// FromTimestamp converts a Time timestamp into a NEX DateTime
|
||||
func (dt *DateTime) FromTimestamp(timestamp time.Time) *DateTime {
|
||||
func (dt *DateTime) FromTimestamp(timestamp time.Time) DateTime {
|
||||
year := timestamp.Year()
|
||||
month := int(timestamp.Month())
|
||||
day := timestamp.Day()
|
||||
|
@ -63,47 +75,42 @@ func (dt *DateTime) FromTimestamp(timestamp time.Time) *DateTime {
|
|||
}
|
||||
|
||||
// Now returns a NEX DateTime value of the current UTC time
|
||||
func (dt *DateTime) Now() *DateTime {
|
||||
func (dt DateTime) Now() DateTime {
|
||||
return dt.FromTimestamp(time.Now().UTC())
|
||||
}
|
||||
|
||||
// Value returns the stored DateTime time
|
||||
func (dt *DateTime) Value() uint64 {
|
||||
return dt.value
|
||||
}
|
||||
|
||||
// Second returns the seconds value stored in the DateTime
|
||||
func (dt *DateTime) Second() int {
|
||||
return int(dt.value & 63)
|
||||
func (dt DateTime) Second() int {
|
||||
return int(dt & 63)
|
||||
}
|
||||
|
||||
// Minute returns the minutes value stored in the DateTime
|
||||
func (dt *DateTime) Minute() int {
|
||||
return int((dt.value >> 6) & 63)
|
||||
func (dt DateTime) Minute() int {
|
||||
return int((dt >> 6) & 63)
|
||||
}
|
||||
|
||||
// Hour returns the hours value stored in the DateTime
|
||||
func (dt *DateTime) Hour() int {
|
||||
return int((dt.value >> 12) & 31)
|
||||
func (dt DateTime) Hour() int {
|
||||
return int((dt >> 12) & 31)
|
||||
}
|
||||
|
||||
// Day returns the day value stored in the DateTime
|
||||
func (dt *DateTime) Day() int {
|
||||
return int((dt.value >> 17) & 31)
|
||||
func (dt DateTime) Day() int {
|
||||
return int((dt >> 17) & 31)
|
||||
}
|
||||
|
||||
// Month returns the month value stored in the DateTime
|
||||
func (dt *DateTime) Month() time.Month {
|
||||
return time.Month((dt.value >> 22) & 15)
|
||||
func (dt DateTime) Month() time.Month {
|
||||
return time.Month((dt >> 22) & 15)
|
||||
}
|
||||
|
||||
// Year returns the year value stored in the DateTime
|
||||
func (dt *DateTime) Year() int {
|
||||
return int(dt.value >> 26)
|
||||
func (dt DateTime) Year() int {
|
||||
return int(dt >> 26)
|
||||
}
|
||||
|
||||
// Standard returns the DateTime as a standard time.Time
|
||||
func (dt *DateTime) Standard() time.Time {
|
||||
func (dt DateTime) Standard() time.Time {
|
||||
return time.Date(
|
||||
dt.Year(),
|
||||
dt.Month(),
|
||||
|
@ -117,25 +124,26 @@ func (dt *DateTime) Standard() time.Time {
|
|||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (dt *DateTime) String() string {
|
||||
func (dt DateTime) String() string {
|
||||
return dt.FormatToString(0)
|
||||
}
|
||||
|
||||
// FormatToString pretty-prints the struct data using the provided indentation level
|
||||
func (dt *DateTime) FormatToString(indentationLevel int) string {
|
||||
func (dt DateTime) FormatToString(indentationLevel int) string {
|
||||
indentationValues := strings.Repeat("\t", indentationLevel+1)
|
||||
indentationEnd := strings.Repeat("\t", indentationLevel)
|
||||
|
||||
var b strings.Builder
|
||||
|
||||
b.WriteString("DateTime{\n")
|
||||
b.WriteString(fmt.Sprintf("%svalue: %d (%s)\n", indentationValues, dt.value, dt.Standard().Format("2006-01-02 15:04:05")))
|
||||
b.WriteString(fmt.Sprintf("%svalue: %d (%s)\n", indentationValues, dt, dt.Standard().Format("2006-01-02 15:04:05")))
|
||||
b.WriteString(fmt.Sprintf("%s}", indentationEnd))
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// NewDateTime returns a new DateTime instance
|
||||
func NewDateTime(value uint64) *DateTime {
|
||||
return &DateTime{value: value}
|
||||
func NewDateTime(input uint64) DateTime {
|
||||
dt := DateTime(input)
|
||||
return dt
|
||||
}
|
||||
|
|
62
types/double.go
Normal file
62
types/double.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Double is a type alias for the Go basic type float64 for use as an RVType
|
||||
type Double float64
|
||||
|
||||
// WriteTo writes the Double to the given writable
|
||||
func (d Double) WriteTo(writable Writable) {
|
||||
writable.WriteFloat64LE(float64(d))
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the Double value from the given readable
|
||||
func (d *Double) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadFloat64LE()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*d = Double(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the Double. Requires type assertion when used
|
||||
func (d Double) Copy() RVType {
|
||||
return NewDouble(float64(d))
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (d Double) Equals(o RVType) bool {
|
||||
other, ok := o.(Double)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return d == other
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the Double
|
||||
// and returns a pointer to the new copy
|
||||
func (d Double) CopyRef() RVTypePtr {
|
||||
copied := d.Copy().(Double)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the Double
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (d *Double) Deref() RVType {
|
||||
return *d
|
||||
}
|
||||
|
||||
// String returns a string representation of the Double
|
||||
func (d Double) String() string {
|
||||
return fmt.Sprintf("%f", d)
|
||||
}
|
||||
|
||||
// NewDouble returns a new Double
|
||||
func NewDouble(input float64) Double {
|
||||
d := Double(input)
|
||||
return d
|
||||
}
|
62
types/float.go
Normal file
62
types/float.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Float is a type alias for the Go basic type float32 for use as an RVType
|
||||
type Float float32
|
||||
|
||||
// WriteTo writes the Float to the given writable
|
||||
func (f Float) WriteTo(writable Writable) {
|
||||
writable.WriteFloat32LE(float32(f))
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the Float value from the given readable
|
||||
func (f *Float) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadFloat32LE()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*f = Float(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the Float. Requires type assertion when used
|
||||
func (f Float) Copy() RVType {
|
||||
return NewFloat(float32(f))
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (f Float) Equals(o RVType) bool {
|
||||
other, ok := o.(Float)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return f == other
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the Float
|
||||
// and returns a pointer to the new copy
|
||||
func (f Float) CopyRef() RVTypePtr {
|
||||
copied := f.Copy().(Float)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the Float
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (f *Float) Deref() RVType {
|
||||
return *f
|
||||
}
|
||||
|
||||
// String returns a string representation of the Float
|
||||
func (f Float) String() string {
|
||||
return fmt.Sprintf("%f", f)
|
||||
}
|
||||
|
||||
// NewFloat returns a new Float
|
||||
func NewFloat(input float32) Float {
|
||||
f := Float(input)
|
||||
return f
|
||||
}
|
62
types/int16.go
Normal file
62
types/int16.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Int16 is a type alias for the Go basic type int16 for use as an RVType
|
||||
type Int16 int16
|
||||
|
||||
// WriteTo writes the Int16 to the given writable
|
||||
func (i16 Int16) WriteTo(writable Writable) {
|
||||
writable.WriteInt16LE(int16(i16))
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the Int16 value from the given readable
|
||||
func (i16 *Int16) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadInt16LE()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*i16 = Int16(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the Int16. Requires type assertion when used
|
||||
func (i16 Int16) Copy() RVType {
|
||||
return NewInt16(int16(i16))
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (i16 Int16) Equals(o RVType) bool {
|
||||
other, ok := o.(Int16)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return i16 == other
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the Int16
|
||||
// and returns a pointer to the new copy
|
||||
func (i16 Int16) CopyRef() RVTypePtr {
|
||||
copied := i16.Copy().(Int16)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the Int16
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (i16 *Int16) Deref() RVType {
|
||||
return *i16
|
||||
}
|
||||
|
||||
// String returns a string representation of the Int16
|
||||
func (i16 Int16) String() string {
|
||||
return fmt.Sprintf("%d", i16)
|
||||
}
|
||||
|
||||
// NewInt16 returns a new Int16
|
||||
func NewInt16(input int16) Int16 {
|
||||
i16 := Int16(input)
|
||||
return i16
|
||||
}
|
62
types/int32.go
Normal file
62
types/int32.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Int32 is a type alias for the Go basic type int32 for use as an RVType
|
||||
type Int32 int32
|
||||
|
||||
// WriteTo writes the Int32 to the given writable
|
||||
func (i32 Int32) WriteTo(writable Writable) {
|
||||
writable.WriteInt32LE(int32(i32))
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the Int32 value from the given readable
|
||||
func (i32 *Int32) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadInt32LE()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*i32 = Int32(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the Int32. Requires type assertion when used
|
||||
func (i32 Int32) Copy() RVType {
|
||||
return NewInt32(int32(i32))
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (i32 Int32) Equals(o RVType) bool {
|
||||
other, ok := o.(Int32)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return i32 == other
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the Int32
|
||||
// and returns a pointer to the new copy
|
||||
func (i32 Int32) CopyRef() RVTypePtr {
|
||||
copied := i32.Copy().(Int32)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the Int32
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (i32 *Int32) Deref() RVType {
|
||||
return *i32
|
||||
}
|
||||
|
||||
// String returns a string representation of the Int32
|
||||
func (i32 Int32) String() string {
|
||||
return fmt.Sprintf("%d", i32)
|
||||
}
|
||||
|
||||
// NewInt32 returns a new Int32
|
||||
func NewInt32(input int32) Int32 {
|
||||
i32 := Int32(input)
|
||||
return i32
|
||||
}
|
62
types/int64.go
Normal file
62
types/int64.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Int64 is a type alias for the Go basic type int64 for use as an RVType
|
||||
type Int64 int64
|
||||
|
||||
// WriteTo writes the Int64 to the given writable
|
||||
func (i64 Int64) WriteTo(writable Writable) {
|
||||
writable.WriteInt64LE(int64(i64))
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the Int64 value from the given readable
|
||||
func (i64 *Int64) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadInt64LE()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*i64 = Int64(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the Int64. Requires type assertion when used
|
||||
func (i64 Int64) Copy() RVType {
|
||||
return NewInt64(int64(i64))
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (i64 Int64) Equals(o RVType) bool {
|
||||
other, ok := o.(Int64)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return i64 == other
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the Int64
|
||||
// and returns a pointer to the new copy
|
||||
func (i64 Int64) CopyRef() RVTypePtr {
|
||||
copied := i64.Copy().(Int64)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the Int64
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (i64 *Int64) Deref() RVType {
|
||||
return *i64
|
||||
}
|
||||
|
||||
// String returns a string representation of the Int64
|
||||
func (i64 Int64) String() string {
|
||||
return fmt.Sprintf("%d", i64)
|
||||
}
|
||||
|
||||
// NewInt64 returns a new Int64
|
||||
func NewInt64(input int64) Int64 {
|
||||
i64 := Int64(input)
|
||||
return i64
|
||||
}
|
62
types/int8.go
Normal file
62
types/int8.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Int8 is a type alias for the Go basic type int8 for use as an RVType
|
||||
type Int8 int8
|
||||
|
||||
// WriteTo writes the Int8 to the given writable
|
||||
func (i8 Int8) WriteTo(writable Writable) {
|
||||
writable.WriteInt8(int8(i8))
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the Int8 value from the given readable
|
||||
func (i8 *Int8) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadInt8()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*i8 = Int8(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the Int8. Requires type assertion when used
|
||||
func (i8 Int8) Copy() RVType {
|
||||
return NewInt8(int8(i8))
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (i8 Int8) Equals(o RVType) bool {
|
||||
other, ok := o.(Int8)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return i8 == other
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the Int8
|
||||
// and returns a pointer to the new copy
|
||||
func (i8 Int8) CopyRef() RVTypePtr {
|
||||
copied := i8.Copy().(Int8)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the Int8
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (i8 *Int8) Deref() RVType {
|
||||
return *i8
|
||||
}
|
||||
|
||||
// String returns a string representation of the Int8
|
||||
func (i8 Int8) String() string {
|
||||
return fmt.Sprintf("%d", i8)
|
||||
}
|
||||
|
||||
// NewInt8 returns a new Int8
|
||||
func NewInt8(input int8) Int8 {
|
||||
i8 := Int8(input)
|
||||
return i8
|
||||
}
|
158
types/list.go
158
types/list.go
|
@ -1,7 +1,6 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
|
@ -10,23 +9,32 @@ import (
|
|||
//
|
||||
// Unlike Buffer and qBuffer, which use the same data type with differing size field lengths,
|
||||
// there does not seem to be an official rdv::List type
|
||||
type List[T RVType] struct {
|
||||
real []T
|
||||
Type T
|
||||
}
|
||||
type List[T RVType] []T
|
||||
|
||||
// WriteTo writes the List to the given writable
|
||||
func (l *List[T]) WriteTo(writable Writable) {
|
||||
writable.WritePrimitiveUInt32LE(uint32(len(l.real)))
|
||||
func (l List[T]) WriteTo(writable Writable) {
|
||||
writable.WriteUInt32LE(uint32(len(l)))
|
||||
|
||||
for _, v := range l.real {
|
||||
for _, v := range l {
|
||||
v.WriteTo(writable)
|
||||
}
|
||||
}
|
||||
|
||||
func (l List[T]) extractType(t any, readable Readable) error {
|
||||
// * This just makes List.ExtractFrom() a bit cleaner
|
||||
// * since it doesn't have to type check
|
||||
if ptr, ok := t.(RVTypePtr); ok {
|
||||
return ptr.ExtractFrom(readable)
|
||||
}
|
||||
|
||||
// * Maybe support other types..?
|
||||
|
||||
return fmt.Errorf("Unsupported List type %T", t)
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the List from the given readable
|
||||
func (l *List[T]) ExtractFrom(readable Readable) error {
|
||||
length, err := readable.ReadPrimitiveUInt32LE()
|
||||
length, err := readable.ReadUInt32LE()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -34,46 +42,44 @@ func (l *List[T]) ExtractFrom(readable Readable) error {
|
|||
slice := make([]T, 0, length)
|
||||
|
||||
for i := 0; i < int(length); i++ {
|
||||
value := l.Type.Copy()
|
||||
if err := value.ExtractFrom(readable); err != nil {
|
||||
var value T
|
||||
if err := l.extractType(&value, readable); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
slice = append(slice, value.(T))
|
||||
slice = append(slice, value)
|
||||
}
|
||||
|
||||
l.real = slice
|
||||
*l = slice
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the List. Requires type assertion when used
|
||||
func (l *List[T]) Copy() RVType {
|
||||
copied := NewList[T]()
|
||||
copied.real = make([]T, len(l.real))
|
||||
copied.Type = l.Type.Copy().(T)
|
||||
func (l List[T]) Copy() RVType {
|
||||
copied := make(List[T], 0)
|
||||
|
||||
for i, v := range l.real {
|
||||
copied.real[i] = v.Copy().(T)
|
||||
for _, v := range l {
|
||||
copied = append(copied, v.Copy().(T))
|
||||
}
|
||||
|
||||
return copied
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (l *List[T]) Equals(o RVType) bool {
|
||||
if _, ok := o.(*List[T]); !ok {
|
||||
func (l List[T]) Equals(o RVType) bool {
|
||||
if _, ok := o.(List[T]); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
other := o.(*List[T])
|
||||
other := o.(List[T])
|
||||
|
||||
if len(l.real) != len(other.real) {
|
||||
if len(l) != len(other) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(l.real); i++ {
|
||||
if !l.real[i].Equals(other.real[i]) {
|
||||
for i := 0; i < len(l); i++ {
|
||||
if !l[i].Equals(other[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -81,73 +87,24 @@ func (l *List[T]) Equals(o RVType) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// Slice returns the real underlying slice for the List
|
||||
func (l *List[T]) Slice() []T {
|
||||
return l.real
|
||||
// CopyRef copies the current value of the List
|
||||
// and returns a pointer to the new copy
|
||||
func (l List[T]) CopyRef() RVTypePtr {
|
||||
copied := l.Copy().(List[T])
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Append appends an element to the List internal slice
|
||||
func (l *List[T]) Append(value T) {
|
||||
l.real = append(l.real, value)
|
||||
// Deref takes a pointer to the List
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (l *List[T]) Deref() RVType {
|
||||
return *l
|
||||
}
|
||||
|
||||
// Get returns an element at the given index. Returns an error if the index is OOB
|
||||
func (l *List[T]) Get(index int) (T, error) {
|
||||
if index < 0 || index >= len(l.real) {
|
||||
return l.Type.Copy().(T), errors.New("Index out of bounds")
|
||||
}
|
||||
|
||||
return l.real[index], nil
|
||||
}
|
||||
|
||||
// SetIndex sets a value in the List at the given index
|
||||
func (l *List[T]) SetIndex(index int, value T) error {
|
||||
if index < 0 || index >= len(l.real) {
|
||||
return errors.New("Index out of bounds")
|
||||
}
|
||||
|
||||
l.real[index] = value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteIndex deletes an element at the given index. Returns an error if the index is OOB
|
||||
func (l *List[T]) DeleteIndex(index int) error {
|
||||
if index < 0 || index >= len(l.real) {
|
||||
return errors.New("Index out of bounds")
|
||||
}
|
||||
|
||||
l.real = append(l.real[:index], l.real[index+1:]...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes the first occurance of the input from the List. Returns an error if the index is OOB
|
||||
func (l *List[T]) Remove(check T) {
|
||||
for i, value := range l.real {
|
||||
if value.Equals(check) {
|
||||
l.DeleteIndex(i)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetFromData sets the List's internal slice to the input data
|
||||
func (l *List[T]) SetFromData(data []T) {
|
||||
l.real = data
|
||||
}
|
||||
|
||||
// Length returns the number of elements in the List
|
||||
func (l *List[T]) Length() int {
|
||||
return len(l.real)
|
||||
}
|
||||
|
||||
// Each runs a callback function for every element in the List
|
||||
// The List should not be modified inside the callback function
|
||||
// Returns true if the loop was terminated early
|
||||
func (l *List[T]) Each(callback func(i int, value T) bool) bool {
|
||||
for i, value := range l.real {
|
||||
if callback(i, value) {
|
||||
// Contains checks if the provided value exists in the List
|
||||
func (l List[T]) Contains(checkValue T) bool {
|
||||
for _, v := range l {
|
||||
if v.Equals(checkValue) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -155,29 +112,12 @@ func (l *List[T]) Each(callback func(i int, value T) bool) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Contains checks if the provided value exists in the List
|
||||
func (l *List[T]) Contains(checkValue T) bool {
|
||||
contains := false
|
||||
|
||||
l.Each(func(_ int, value T) bool {
|
||||
if value.Equals(checkValue) {
|
||||
contains = true
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
return contains
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (l *List[T]) String() string {
|
||||
return fmt.Sprintf("%v", l.real)
|
||||
func (l List[T]) String() string {
|
||||
return fmt.Sprintf("%v", ([]T)(l))
|
||||
}
|
||||
|
||||
// NewList returns a new List of the provided type
|
||||
func NewList[T RVType]() *List[T] {
|
||||
return &List[T]{real: make([]T, 0)}
|
||||
func NewList[T RVType]() List[T] {
|
||||
return make(List[T], 0)
|
||||
}
|
||||
|
|
202
types/map.go
202
types/map.go
|
@ -6,99 +6,120 @@ import (
|
|||
)
|
||||
|
||||
// Map represents a Quazal Rendez-Vous/NEX Map type.
|
||||
//
|
||||
// Type alias of map[K]V.
|
||||
// There is not an official type in either the rdv or nn::nex namespaces.
|
||||
// The data is stored as an array of key-value pairs.
|
||||
type Map[K RVType, V RVType] struct {
|
||||
// * Rendez-Vous/NEX MapMap types can have ANY value for the key, but Go requires
|
||||
// * map keys to implement the "comparable" constraint. This is not possible with
|
||||
// * RVTypes. We have to either break spec and only allow primitives as Map keys,
|
||||
// * or store the key/value types indirectly
|
||||
keys []K
|
||||
values []V
|
||||
KeyType K
|
||||
ValueType V
|
||||
// May have any RVType as both a key and value. If either they key or
|
||||
// value types are not an RVType, they are ignored.
|
||||
//
|
||||
// Incompatible with RVType pointers!
|
||||
type Map[K comparable, V RVType] map[K]V
|
||||
|
||||
func (m Map[K, V]) writeType(t any, writable Writable) {
|
||||
// * This just makes Map.WriteTo() a bit cleaner
|
||||
// * since it doesn't have to type check
|
||||
if rvt, ok := t.(interface{ WriteTo(writable Writable) }); ok {
|
||||
rvt.WriteTo(writable)
|
||||
}
|
||||
}
|
||||
|
||||
// WriteTo writes the Map to the given writable
|
||||
func (m *Map[K, V]) WriteTo(writable Writable) {
|
||||
writable.WritePrimitiveUInt32LE(uint32(m.Size()))
|
||||
func (m Map[K, V]) WriteTo(writable Writable) {
|
||||
writable.WriteUInt32LE(uint32(len(m)))
|
||||
|
||||
for i := 0; i < len(m.keys); i++ {
|
||||
m.keys[i].WriteTo(writable)
|
||||
m.values[i].WriteTo(writable)
|
||||
for key, value := range m {
|
||||
m.writeType(key, writable)
|
||||
m.writeType(value, writable)
|
||||
}
|
||||
}
|
||||
|
||||
func (m Map[K, V]) extractType(t any, readable Readable) error {
|
||||
// * This just makes Map.ExtractFrom() a bit cleaner
|
||||
// * since it doesn't have to type check
|
||||
if ptr, ok := t.(RVTypePtr); ok {
|
||||
return ptr.ExtractFrom(readable)
|
||||
}
|
||||
|
||||
// * Maybe support other types..?
|
||||
|
||||
return fmt.Errorf("Unsupported Map type %T", t)
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the Map from the given readable
|
||||
func (m *Map[K, V]) ExtractFrom(readable Readable) error {
|
||||
length, err := readable.ReadPrimitiveUInt32LE()
|
||||
length, err := readable.ReadUInt32LE()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keys := make([]K, 0, length)
|
||||
values := make([]V, 0, length)
|
||||
extracted := make(Map[K, V])
|
||||
|
||||
for i := 0; i < int(length); i++ {
|
||||
key := m.KeyType.Copy()
|
||||
if err := key.ExtractFrom(readable); err != nil {
|
||||
var key K
|
||||
if err := m.extractType(&key, readable); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
value := m.ValueType.Copy()
|
||||
if err := value.ExtractFrom(readable); err != nil {
|
||||
var value V
|
||||
if err := m.extractType(&value, readable); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keys = append(keys, key.(K))
|
||||
values = append(values, value.(V))
|
||||
extracted[key] = value
|
||||
}
|
||||
|
||||
m.keys = keys
|
||||
m.values = values
|
||||
*m = extracted
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the Map. Requires type assertion when used
|
||||
func (m *Map[K, V]) Copy() RVType {
|
||||
copied := NewMap[K, V]()
|
||||
copied.keys = make([]K, len(m.keys))
|
||||
copied.values = make([]V, len(m.values))
|
||||
copied.KeyType = m.KeyType.Copy().(K)
|
||||
copied.ValueType = m.ValueType.Copy().(V)
|
||||
func (m Map[K, V]) copyType(t any) RVType {
|
||||
// * This just makes Map.Copy() a bit cleaner
|
||||
// * since it doesn't have to type check
|
||||
if rvt, ok := t.(RVType); ok {
|
||||
return rvt.Copy()
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.keys); i++ {
|
||||
copied.keys[i] = m.keys[i].Copy().(K)
|
||||
copied.values[i] = m.values[i].Copy().(V)
|
||||
// TODO - Improve this, this isn't safe
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the Map. Requires type assertion when used
|
||||
func (m Map[K, V]) Copy() RVType {
|
||||
copied := make(Map[K, V])
|
||||
|
||||
for key, value := range m {
|
||||
copied[m.copyType(key).(K)] = value.Copy().(V)
|
||||
}
|
||||
|
||||
return copied
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (m *Map[K, V]) Equals(o RVType) bool {
|
||||
if _, ok := o.(*Map[K, V]); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
other := o.(*Map[K, V])
|
||||
|
||||
if len(m.keys) != len(other.keys) {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(m.values) != len(other.values) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.keys); i++ {
|
||||
if !m.keys[i].Equals(other.keys[i]) {
|
||||
return false
|
||||
func (m Map[K, V]) typesEqual(t1, t2 any) bool {
|
||||
// * This just makes Map.Equals() a bit cleaner
|
||||
// * since it doesn't have to type check
|
||||
if rvt1, ok := t1.(RVType); ok {
|
||||
if rvt2, ok := t2.(RVType); ok {
|
||||
return rvt1.Equals(rvt2)
|
||||
}
|
||||
}
|
||||
|
||||
if !m.values[i].Equals(other.values[i]) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (m Map[K, V]) Equals(o RVType) bool {
|
||||
if _, ok := o.(Map[K, V]); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
other := o.(Map[K, V])
|
||||
|
||||
if len(m) != len(other) {
|
||||
return false
|
||||
}
|
||||
|
||||
for key, value := range m {
|
||||
if otherValue, ok := other[key]; !ok || m.typesEqual(&value, &otherValue) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -106,70 +127,40 @@ func (m *Map[K, V]) Equals(o RVType) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// Set sets an element to the Map internal slices
|
||||
func (m *Map[K, V]) Set(key K, value V) {
|
||||
var index int = -1
|
||||
|
||||
for i := 0; i < len(m.keys); i++ {
|
||||
if m.keys[i].Equals(key) {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// * Replace the element if exists, otherwise push new
|
||||
if index != -1 {
|
||||
m.keys[index] = key
|
||||
m.values[index] = value
|
||||
} else {
|
||||
m.keys = append(m.keys, key)
|
||||
m.values = append(m.values, value)
|
||||
}
|
||||
// CopyRef copies the current value of the Map
|
||||
// and returns a pointer to the new copy
|
||||
func (m Map[K, V]) CopyRef() RVTypePtr {
|
||||
copied := m.Copy().(Map[K, V])
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Get returns an element from the Map. If not found, "ok" is false
|
||||
func (m *Map[K, V]) Get(key K) (V, bool) {
|
||||
var index int = -1
|
||||
|
||||
for i := 0; i < len(m.keys); i++ {
|
||||
if m.keys[i].Equals(key) {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if index != -1 {
|
||||
return m.values[index], true
|
||||
}
|
||||
|
||||
return m.ValueType.Copy().(V), false
|
||||
}
|
||||
|
||||
// Size returns the length of the Map
|
||||
func (m *Map[K, V]) Size() int {
|
||||
return len(m.keys)
|
||||
// Deref takes a pointer to the Map
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (m *Map[K, V]) Deref() RVType {
|
||||
return *m
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (m *Map[K, V]) String() string {
|
||||
func (m Map[K, V]) String() string {
|
||||
return m.FormatToString(0)
|
||||
}
|
||||
|
||||
// FormatToString pretty-prints the struct data using the provided indentation level
|
||||
func (m *Map[K, V]) FormatToString(indentationLevel int) string {
|
||||
func (m Map[K, V]) FormatToString(indentationLevel int) string {
|
||||
indentationValues := strings.Repeat("\t", indentationLevel+1)
|
||||
indentationEnd := strings.Repeat("\t", indentationLevel)
|
||||
|
||||
var b strings.Builder
|
||||
|
||||
if len(m.keys) == 0 {
|
||||
if len(m) == 0 {
|
||||
b.WriteString("{}\n")
|
||||
} else {
|
||||
b.WriteString("{\n")
|
||||
|
||||
for i := 0; i < len(m.keys); i++ {
|
||||
for key, value := range m {
|
||||
// TODO - Special handle the the last item to not add the comma on last item
|
||||
b.WriteString(fmt.Sprintf("%s%v: %v,\n", indentationValues, m.keys[i], m.values[i]))
|
||||
b.WriteString(fmt.Sprintf("%s%v: %v,\n", indentationValues, key, value))
|
||||
}
|
||||
|
||||
b.WriteString(fmt.Sprintf("%s}\n", indentationEnd))
|
||||
|
@ -179,9 +170,6 @@ func (m *Map[K, V]) FormatToString(indentationLevel int) string {
|
|||
}
|
||||
|
||||
// NewMap returns a new Map of the provided type
|
||||
func NewMap[K RVType, V RVType]() *Map[K, V] {
|
||||
return &Map[K, V]{
|
||||
keys: make([]K, 0),
|
||||
values: make([]V, 0),
|
||||
}
|
||||
func NewMap[K comparable, V RVType]() Map[K, V] {
|
||||
return make(Map[K, V])
|
||||
}
|
||||
|
|
60
types/pid.go
60
types/pid.go
|
@ -6,21 +6,17 @@ import (
|
|||
)
|
||||
|
||||
// PID represents a unique number to identify a user.
|
||||
// The official library treats this as a primitive integer.
|
||||
//
|
||||
// Type alias of uint64.
|
||||
// The true size of this value depends on the client version.
|
||||
// Legacy clients (WiiU/3DS) use a uint32, whereas modern clients (Nintendo Switch) use a uint64.
|
||||
// Value is always stored as the higher uint64, the consuming API should assert accordingly
|
||||
type PID struct {
|
||||
pid uint64
|
||||
}
|
||||
// Legacy clients (Wii U/3DS) use a uint32, whereas modern clients (Nintendo Switch) use a uint64.
|
||||
type PID uint64
|
||||
|
||||
// WriteTo writes the PID to the given writable
|
||||
func (p *PID) WriteTo(writable Writable) {
|
||||
func (p PID) WriteTo(writable Writable) {
|
||||
if writable.PIDSize() == 8 {
|
||||
writable.WritePrimitiveUInt64LE(p.pid)
|
||||
writable.WriteUInt64LE(uint64(p))
|
||||
} else {
|
||||
writable.WritePrimitiveUInt32LE(uint32(p.pid))
|
||||
writable.WriteUInt32LE(uint32(p))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,9 +26,9 @@ func (p *PID) ExtractFrom(readable Readable) error {
|
|||
var err error
|
||||
|
||||
if readable.PIDSize() == 8 {
|
||||
pid, err = readable.ReadPrimitiveUInt64LE()
|
||||
pid, err = readable.ReadUInt64LE()
|
||||
} else {
|
||||
p, e := readable.ReadPrimitiveUInt32LE()
|
||||
p, e := readable.ReadUInt32LE()
|
||||
|
||||
pid = uint64(p)
|
||||
err = e
|
||||
|
@ -42,55 +38,59 @@ func (p *PID) ExtractFrom(readable Readable) error {
|
|||
return err
|
||||
}
|
||||
|
||||
p.pid = pid
|
||||
|
||||
*p = PID(pid)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the PID. Requires type assertion when used
|
||||
func (p *PID) Copy() RVType {
|
||||
return NewPID(p.pid)
|
||||
func (p PID) Copy() RVType {
|
||||
return NewPID(uint64(p))
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (p *PID) Equals(o RVType) bool {
|
||||
if _, ok := o.(*PID); !ok {
|
||||
func (p PID) Equals(o RVType) bool {
|
||||
if _, ok := o.(PID); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return p.pid == o.(*PID).pid
|
||||
return p == o.(PID)
|
||||
}
|
||||
|
||||
// Value returns the numeric value of the PID as a uint64 regardless of client version
|
||||
func (p *PID) Value() uint64 {
|
||||
return p.pid
|
||||
// CopyRef copies the current value of the PID
|
||||
// and returns a pointer to the new copy
|
||||
func (p PID) CopyRef() RVTypePtr {
|
||||
copied := p.Copy().(PID)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// LegacyValue returns the numeric value of the PID as a uint32, for legacy clients
|
||||
func (p *PID) LegacyValue() uint32 {
|
||||
return uint32(p.pid)
|
||||
// Deref takes a pointer to the PID
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (p *PID) Deref() RVType {
|
||||
return *p
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (p *PID) String() string {
|
||||
func (p PID) String() string {
|
||||
return p.FormatToString(0)
|
||||
}
|
||||
|
||||
// FormatToString pretty-prints the struct data using the provided indentation level
|
||||
func (p *PID) FormatToString(indentationLevel int) string {
|
||||
func (p PID) FormatToString(indentationLevel int) string {
|
||||
indentationValues := strings.Repeat("\t", indentationLevel+1)
|
||||
indentationEnd := strings.Repeat("\t", indentationLevel)
|
||||
|
||||
var b strings.Builder
|
||||
|
||||
b.WriteString("PID{\n")
|
||||
b.WriteString(fmt.Sprintf("%spid: %d\n", indentationValues, p.pid))
|
||||
b.WriteString(fmt.Sprintf("%spid: %d\n", indentationValues, p))
|
||||
b.WriteString(fmt.Sprintf("%s}", indentationEnd))
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// NewPID returns a PID instance. The real size of PID depends on the client version
|
||||
func NewPID(pid uint64) *PID {
|
||||
return &PID{pid: pid}
|
||||
func NewPID(input uint64) PID {
|
||||
p := PID(input)
|
||||
return p
|
||||
}
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// PrimitiveBool is wrapper around a Go primitive bool with receiver methods to conform to RVType
|
||||
type PrimitiveBool struct {
|
||||
Value bool
|
||||
}
|
||||
|
||||
// WriteTo writes the bool to the given writable
|
||||
func (b *PrimitiveBool) WriteTo(writable Writable) {
|
||||
writable.WritePrimitiveBool(b.Value)
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the bool from the given readable
|
||||
func (b *PrimitiveBool) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadPrimitiveBool()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.Value = value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the PrimitiveBool. Requires type assertion when used
|
||||
func (b *PrimitiveBool) Copy() RVType {
|
||||
return NewPrimitiveBool(b.Value)
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (b *PrimitiveBool) Equals(o RVType) bool {
|
||||
if _, ok := o.(*PrimitiveBool); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return b.Value == o.(*PrimitiveBool).Value
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (b *PrimitiveBool) String() string {
|
||||
return fmt.Sprintf("%t", b.Value)
|
||||
}
|
||||
|
||||
// NewPrimitiveBool returns a new PrimitiveBool
|
||||
func NewPrimitiveBool(boolean bool) *PrimitiveBool {
|
||||
return &PrimitiveBool{Value: boolean}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// PrimitiveF32 is wrapper around a Go primitive float32 with receiver methods to conform to RVType
|
||||
type PrimitiveF32 struct {
|
||||
Value float32
|
||||
}
|
||||
|
||||
// WriteTo writes the float32 to the given writable
|
||||
func (f32 *PrimitiveF32) WriteTo(writable Writable) {
|
||||
writable.WritePrimitiveFloat32LE(f32.Value)
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the float32 from the given readable
|
||||
func (f32 *PrimitiveF32) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadPrimitiveFloat32LE()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f32.Value = value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the float32. Requires type assertion when used
|
||||
func (f32 *PrimitiveF32) Copy() RVType {
|
||||
return NewPrimitiveF32(f32.Value)
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (f32 *PrimitiveF32) Equals(o RVType) bool {
|
||||
if _, ok := o.(*PrimitiveF32); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return f32.Value == o.(*PrimitiveF32).Value
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (f32 *PrimitiveF32) String() string {
|
||||
return fmt.Sprintf("%f", f32.Value)
|
||||
}
|
||||
|
||||
// NewPrimitiveF32 returns a new PrimitiveF32
|
||||
func NewPrimitiveF32(float float32) *PrimitiveF32 {
|
||||
return &PrimitiveF32{Value: float}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// PrimitiveF64 is wrapper around a Go primitive float64 with receiver methods to conform to RVType
|
||||
type PrimitiveF64 struct {
|
||||
Value float64
|
||||
}
|
||||
|
||||
// WriteTo writes the float64 to the given writable
|
||||
func (f64 *PrimitiveF64) WriteTo(writable Writable) {
|
||||
writable.WritePrimitiveFloat64LE(f64.Value)
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the float64 from the given readable
|
||||
func (f64 *PrimitiveF64) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadPrimitiveFloat64LE()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f64.Value = value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the float64. Requires type assertion when used
|
||||
func (f64 *PrimitiveF64) Copy() RVType {
|
||||
return NewPrimitiveF64(f64.Value)
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (f64 *PrimitiveF64) Equals(o RVType) bool {
|
||||
if _, ok := o.(*PrimitiveF64); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return f64.Value == o.(*PrimitiveF64).Value
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (f64 *PrimitiveF64) String() string {
|
||||
return fmt.Sprintf("%f", f64.Value)
|
||||
}
|
||||
|
||||
// NewPrimitiveF64 returns a new PrimitiveF64
|
||||
func NewPrimitiveF64(float float64) *PrimitiveF64 {
|
||||
return &PrimitiveF64{Value: float}
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// PrimitiveS16 is wrapper around a Go primitive int16 with receiver methods to conform to RVType
|
||||
type PrimitiveS16 struct {
|
||||
Value int16
|
||||
}
|
||||
|
||||
// WriteTo writes the int16 to the given writable
|
||||
func (s16 *PrimitiveS16) WriteTo(writable Writable) {
|
||||
writable.WritePrimitiveInt16LE(s16.Value)
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the int16 from the given readable
|
||||
func (s16 *PrimitiveS16) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadPrimitiveInt16LE()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s16.Value = value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the int16. Requires type assertion when used
|
||||
func (s16 *PrimitiveS16) Copy() RVType {
|
||||
return NewPrimitiveS16(s16.Value)
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (s16 *PrimitiveS16) Equals(o RVType) bool {
|
||||
if _, ok := o.(*PrimitiveS16); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return s16.Value == o.(*PrimitiveS16).Value
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (s16 *PrimitiveS16) String() string {
|
||||
return fmt.Sprintf("%d", s16.Value)
|
||||
}
|
||||
|
||||
// AND runs a bitwise AND operation on the PrimitiveS16 value. Consumes and returns a NEX primitive
|
||||
func (s16 *PrimitiveS16) AND(other *PrimitiveS16) *PrimitiveS16 {
|
||||
return NewPrimitiveS16(s16.PAND(other.Value))
|
||||
}
|
||||
|
||||
// PAND (Primitive AND) runs a bitwise AND operation on the PrimitiveS16 value. Consumes and returns a Go primitive
|
||||
func (s16 *PrimitiveS16) PAND(value int16) int16 {
|
||||
return s16.Value & value
|
||||
}
|
||||
|
||||
// OR runs a bitwise OR operation on the PrimitiveS16 value. Consumes and returns a NEX primitive
|
||||
func (s16 *PrimitiveS16) OR(other *PrimitiveS16) *PrimitiveS16 {
|
||||
return NewPrimitiveS16(s16.POR(other.Value))
|
||||
}
|
||||
|
||||
// POR (Primitive OR) runs a bitwise OR operation on the PrimitiveS16 value. Consumes and returns a Go primitive
|
||||
func (s16 *PrimitiveS16) POR(value int16) int16 {
|
||||
return s16.Value | value
|
||||
}
|
||||
|
||||
// XOR runs a bitwise XOR operation on the PrimitiveS16 value. Consumes and returns a NEX primitive
|
||||
func (s16 *PrimitiveS16) XOR(other *PrimitiveS16) *PrimitiveS16 {
|
||||
return NewPrimitiveS16(s16.PXOR(other.Value))
|
||||
}
|
||||
|
||||
// PXOR (Primitive XOR) runs a bitwise XOR operation on the PrimitiveS16 value. Consumes and returns a Go primitive
|
||||
func (s16 *PrimitiveS16) PXOR(value int16) int16 {
|
||||
return s16.Value ^ value
|
||||
}
|
||||
|
||||
// NOT runs a bitwise NOT operation on the PrimitiveS16 value. Returns a NEX primitive
|
||||
func (s16 *PrimitiveS16) NOT() *PrimitiveS16 {
|
||||
return NewPrimitiveS16(s16.PNOT())
|
||||
}
|
||||
|
||||
// PNOT (Primitive NOT) runs a bitwise NOT operation on the PrimitiveS16 value. Returns a Go primitive
|
||||
func (s16 *PrimitiveS16) PNOT() int16 {
|
||||
return ^s16.Value
|
||||
}
|
||||
|
||||
// ANDNOT runs a bitwise ANDNOT operation on the PrimitiveS16 value. Consumes and returns a NEX primitive
|
||||
func (s16 *PrimitiveS16) ANDNOT(other *PrimitiveS16) *PrimitiveS16 {
|
||||
return NewPrimitiveS16(s16.PANDNOT(other.Value))
|
||||
}
|
||||
|
||||
// PANDNOT (Primitive AND-NOT) runs a bitwise AND-NOT operation on the PrimitiveS16 value. Consumes and returns a Go primitive
|
||||
func (s16 *PrimitiveS16) PANDNOT(value int16) int16 {
|
||||
return s16.Value &^ value
|
||||
}
|
||||
|
||||
// LShift runs a left shift operation on the PrimitiveS16 value. Consumes and returns a NEX primitive
|
||||
func (s16 *PrimitiveS16) LShift(other *PrimitiveS16) *PrimitiveS16 {
|
||||
return NewPrimitiveS16(s16.PLShift(other.Value))
|
||||
}
|
||||
|
||||
// PLShift (Primitive Left Shift) runs a left shift operation on the PrimitiveS16 value. Consumes and returns a Go primitive
|
||||
func (s16 *PrimitiveS16) PLShift(value int16) int16 {
|
||||
return s16.Value << value
|
||||
}
|
||||
|
||||
// RShift runs a right shift operation on the PrimitiveS16 value. Consumes and returns a NEX primitive
|
||||
func (s16 *PrimitiveS16) RShift(other *PrimitiveS16) *PrimitiveS16 {
|
||||
return NewPrimitiveS16(s16.PRShift(other.Value))
|
||||
}
|
||||
|
||||
// PRShift (Primitive Right Shift) runs a right shift operation on the PrimitiveS16 value. Consumes and returns a Go primitive
|
||||
func (s16 *PrimitiveS16) PRShift(value int16) int16 {
|
||||
return s16.Value >> value
|
||||
}
|
||||
|
||||
// NewPrimitiveS16 returns a new PrimitiveS16
|
||||
func NewPrimitiveS16(i16 int16) *PrimitiveS16 {
|
||||
return &PrimitiveS16{Value: i16}
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// PrimitiveS32 is wrapper around a Go primitive int32 with receiver methods to conform to RVType
|
||||
type PrimitiveS32 struct {
|
||||
Value int32
|
||||
}
|
||||
|
||||
// WriteTo writes the int32 to the given writable
|
||||
func (s32 *PrimitiveS32) WriteTo(writable Writable) {
|
||||
writable.WritePrimitiveInt32LE(s32.Value)
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the int32 from the given readable
|
||||
func (s32 *PrimitiveS32) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadPrimitiveInt32LE()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s32.Value = value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the int32. Requires type assertion when used
|
||||
func (s32 *PrimitiveS32) Copy() RVType {
|
||||
return NewPrimitiveS32(s32.Value)
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (s32 *PrimitiveS32) Equals(o RVType) bool {
|
||||
if _, ok := o.(*PrimitiveS32); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return s32.Value == o.(*PrimitiveS32).Value
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (s32 *PrimitiveS32) String() string {
|
||||
return fmt.Sprintf("%d", s32.Value)
|
||||
}
|
||||
|
||||
// AND runs a bitwise AND operation on the PrimitiveS32 value. Consumes and returns a NEX primitive
|
||||
func (s32 *PrimitiveS32) AND(other *PrimitiveS32) *PrimitiveS32 {
|
||||
return NewPrimitiveS32(s32.PAND(other.Value))
|
||||
}
|
||||
|
||||
// PAND (Primitive AND) runs a bitwise AND operation on the PrimitiveS32 value. Consumes and returns a Go primitive
|
||||
func (s32 *PrimitiveS32) PAND(value int32) int32 {
|
||||
return s32.Value & value
|
||||
}
|
||||
|
||||
// OR runs a bitwise OR operation on the PrimitiveS32 value. Consumes and returns a NEX primitive
|
||||
func (s32 *PrimitiveS32) OR(other *PrimitiveS32) *PrimitiveS32 {
|
||||
return NewPrimitiveS32(s32.POR(other.Value))
|
||||
}
|
||||
|
||||
// POR (Primitive OR) runs a bitwise OR operation on the PrimitiveS32 value. Consumes and returns a Go primitive
|
||||
func (s32 *PrimitiveS32) POR(value int32) int32 {
|
||||
return s32.Value | value
|
||||
}
|
||||
|
||||
// XOR runs a bitwise XOR operation on the PrimitiveS32 value. Consumes and returns a NEX primitive
|
||||
func (s32 *PrimitiveS32) XOR(other *PrimitiveS32) *PrimitiveS32 {
|
||||
return NewPrimitiveS32(s32.PXOR(other.Value))
|
||||
}
|
||||
|
||||
// PXOR (Primitive XOR) runs a bitwise XOR operation on the PrimitiveS32 value. Consumes and returns a Go primitive
|
||||
func (s32 *PrimitiveS32) PXOR(value int32) int32 {
|
||||
return s32.Value ^ value
|
||||
}
|
||||
|
||||
// NOT runs a bitwise NOT operation on the PrimitiveS32 value. Returns a NEX primitive
|
||||
func (s32 *PrimitiveS32) NOT() *PrimitiveS32 {
|
||||
return NewPrimitiveS32(s32.PNOT())
|
||||
}
|
||||
|
||||
// PNOT (Primitive NOT) runs a bitwise NOT operation on the PrimitiveS32 value. Returns a Go primitive
|
||||
func (s32 *PrimitiveS32) PNOT() int32 {
|
||||
return ^s32.Value
|
||||
}
|
||||
|
||||
// ANDNOT runs a bitwise ANDNOT operation on the PrimitiveS32 value. Consumes and returns a NEX primitive
|
||||
func (s32 *PrimitiveS32) ANDNOT(other *PrimitiveS32) *PrimitiveS32 {
|
||||
return NewPrimitiveS32(s32.PANDNOT(other.Value))
|
||||
}
|
||||
|
||||
// PANDNOT (Primitive AND-NOT) runs a bitwise AND-NOT operation on the PrimitiveS32 value. Consumes and returns a Go primitive
|
||||
func (s32 *PrimitiveS32) PANDNOT(value int32) int32 {
|
||||
return s32.Value &^ value
|
||||
}
|
||||
|
||||
// LShift runs a left shift operation on the PrimitiveS32 value. Consumes and returns a NEX primitive
|
||||
func (s32 *PrimitiveS32) LShift(other *PrimitiveS32) *PrimitiveS32 {
|
||||
return NewPrimitiveS32(s32.PLShift(other.Value))
|
||||
}
|
||||
|
||||
// PLShift (Primitive Left Shift) runs a left shift operation on the PrimitiveS32 value. Consumes and returns a Go primitive
|
||||
func (s32 *PrimitiveS32) PLShift(value int32) int32 {
|
||||
return s32.Value << value
|
||||
}
|
||||
|
||||
// RShift runs a right shift operation on the PrimitiveS32 value. Consumes and returns a NEX primitive
|
||||
func (s32 *PrimitiveS32) RShift(other *PrimitiveS32) *PrimitiveS32 {
|
||||
return NewPrimitiveS32(s32.PRShift(other.Value))
|
||||
}
|
||||
|
||||
// PRShift (Primitive Right Shift) runs a right shift operation on the PrimitiveS32 value. Consumes and returns a Go primitive
|
||||
func (s32 *PrimitiveS32) PRShift(value int32) int32 {
|
||||
return s32.Value >> value
|
||||
}
|
||||
|
||||
// NewPrimitiveS32 returns a new PrimitiveS32
|
||||
func NewPrimitiveS32(i32 int32) *PrimitiveS32 {
|
||||
return &PrimitiveS32{Value: i32}
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// PrimitiveS64 is wrapper around a Go primitive int64 with receiver methods to conform to RVType
|
||||
type PrimitiveS64 struct {
|
||||
Value int64
|
||||
}
|
||||
|
||||
// WriteTo writes the int64 to the given writable
|
||||
func (s64 *PrimitiveS64) WriteTo(writable Writable) {
|
||||
writable.WritePrimitiveInt64LE(s64.Value)
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the int64 from the given readable
|
||||
func (s64 *PrimitiveS64) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadPrimitiveInt64LE()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s64.Value = value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the int64. Requires type assertion when used
|
||||
func (s64 *PrimitiveS64) Copy() RVType {
|
||||
return NewPrimitiveS64(s64.Value)
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (s64 *PrimitiveS64) Equals(o RVType) bool {
|
||||
if _, ok := o.(*PrimitiveS64); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return s64.Value == o.(*PrimitiveS64).Value
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (s64 *PrimitiveS64) String() string {
|
||||
return fmt.Sprintf("%d", s64.Value)
|
||||
}
|
||||
|
||||
// AND runs a bitwise AND operation on the PrimitiveS64 value. Consumes and returns a NEX primitive
|
||||
func (s64 *PrimitiveS64) AND(other *PrimitiveS64) *PrimitiveS64 {
|
||||
return NewPrimitiveS64(s64.PAND(other.Value))
|
||||
}
|
||||
|
||||
// PAND (Primitive AND) runs a bitwise AND operation on the PrimitiveS64 value. Consumes and returns a Go primitive
|
||||
func (s64 *PrimitiveS64) PAND(value int64) int64 {
|
||||
return s64.Value & value
|
||||
}
|
||||
|
||||
// OR runs a bitwise OR operation on the PrimitiveS64 value. Consumes and returns a NEX primitive
|
||||
func (s64 *PrimitiveS64) OR(other *PrimitiveS64) *PrimitiveS64 {
|
||||
return NewPrimitiveS64(s64.POR(other.Value))
|
||||
}
|
||||
|
||||
// POR (Primitive OR) runs a bitwise OR operation on the PrimitiveS64 value. Consumes and returns a Go primitive
|
||||
func (s64 *PrimitiveS64) POR(value int64) int64 {
|
||||
return s64.Value | value
|
||||
}
|
||||
|
||||
// XOR runs a bitwise XOR operation on the PrimitiveS64 value. Consumes and returns a NEX primitive
|
||||
func (s64 *PrimitiveS64) XOR(other *PrimitiveS64) *PrimitiveS64 {
|
||||
return NewPrimitiveS64(s64.PXOR(other.Value))
|
||||
}
|
||||
|
||||
// PXOR (Primitive XOR) runs a bitwise XOR operation on the PrimitiveS64 value. Consumes and returns a Go primitive
|
||||
func (s64 *PrimitiveS64) PXOR(value int64) int64 {
|
||||
return s64.Value ^ value
|
||||
}
|
||||
|
||||
// NOT runs a bitwise NOT operation on the PrimitiveS64 value. Returns a NEX primitive
|
||||
func (s64 *PrimitiveS64) NOT() *PrimitiveS64 {
|
||||
return NewPrimitiveS64(s64.PNOT())
|
||||
}
|
||||
|
||||
// PNOT (Primitive NOT) runs a bitwise NOT operation on the PrimitiveS64 value. Returns a Go primitive
|
||||
func (s64 *PrimitiveS64) PNOT() int64 {
|
||||
return ^s64.Value
|
||||
}
|
||||
|
||||
// ANDNOT runs a bitwise ANDNOT operation on the PrimitiveS64 value. Consumes and returns a NEX primitive
|
||||
func (s64 *PrimitiveS64) ANDNOT(other *PrimitiveS64) *PrimitiveS64 {
|
||||
return NewPrimitiveS64(s64.PANDNOT(other.Value))
|
||||
}
|
||||
|
||||
// PANDNOT (Primitive AND-NOT) runs a bitwise AND-NOT operation on the PrimitiveS64 value. Consumes and returns a Go primitive
|
||||
func (s64 *PrimitiveS64) PANDNOT(value int64) int64 {
|
||||
return s64.Value &^ value
|
||||
}
|
||||
|
||||
// LShift runs a left shift operation on the PrimitiveS64 value. Consumes and returns a NEX primitive
|
||||
func (s64 *PrimitiveS64) LShift(other *PrimitiveS64) *PrimitiveS64 {
|
||||
return NewPrimitiveS64(s64.PLShift(other.Value))
|
||||
}
|
||||
|
||||
// PLShift (Primitive Left Shift) runs a left shift operation on the PrimitiveS64 value. Consumes and returns a Go primitive
|
||||
func (s64 *PrimitiveS64) PLShift(value int64) int64 {
|
||||
return s64.Value << value
|
||||
}
|
||||
|
||||
// RShift runs a right shift operation on the PrimitiveS64 value. Consumes and returns a NEX primitive
|
||||
func (s64 *PrimitiveS64) RShift(other *PrimitiveS64) *PrimitiveS64 {
|
||||
return NewPrimitiveS64(s64.PRShift(other.Value))
|
||||
}
|
||||
|
||||
// PRShift (Primitive Right Shift) runs a right shift operation on the PrimitiveS64 value. Consumes and returns a Go primitive
|
||||
func (s64 *PrimitiveS64) PRShift(value int64) int64 {
|
||||
return s64.Value >> value
|
||||
}
|
||||
|
||||
// NewPrimitiveS64 returns a new PrimitiveS64
|
||||
func NewPrimitiveS64(i64 int64) *PrimitiveS64 {
|
||||
return &PrimitiveS64{Value: i64}
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// PrimitiveS8 is wrapper around a Go primitive int8 with receiver methods to conform to RVType
|
||||
type PrimitiveS8 struct {
|
||||
Value int8
|
||||
}
|
||||
|
||||
// WriteTo writes the int8 to the given writable
|
||||
func (s8 *PrimitiveS8) WriteTo(writable Writable) {
|
||||
writable.WritePrimitiveInt8(s8.Value)
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the int8 from the given readable
|
||||
func (s8 *PrimitiveS8) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadPrimitiveInt8()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s8.Value = value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the int8. Requires type assertion when used
|
||||
func (s8 *PrimitiveS8) Copy() RVType {
|
||||
return NewPrimitiveS8(s8.Value)
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (s8 *PrimitiveS8) Equals(o RVType) bool {
|
||||
if _, ok := o.(*PrimitiveS8); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return s8.Value == o.(*PrimitiveS8).Value
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (s8 *PrimitiveS8) String() string {
|
||||
return fmt.Sprintf("%d", s8.Value)
|
||||
}
|
||||
|
||||
// AND runs a bitwise AND operation on the PrimitiveS8 value. Consumes and returns a NEX primitive
|
||||
func (s8 *PrimitiveS8) AND(other *PrimitiveS8) *PrimitiveS8 {
|
||||
return NewPrimitiveS8(s8.PAND(other.Value))
|
||||
}
|
||||
|
||||
// PAND (Primitive AND) runs a bitwise AND operation on the PrimitiveS8 value. Consumes and returns a Go primitive
|
||||
func (s8 *PrimitiveS8) PAND(value int8) int8 {
|
||||
return s8.Value & value
|
||||
}
|
||||
|
||||
// OR runs a bitwise OR operation on the PrimitiveS8 value. Consumes and returns a NEX primitive
|
||||
func (s8 *PrimitiveS8) OR(other *PrimitiveS8) *PrimitiveS8 {
|
||||
return NewPrimitiveS8(s8.POR(other.Value))
|
||||
}
|
||||
|
||||
// POR (Primitive OR) runs a bitwise OR operation on the PrimitiveS8 value. Consumes and returns a Go primitive
|
||||
func (s8 *PrimitiveS8) POR(value int8) int8 {
|
||||
return s8.Value | value
|
||||
}
|
||||
|
||||
// XOR runs a bitwise XOR operation on the PrimitiveS8 value. Consumes and returns a NEX primitive
|
||||
func (s8 *PrimitiveS8) XOR(other *PrimitiveS8) *PrimitiveS8 {
|
||||
return NewPrimitiveS8(s8.PXOR(other.Value))
|
||||
}
|
||||
|
||||
// PXOR (Primitive XOR) runs a bitwise XOR operation on the PrimitiveS8 value. Consumes and returns a Go primitive
|
||||
func (s8 *PrimitiveS8) PXOR(value int8) int8 {
|
||||
return s8.Value ^ value
|
||||
}
|
||||
|
||||
// NOT runs a bitwise NOT operation on the PrimitiveS8 value. Returns a NEX primitive
|
||||
func (s8 *PrimitiveS8) NOT() *PrimitiveS8 {
|
||||
return NewPrimitiveS8(s8.PNOT())
|
||||
}
|
||||
|
||||
// PNOT (Primitive NOT) runs a bitwise NOT operation on the PrimitiveS8 value. Returns a Go primitive
|
||||
func (s8 *PrimitiveS8) PNOT() int8 {
|
||||
return ^s8.Value
|
||||
}
|
||||
|
||||
// ANDNOT runs a bitwise ANDNOT operation on the PrimitiveS8 value. Consumes and returns a NEX primitive
|
||||
func (s8 *PrimitiveS8) ANDNOT(other *PrimitiveS8) *PrimitiveS8 {
|
||||
return NewPrimitiveS8(s8.PANDNOT(other.Value))
|
||||
}
|
||||
|
||||
// PANDNOT (Primitive AND-NOT) runs a bitwise AND-NOT operation on the PrimitiveS8 value. Consumes and returns a Go primitive
|
||||
func (s8 *PrimitiveS8) PANDNOT(value int8) int8 {
|
||||
return s8.Value &^ value
|
||||
}
|
||||
|
||||
// LShift runs a left shift operation on the PrimitiveS8 value. Consumes and returns a NEX primitive
|
||||
func (s8 *PrimitiveS8) LShift(other *PrimitiveS8) *PrimitiveS8 {
|
||||
return NewPrimitiveS8(s8.PLShift(other.Value))
|
||||
}
|
||||
|
||||
// PLShift (Primitive Left Shift) runs a left shift operation on the PrimitiveS8 value. Consumes and returns a Go primitive
|
||||
func (s8 *PrimitiveS8) PLShift(value int8) int8 {
|
||||
return s8.Value << value
|
||||
}
|
||||
|
||||
// RShift runs a right shift operation on the PrimitiveS8 value. Consumes and returns a NEX primitive
|
||||
func (s8 *PrimitiveS8) RShift(other *PrimitiveS8) *PrimitiveS8 {
|
||||
return NewPrimitiveS8(s8.PRShift(other.Value))
|
||||
}
|
||||
|
||||
// PRShift (Primitive Right Shift) runs a right shift operation on the PrimitiveS8 value. Consumes and returns a Go primitive
|
||||
func (s8 *PrimitiveS8) PRShift(value int8) int8 {
|
||||
return s8.Value >> value
|
||||
}
|
||||
|
||||
// NewPrimitiveS8 returns a new PrimitiveS8
|
||||
func NewPrimitiveS8(i8 int8) *PrimitiveS8 {
|
||||
return &PrimitiveS8{Value: i8}
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// PrimitiveU16 is wrapper around a Go primitive uint16 with receiver methods to conform to RVType
|
||||
type PrimitiveU16 struct {
|
||||
Value uint16
|
||||
}
|
||||
|
||||
// WriteTo writes the uint16 to the given writable
|
||||
func (u16 *PrimitiveU16) WriteTo(writable Writable) {
|
||||
writable.WritePrimitiveUInt16LE(u16.Value)
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the uint16 from the given readable
|
||||
func (u16 *PrimitiveU16) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadPrimitiveUInt16LE()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u16.Value = value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the uint16. Requires type assertion when used
|
||||
func (u16 *PrimitiveU16) Copy() RVType {
|
||||
return NewPrimitiveU16(u16.Value)
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (u16 *PrimitiveU16) Equals(o RVType) bool {
|
||||
if _, ok := o.(*PrimitiveU16); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return u16.Value == o.(*PrimitiveU16).Value
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (u16 *PrimitiveU16) String() string {
|
||||
return fmt.Sprintf("%d", u16.Value)
|
||||
}
|
||||
|
||||
// AND runs a bitwise AND operation on the PrimitiveU16 value. Consumes and returns a NEX primitive
|
||||
func (u16 *PrimitiveU16) AND(other *PrimitiveU16) *PrimitiveU16 {
|
||||
return NewPrimitiveU16(u16.PAND(other.Value))
|
||||
}
|
||||
|
||||
// PAND (Primitive AND) runs a bitwise AND operation on the PrimitiveU16 value. Consumes and returns a Go primitive
|
||||
func (u16 *PrimitiveU16) PAND(value uint16) uint16 {
|
||||
return u16.Value & value
|
||||
}
|
||||
|
||||
// OR runs a bitwise OR operation on the PrimitiveU16 value. Consumes and returns a NEX primitive
|
||||
func (u16 *PrimitiveU16) OR(other *PrimitiveU16) *PrimitiveU16 {
|
||||
return NewPrimitiveU16(u16.POR(other.Value))
|
||||
}
|
||||
|
||||
// POR (Primitive OR) runs a bitwise OR operation on the PrimitiveU16 value. Consumes and returns a Go primitive
|
||||
func (u16 *PrimitiveU16) POR(value uint16) uint16 {
|
||||
return u16.Value | value
|
||||
}
|
||||
|
||||
// XOR runs a bitwise XOR operation on the PrimitiveU16 value. Consumes and returns a NEX primitive
|
||||
func (u16 *PrimitiveU16) XOR(other *PrimitiveU16) *PrimitiveU16 {
|
||||
return NewPrimitiveU16(u16.PXOR(other.Value))
|
||||
}
|
||||
|
||||
// PXOR (Primitive XOR) runs a bitwise XOR operation on the PrimitiveU16 value. Consumes and returns a Go primitive
|
||||
func (u16 *PrimitiveU16) PXOR(value uint16) uint16 {
|
||||
return u16.Value ^ value
|
||||
}
|
||||
|
||||
// NOT runs a bitwise NOT operation on the PrimitiveU16 value. Returns a NEX primitive
|
||||
func (u16 *PrimitiveU16) NOT() *PrimitiveU16 {
|
||||
return NewPrimitiveU16(u16.PNOT())
|
||||
}
|
||||
|
||||
// PNOT (Primitive NOT) runs a bitwise NOT operation on the PrimitiveU16 value. Returns a Go primitive
|
||||
func (u16 *PrimitiveU16) PNOT() uint16 {
|
||||
return ^u16.Value
|
||||
}
|
||||
|
||||
// ANDNOT runs a bitwise ANDNOT operation on the PrimitiveU16 value. Consumes and returns a NEX primitive
|
||||
func (u16 *PrimitiveU16) ANDNOT(other *PrimitiveU16) *PrimitiveU16 {
|
||||
return NewPrimitiveU16(u16.PANDNOT(other.Value))
|
||||
}
|
||||
|
||||
// PANDNOT (Primitive AND-NOT) runs a bitwise AND-NOT operation on the PrimitiveU16 value. Consumes and returns a Go primitive
|
||||
func (u16 *PrimitiveU16) PANDNOT(value uint16) uint16 {
|
||||
return u16.Value &^ value
|
||||
}
|
||||
|
||||
// LShift runs a left shift operation on the PrimitiveU16 value. Consumes and returns a NEX primitive
|
||||
func (u16 *PrimitiveU16) LShift(other *PrimitiveU16) *PrimitiveU16 {
|
||||
return NewPrimitiveU16(u16.PLShift(other.Value))
|
||||
}
|
||||
|
||||
// PLShift (Primitive Left Shift) runs a left shift operation on the PrimitiveU16 value. Consumes and returns a Go primitive
|
||||
func (u16 *PrimitiveU16) PLShift(value uint16) uint16 {
|
||||
return u16.Value << value
|
||||
}
|
||||
|
||||
// RShift runs a right shift operation on the PrimitiveU16 value. Consumes and returns a NEX primitive
|
||||
func (u16 *PrimitiveU16) RShift(other *PrimitiveU16) *PrimitiveU16 {
|
||||
return NewPrimitiveU16(u16.PRShift(other.Value))
|
||||
}
|
||||
|
||||
// PRShift (Primitive Right Shift) runs a right shift operation on the PrimitiveU16 value. Consumes and returns a Go primitive
|
||||
func (u16 *PrimitiveU16) PRShift(value uint16) uint16 {
|
||||
return u16.Value >> value
|
||||
}
|
||||
|
||||
// NewPrimitiveU16 returns a new PrimitiveU16
|
||||
func NewPrimitiveU16(ui16 uint16) *PrimitiveU16 {
|
||||
return &PrimitiveU16{Value: ui16}
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// PrimitiveU32 is wrapper around a Go primitive uint32 with receiver methods to conform to RVType
|
||||
type PrimitiveU32 struct {
|
||||
Value uint32
|
||||
}
|
||||
|
||||
// WriteTo writes the uint32 to the given writable
|
||||
func (u32 *PrimitiveU32) WriteTo(writable Writable) {
|
||||
writable.WritePrimitiveUInt32LE(u32.Value)
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the uint32 from the given readable
|
||||
func (u32 *PrimitiveU32) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadPrimitiveUInt32LE()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u32.Value = value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the uint32. Requires type assertion when used
|
||||
func (u32 *PrimitiveU32) Copy() RVType {
|
||||
return NewPrimitiveU32(u32.Value)
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (u32 *PrimitiveU32) Equals(o RVType) bool {
|
||||
if _, ok := o.(*PrimitiveU32); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return u32.Value == o.(*PrimitiveU32).Value
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (u32 *PrimitiveU32) String() string {
|
||||
return fmt.Sprintf("%d", u32.Value)
|
||||
}
|
||||
|
||||
// AND runs a bitwise AND operation on the PrimitiveU32 value. Consumes and returns a NEX primitive
|
||||
func (u32 *PrimitiveU32) AND(other *PrimitiveU32) *PrimitiveU32 {
|
||||
return NewPrimitiveU32(u32.PAND(other.Value))
|
||||
}
|
||||
|
||||
// PAND (Primitive AND) runs a bitwise AND operation on the PrimitiveU32 value. Consumes and returns a Go primitive
|
||||
func (u32 *PrimitiveU32) PAND(value uint32) uint32 {
|
||||
return u32.Value & value
|
||||
}
|
||||
|
||||
// OR runs a bitwise OR operation on the PrimitiveU32 value. Consumes and returns a NEX primitive
|
||||
func (u32 *PrimitiveU32) OR(other *PrimitiveU32) *PrimitiveU32 {
|
||||
return NewPrimitiveU32(u32.POR(other.Value))
|
||||
}
|
||||
|
||||
// POR (Primitive OR) runs a bitwise OR operation on the PrimitiveU32 value. Consumes and returns a Go primitive
|
||||
func (u32 *PrimitiveU32) POR(value uint32) uint32 {
|
||||
return u32.Value | value
|
||||
}
|
||||
|
||||
// XOR runs a bitwise XOR operation on the PrimitiveU32 value. Consumes and returns a NEX primitive
|
||||
func (u32 *PrimitiveU32) XOR(other *PrimitiveU32) *PrimitiveU32 {
|
||||
return NewPrimitiveU32(u32.PXOR(other.Value))
|
||||
}
|
||||
|
||||
// PXOR (Primitive XOR) runs a bitwise XOR operation on the PrimitiveU32 value. Consumes and returns a Go primitive
|
||||
func (u32 *PrimitiveU32) PXOR(value uint32) uint32 {
|
||||
return u32.Value ^ value
|
||||
}
|
||||
|
||||
// NOT runs a bitwise NOT operation on the PrimitiveU32 value. Returns a NEX primitive
|
||||
func (u32 *PrimitiveU32) NOT() *PrimitiveU32 {
|
||||
return NewPrimitiveU32(u32.PNOT())
|
||||
}
|
||||
|
||||
// PNOT (Primitive NOT) runs a bitwise NOT operation on the PrimitiveU32 value. Returns a Go primitive
|
||||
func (u32 *PrimitiveU32) PNOT() uint32 {
|
||||
return ^u32.Value
|
||||
}
|
||||
|
||||
// ANDNOT runs a bitwise ANDNOT operation on the PrimitiveU32 value. Consumes and returns a NEX primitive
|
||||
func (u32 *PrimitiveU32) ANDNOT(other *PrimitiveU32) *PrimitiveU32 {
|
||||
return NewPrimitiveU32(u32.PANDNOT(other.Value))
|
||||
}
|
||||
|
||||
// PANDNOT (Primitive AND-NOT) runs a bitwise AND-NOT operation on the PrimitiveU32 value. Consumes and returns a Go primitive
|
||||
func (u32 *PrimitiveU32) PANDNOT(value uint32) uint32 {
|
||||
return u32.Value &^ value
|
||||
}
|
||||
|
||||
// LShift runs a left shift operation on the PrimitiveU32 value. Consumes and returns a NEX primitive
|
||||
func (u32 *PrimitiveU32) LShift(other *PrimitiveU32) *PrimitiveU32 {
|
||||
return NewPrimitiveU32(u32.PLShift(other.Value))
|
||||
}
|
||||
|
||||
// PLShift (Primitive Left Shift) runs a left shift operation on the PrimitiveU32 value. Consumes and returns a Go primitive
|
||||
func (u32 *PrimitiveU32) PLShift(value uint32) uint32 {
|
||||
return u32.Value << value
|
||||
}
|
||||
|
||||
// RShift runs a right shift operation on the PrimitiveU32 value. Consumes and returns a NEX primitive
|
||||
func (u32 *PrimitiveU32) RShift(other *PrimitiveU32) *PrimitiveU32 {
|
||||
return NewPrimitiveU32(u32.PRShift(other.Value))
|
||||
}
|
||||
|
||||
// PRShift (Primitive Right Shift) runs a right shift operation on the PrimitiveU32 value. Consumes and returns a Go primitive
|
||||
func (u32 *PrimitiveU32) PRShift(value uint32) uint32 {
|
||||
return u32.Value >> value
|
||||
}
|
||||
|
||||
// NewPrimitiveU32 returns a new PrimitiveU32
|
||||
func NewPrimitiveU32(ui32 uint32) *PrimitiveU32 {
|
||||
return &PrimitiveU32{Value: ui32}
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// PrimitiveU64 is wrapper around a Go primitive uint64 with receiver methods to conform to RVType
|
||||
type PrimitiveU64 struct {
|
||||
Value uint64
|
||||
}
|
||||
|
||||
// WriteTo writes the uint64 to the given writable
|
||||
func (u64 *PrimitiveU64) WriteTo(writable Writable) {
|
||||
writable.WritePrimitiveUInt64LE(u64.Value)
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the uint64 from the given readable
|
||||
func (u64 *PrimitiveU64) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadPrimitiveUInt64LE()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u64.Value = value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the uint64. Requires type assertion when used
|
||||
func (u64 *PrimitiveU64) Copy() RVType {
|
||||
return NewPrimitiveU64(u64.Value)
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (u64 *PrimitiveU64) Equals(o RVType) bool {
|
||||
if _, ok := o.(*PrimitiveU64); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return u64.Value == o.(*PrimitiveU64).Value
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (u64 *PrimitiveU64) String() string {
|
||||
return fmt.Sprintf("%d", u64.Value)
|
||||
}
|
||||
|
||||
// AND runs a bitwise AND operation on the PrimitiveU64 value. Consumes and returns a NEX primitive
|
||||
func (u64 *PrimitiveU64) AND(other *PrimitiveU64) *PrimitiveU64 {
|
||||
return NewPrimitiveU64(u64.PAND(other.Value))
|
||||
}
|
||||
|
||||
// PAND (Primitive AND) runs a bitwise AND operation on the PrimitiveU64 value. Consumes and returns a Go primitive
|
||||
func (u64 *PrimitiveU64) PAND(value uint64) uint64 {
|
||||
return u64.Value & value
|
||||
}
|
||||
|
||||
// OR runs a bitwise OR operation on the PrimitiveU64 value. Consumes and returns a NEX primitive
|
||||
func (u64 *PrimitiveU64) OR(other *PrimitiveU64) *PrimitiveU64 {
|
||||
return NewPrimitiveU64(u64.POR(other.Value))
|
||||
}
|
||||
|
||||
// POR (Primitive OR) runs a bitwise OR operation on the PrimitiveU64 value. Consumes and returns a Go primitive
|
||||
func (u64 *PrimitiveU64) POR(value uint64) uint64 {
|
||||
return u64.Value | value
|
||||
}
|
||||
|
||||
// XOR runs a bitwise XOR operation on the PrimitiveU64 value. Consumes and returns a NEX primitive
|
||||
func (u64 *PrimitiveU64) XOR(other *PrimitiveU64) *PrimitiveU64 {
|
||||
return NewPrimitiveU64(u64.PXOR(other.Value))
|
||||
}
|
||||
|
||||
// PXOR (Primitive XOR) runs a bitwise XOR operation on the PrimitiveU64 value. Consumes and returns a Go primitive
|
||||
func (u64 *PrimitiveU64) PXOR(value uint64) uint64 {
|
||||
return u64.Value ^ value
|
||||
}
|
||||
|
||||
// NOT runs a bitwise NOT operation on the PrimitiveU64 value. Returns a NEX primitive
|
||||
func (u64 *PrimitiveU64) NOT() *PrimitiveU64 {
|
||||
return NewPrimitiveU64(u64.PNOT())
|
||||
}
|
||||
|
||||
// PNOT (Primitive NOT) runs a bitwise NOT operation on the PrimitiveU64 value. Returns a Go primitive
|
||||
func (u64 *PrimitiveU64) PNOT() uint64 {
|
||||
return ^u64.Value
|
||||
}
|
||||
|
||||
// ANDNOT runs a bitwise ANDNOT operation on the PrimitiveU64 value. Consumes and returns a NEX primitive
|
||||
func (u64 *PrimitiveU64) ANDNOT(other *PrimitiveU64) *PrimitiveU64 {
|
||||
return NewPrimitiveU64(u64.PANDNOT(other.Value))
|
||||
}
|
||||
|
||||
// PANDNOT (Primitive AND-NOT) runs a bitwise AND-NOT operation on the PrimitiveU64 value. Consumes and returns a Go primitive
|
||||
func (u64 *PrimitiveU64) PANDNOT(value uint64) uint64 {
|
||||
return u64.Value &^ value
|
||||
}
|
||||
|
||||
// LShift runs a left shift operation on the PrimitiveU64 value. Consumes and returns a NEX primitive
|
||||
func (u64 *PrimitiveU64) LShift(other *PrimitiveU64) *PrimitiveU64 {
|
||||
return NewPrimitiveU64(u64.PLShift(other.Value))
|
||||
}
|
||||
|
||||
// PLShift (Primitive Left Shift) runs a left shift operation on the PrimitiveU64 value. Consumes and returns a Go primitive
|
||||
func (u64 *PrimitiveU64) PLShift(value uint64) uint64 {
|
||||
return u64.Value << value
|
||||
}
|
||||
|
||||
// RShift runs a right shift operation on the PrimitiveU64 value. Consumes and returns a NEX primitive
|
||||
func (u64 *PrimitiveU64) RShift(other *PrimitiveU64) *PrimitiveU64 {
|
||||
return NewPrimitiveU64(u64.PRShift(other.Value))
|
||||
}
|
||||
|
||||
// PRShift (Primitive Right Shift) runs a right shift operation on the PrimitiveU64 value. Consumes and returns a Go primitive
|
||||
func (u64 *PrimitiveU64) PRShift(value uint64) uint64 {
|
||||
return u64.Value >> value
|
||||
}
|
||||
|
||||
// NewPrimitiveU64 returns a new PrimitiveU64
|
||||
func NewPrimitiveU64(ui64 uint64) *PrimitiveU64 {
|
||||
return &PrimitiveU64{Value: ui64}
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// PrimitiveU8 is wrapper around a Go primitive uint8 with receiver methods to conform to RVType
|
||||
type PrimitiveU8 struct {
|
||||
Value uint8
|
||||
}
|
||||
|
||||
// WriteTo writes the uint8 to the given writable
|
||||
func (u8 *PrimitiveU8) WriteTo(writable Writable) {
|
||||
writable.WritePrimitiveUInt8(u8.Value)
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the uint8 from the given readable
|
||||
func (u8 *PrimitiveU8) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadPrimitiveUInt8()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u8.Value = value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the uint8. Requires type assertion when used
|
||||
func (u8 *PrimitiveU8) Copy() RVType {
|
||||
return NewPrimitiveU8(u8.Value)
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (u8 *PrimitiveU8) Equals(o RVType) bool {
|
||||
if _, ok := o.(*PrimitiveU8); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return u8.Value == o.(*PrimitiveU8).Value
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (u8 *PrimitiveU8) String() string {
|
||||
return fmt.Sprintf("%d", u8.Value)
|
||||
}
|
||||
|
||||
// AND runs a bitwise AND operation on the PrimitiveU8 value. Consumes and returns a NEX primitive
|
||||
func (u8 *PrimitiveU8) AND(other *PrimitiveU8) *PrimitiveU8 {
|
||||
return NewPrimitiveU8(u8.PAND(other.Value))
|
||||
}
|
||||
|
||||
// PAND (Primitive AND) runs a bitwise AND operation on the PrimitiveU8 value. Consumes and returns a Go primitive
|
||||
func (u8 *PrimitiveU8) PAND(value uint8) uint8 {
|
||||
return u8.Value & value
|
||||
}
|
||||
|
||||
// OR runs a bitwise OR operation on the PrimitiveU8 value. Consumes and returns a NEX primitive
|
||||
func (u8 *PrimitiveU8) OR(other *PrimitiveU8) *PrimitiveU8 {
|
||||
return NewPrimitiveU8(u8.POR(other.Value))
|
||||
}
|
||||
|
||||
// POR (Primitive OR) runs a bitwise OR operation on the PrimitiveU8 value. Consumes and returns a Go primitive
|
||||
func (u8 *PrimitiveU8) POR(value uint8) uint8 {
|
||||
return u8.Value | value
|
||||
}
|
||||
|
||||
// XOR runs a bitwise XOR operation on the PrimitiveU8 value. Consumes and returns a NEX primitive
|
||||
func (u8 *PrimitiveU8) XOR(other *PrimitiveU8) *PrimitiveU8 {
|
||||
return NewPrimitiveU8(u8.PXOR(other.Value))
|
||||
}
|
||||
|
||||
// PXOR (Primitive XOR) runs a bitwise XOR operation on the PrimitiveU8 value. Consumes and returns a Go primitive
|
||||
func (u8 *PrimitiveU8) PXOR(value uint8) uint8 {
|
||||
return u8.Value ^ value
|
||||
}
|
||||
|
||||
// NOT runs a bitwise NOT operation on the PrimitiveU8 value. Returns a NEX primitive
|
||||
func (u8 *PrimitiveU8) NOT() *PrimitiveU8 {
|
||||
return NewPrimitiveU8(u8.PNOT())
|
||||
}
|
||||
|
||||
// PNOT (Primitive NOT) runs a bitwise NOT operation on the PrimitiveU8 value. Returns a Go primitive
|
||||
func (u8 *PrimitiveU8) PNOT() uint8 {
|
||||
return ^u8.Value
|
||||
}
|
||||
|
||||
// ANDNOT runs a bitwise ANDNOT operation on the PrimitiveU8 value. Consumes and returns a NEX primitive
|
||||
func (u8 *PrimitiveU8) ANDNOT(other *PrimitiveU8) *PrimitiveU8 {
|
||||
return NewPrimitiveU8(u8.PANDNOT(other.Value))
|
||||
}
|
||||
|
||||
// PANDNOT (Primitive AND-NOT) runs a bitwise AND-NOT operation on the PrimitiveU8 value. Consumes and returns a Go primitive
|
||||
func (u8 *PrimitiveU8) PANDNOT(value uint8) uint8 {
|
||||
return u8.Value &^ value
|
||||
}
|
||||
|
||||
// LShift runs a left shift operation on the PrimitiveU8 value. Consumes and returns a NEX primitive
|
||||
func (u8 *PrimitiveU8) LShift(other *PrimitiveU8) *PrimitiveU8 {
|
||||
return NewPrimitiveU8(u8.PLShift(other.Value))
|
||||
}
|
||||
|
||||
// PLShift (Primitive Left Shift) runs a left shift operation on the PrimitiveU8 value. Consumes and returns a Go primitive
|
||||
func (u8 *PrimitiveU8) PLShift(value uint8) uint8 {
|
||||
return u8.Value << value
|
||||
}
|
||||
|
||||
// RShift runs a right shift operation on the PrimitiveU8 value. Consumes and returns a NEX primitive
|
||||
func (u8 *PrimitiveU8) RShift(other *PrimitiveU8) *PrimitiveU8 {
|
||||
return NewPrimitiveU8(u8.PRShift(other.Value))
|
||||
}
|
||||
|
||||
// PRShift (Primitive Right Shift) runs a right shift operation on the PrimitiveU8 value. Consumes and returns a Go primitive
|
||||
func (u8 *PrimitiveU8) PRShift(value uint8) uint8 {
|
||||
return u8.Value >> value
|
||||
}
|
||||
|
||||
// NewPrimitiveU8 returns a new PrimitiveU8
|
||||
func NewPrimitiveU8(ui8 uint8) *PrimitiveU8 {
|
||||
return &PrimitiveU8{Value: ui8}
|
||||
}
|
|
@ -2,30 +2,29 @@ package types
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// QBuffer is an implementation of rdv::qBuffer.
|
||||
// Wraps a primitive Go byte slice.
|
||||
// Type alias of []byte.
|
||||
// Same as Buffer but with a uint16 length field.
|
||||
type QBuffer struct {
|
||||
Value []byte
|
||||
}
|
||||
type QBuffer []byte
|
||||
|
||||
// WriteTo writes the []byte to the given writable
|
||||
func (qb *QBuffer) WriteTo(writable Writable) {
|
||||
length := len(qb.Value)
|
||||
func (qb QBuffer) WriteTo(writable Writable) {
|
||||
length := len(qb)
|
||||
|
||||
writable.WritePrimitiveUInt16LE(uint16(length))
|
||||
writable.WriteUInt16LE(uint16(length))
|
||||
|
||||
if length > 0 {
|
||||
writable.Write(qb.Value)
|
||||
writable.Write(qb)
|
||||
}
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the QBuffer from the given readable
|
||||
func (qb *QBuffer) ExtractFrom(readable Readable) error {
|
||||
length, err := readable.ReadPrimitiveUInt16LE()
|
||||
length, err := readable.ReadUInt16LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read NEX qBuffer length. %s", err.Error())
|
||||
}
|
||||
|
@ -35,31 +34,47 @@ func (qb *QBuffer) ExtractFrom(readable Readable) error {
|
|||
return fmt.Errorf("Failed to read NEX qBuffer data. %s", err.Error())
|
||||
}
|
||||
|
||||
qb.Value = data
|
||||
|
||||
*qb = data
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the qBuffer. Requires type assertion when used
|
||||
func (qb *QBuffer) Copy() RVType {
|
||||
return NewQBuffer(qb.Value)
|
||||
func (qb QBuffer) Copy() RVType {
|
||||
return NewQBuffer(qb)
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (qb *QBuffer) Equals(o RVType) bool {
|
||||
if _, ok := o.(*QBuffer); !ok {
|
||||
func (qb QBuffer) Equals(o RVType) bool {
|
||||
if _, ok := o.(QBuffer); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return bytes.Equal(qb.Value, o.(*QBuffer).Value)
|
||||
return bytes.Equal(qb, o.(QBuffer))
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the QBuffer
|
||||
// and returns a pointer to the new copy
|
||||
func (qb QBuffer) CopyRef() RVTypePtr {
|
||||
copied := qb.Copy().(QBuffer)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the QBuffer
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (qb *QBuffer) Deref() RVType {
|
||||
return *qb
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (qb *QBuffer) String() string {
|
||||
return fmt.Sprintf("%x", qb.Value)
|
||||
func (qb QBuffer) String() string {
|
||||
return hex.EncodeToString(qb)
|
||||
}
|
||||
|
||||
// NewQBuffer returns a new QBuffer
|
||||
func NewQBuffer(data []byte) *QBuffer {
|
||||
return &QBuffer{Value: data}
|
||||
func NewQBuffer(input []byte) QBuffer {
|
||||
qb := make(QBuffer, len(input))
|
||||
copy(qb, input)
|
||||
|
||||
return qb
|
||||
}
|
||||
|
|
|
@ -8,60 +8,72 @@ import (
|
|||
var errorMask = 1 << 31
|
||||
|
||||
// QResult is an implementation of rdv::qResult.
|
||||
// Type alias of uint32.
|
||||
// Determines the result of an operation.
|
||||
// If the MSB is set the result is an error, otherwise success
|
||||
type QResult struct {
|
||||
Code uint32
|
||||
}
|
||||
type QResult uint32
|
||||
|
||||
// WriteTo writes the QResult to the given writable
|
||||
func (r *QResult) WriteTo(writable Writable) {
|
||||
writable.WritePrimitiveUInt32LE(r.Code)
|
||||
func (r QResult) WriteTo(writable Writable) {
|
||||
writable.WriteUInt32LE(uint32(r))
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the QResult from the given readable
|
||||
func (r *QResult) ExtractFrom(readable Readable) error {
|
||||
code, err := readable.ReadPrimitiveUInt32LE()
|
||||
code, err := readable.ReadUInt32LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read QResult code. %s", err.Error())
|
||||
}
|
||||
|
||||
r.Code = code
|
||||
|
||||
*r = QResult(code)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the QResult. Requires type assertion when used
|
||||
func (r *QResult) Copy() RVType {
|
||||
return NewQResult(r.Code)
|
||||
func (r QResult) Copy() RVType {
|
||||
return NewQResult(uint32(r))
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (r *QResult) Equals(o RVType) bool {
|
||||
if _, ok := o.(*QResult); !ok {
|
||||
func (r QResult) Equals(o RVType) bool {
|
||||
if _, ok := o.(QResult); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return r.Code == o.(*QResult).Code
|
||||
return r == o.(QResult)
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the QResult
|
||||
// and returns a pointer to the new copy
|
||||
func (r QResult) CopyRef() RVTypePtr {
|
||||
copied := r.Copy().(QResult)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the QResult
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (r *QResult) Deref() RVType {
|
||||
return *r
|
||||
}
|
||||
|
||||
// IsSuccess returns true if the QResult is a success
|
||||
func (r *QResult) IsSuccess() bool {
|
||||
return int(r.Code)&errorMask == 0
|
||||
func (r QResult) IsSuccess() bool {
|
||||
return int(r)&errorMask == 0
|
||||
}
|
||||
|
||||
// IsError returns true if the QResult is a error
|
||||
func (r *QResult) IsError() bool {
|
||||
return int(r.Code)&errorMask != 0
|
||||
func (r QResult) IsError() bool {
|
||||
return int(r)&errorMask != 0
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (r *QResult) String() string {
|
||||
func (r QResult) String() string {
|
||||
return r.FormatToString(0)
|
||||
}
|
||||
|
||||
// FormatToString pretty-prints the struct data using the provided indentation level
|
||||
func (r *QResult) FormatToString(indentationLevel int) string {
|
||||
func (r QResult) FormatToString(indentationLevel int) string {
|
||||
indentationValues := strings.Repeat("\t", indentationLevel+1)
|
||||
indentationEnd := strings.Repeat("\t", indentationLevel)
|
||||
|
||||
|
@ -70,9 +82,9 @@ func (r *QResult) FormatToString(indentationLevel int) string {
|
|||
b.WriteString("QResult{\n")
|
||||
|
||||
if r.IsSuccess() {
|
||||
b.WriteString(fmt.Sprintf("%scode: %d (success)\n", indentationValues, r.Code))
|
||||
b.WriteString(fmt.Sprintf("%scode: %d (success)\n", indentationValues, r))
|
||||
} else {
|
||||
b.WriteString(fmt.Sprintf("%scode: %d (error)\n", indentationValues, r.Code))
|
||||
b.WriteString(fmt.Sprintf("%scode: %d (error)\n", indentationValues, r))
|
||||
}
|
||||
|
||||
b.WriteString(fmt.Sprintf("%s}", indentationEnd))
|
||||
|
@ -81,16 +93,17 @@ func (r *QResult) FormatToString(indentationLevel int) string {
|
|||
}
|
||||
|
||||
// NewQResult returns a new QResult
|
||||
func NewQResult(code uint32) *QResult {
|
||||
return &QResult{code}
|
||||
func NewQResult(input uint32) QResult {
|
||||
r := QResult(input)
|
||||
return r
|
||||
}
|
||||
|
||||
// NewQResultSuccess returns a new QResult set as a success
|
||||
func NewQResultSuccess(code uint32) *QResult {
|
||||
func NewQResultSuccess(code uint32) QResult {
|
||||
return NewQResult(uint32(int(code) & ^errorMask))
|
||||
}
|
||||
|
||||
// NewQResultError returns a new QResult set as an error
|
||||
func NewQResultError(code uint32) *QResult {
|
||||
func NewQResultError(code uint32) QResult {
|
||||
return NewQResult(uint32(int(code) | errorMask))
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -9,54 +10,60 @@ import (
|
|||
)
|
||||
|
||||
// QUUID is an implementation of rdv::qUUID.
|
||||
// Type alias of []byte.
|
||||
// Encodes a UUID in little-endian byte order.
|
||||
type QUUID struct {
|
||||
Data []byte
|
||||
}
|
||||
type QUUID []byte
|
||||
|
||||
// WriteTo writes the QUUID to the given writable
|
||||
func (qu *QUUID) WriteTo(writable Writable) {
|
||||
writable.Write(qu.Data)
|
||||
func (qu QUUID) WriteTo(writable Writable) {
|
||||
writable.Write(qu)
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the QUUID from the given readable
|
||||
func (qu *QUUID) ExtractFrom(readable Readable) error {
|
||||
if readable.Remaining() < uint64(16) {
|
||||
if readable.Remaining() < 16 {
|
||||
return errors.New("Not enough data left to read qUUID")
|
||||
}
|
||||
|
||||
qu.Data, _ = readable.Read(16)
|
||||
|
||||
*qu, _ = readable.Read(16)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a new copied instance of qUUID
|
||||
func (qu *QUUID) Copy() RVType {
|
||||
copied := NewQUUID()
|
||||
|
||||
copied.Data = make([]byte, len(qu.Data))
|
||||
|
||||
copy(copied.Data, qu.Data)
|
||||
|
||||
return copied
|
||||
func (qu QUUID) Copy() RVType {
|
||||
return NewQUUID(qu)
|
||||
}
|
||||
|
||||
// Equals checks if the passed Structure contains the same data as the current instance
|
||||
func (qu *QUUID) Equals(o RVType) bool {
|
||||
if _, ok := o.(*QUUID); !ok {
|
||||
func (qu QUUID) Equals(o RVType) bool {
|
||||
if _, ok := o.(QUUID); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return qu.GetStringValue() == (o.(*QUUID)).GetStringValue()
|
||||
return bytes.Equal(qu, o.(QUUID))
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the QUUID
|
||||
// and returns a pointer to the new copy
|
||||
func (qu QUUID) CopyRef() RVTypePtr {
|
||||
copied := qu.Copy().(QUUID)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the QUUID
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (qu *QUUID) Deref() RVType {
|
||||
return *qu
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (qu *QUUID) String() string {
|
||||
func (qu QUUID) String() string {
|
||||
return qu.FormatToString(0)
|
||||
}
|
||||
|
||||
// FormatToString pretty-prints the struct data using the provided indentation level
|
||||
func (qu *QUUID) FormatToString(indentationLevel int) string {
|
||||
func (qu QUUID) FormatToString(indentationLevel int) string {
|
||||
indentationValues := strings.Repeat("\t", indentationLevel+1)
|
||||
indentationEnd := strings.Repeat("\t", indentationLevel)
|
||||
|
||||
|
@ -70,10 +77,10 @@ func (qu *QUUID) FormatToString(indentationLevel int) string {
|
|||
}
|
||||
|
||||
// GetStringValue returns the UUID encoded in the qUUID
|
||||
func (qu *QUUID) GetStringValue() string {
|
||||
func (qu QUUID) GetStringValue() string {
|
||||
// * Create copy of the data since slices.Reverse modifies the slice in-line
|
||||
data := make([]byte, len(qu.Data))
|
||||
copy(data, qu.Data)
|
||||
data := make([]byte, len(qu))
|
||||
copy(data, qu)
|
||||
|
||||
if len(data) != 16 {
|
||||
// * Default dummy UUID as found in WATCH_DOGS
|
||||
|
@ -165,16 +172,15 @@ func (qu *QUUID) FromString(uuid string) error {
|
|||
slices.Reverse(data[12:14])
|
||||
slices.Reverse(data[14:16])
|
||||
|
||||
qu.Data = make([]byte, 0, 16)
|
||||
|
||||
copy(qu.Data, data)
|
||||
*qu = data
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewQUUID returns a new qUUID
|
||||
func NewQUUID() *QUUID {
|
||||
return &QUUID{
|
||||
Data: make([]byte, 0, 16),
|
||||
}
|
||||
func NewQUUID(input []byte) QUUID {
|
||||
qu := make(QUUID, len(input))
|
||||
copy(qu, input)
|
||||
|
||||
return qu
|
||||
}
|
||||
|
|
|
@ -2,21 +2,21 @@ package types
|
|||
|
||||
// Readable represents a struct that types can read from
|
||||
type Readable interface {
|
||||
StringLengthSize() int // Returns the size of the length field for rdv::String types. Only 2 and 4 are valid
|
||||
PIDSize() int // Returns the size of the length fields for nn::nex::PID types. Only 4 and 8 are valid
|
||||
UseStructureHeader() bool // Returns whether or not Structure types should use a header
|
||||
Remaining() uint64 // Returns the number of bytes left unread in the buffer
|
||||
ReadRemaining() []byte // Reads the remaining data from the buffer
|
||||
Read(length uint64) ([]byte, error) // Reads up to length bytes of data from the buffer. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadPrimitiveUInt8() (uint8, error) // Reads a primitive Go uint8. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadPrimitiveUInt16LE() (uint16, error) // Reads a primitive Go uint16. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadPrimitiveUInt32LE() (uint32, error) // Reads a primitive Go uint32. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadPrimitiveUInt64LE() (uint64, error) // Reads a primitive Go uint64. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadPrimitiveInt8() (int8, error) // Reads a primitive Go int8. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadPrimitiveInt16LE() (int16, error) // Reads a primitive Go int16. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadPrimitiveInt32LE() (int32, error) // Reads a primitive Go int32. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadPrimitiveInt64LE() (int64, error) // Reads a primitive Go int64. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadPrimitiveFloat32LE() (float32, error) // Reads a primitive Go float32. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadPrimitiveFloat64LE() (float64, error) // Reads a primitive Go float64. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadPrimitiveBool() (bool, error) // Reads a primitive Go bool. Returns an error if the read failed, such as if there was not enough data to read
|
||||
StringLengthSize() int // Returns the size of the length field for rdv::String types. Only 2 and 4 are valid
|
||||
PIDSize() int // Returns the size of the length fields for nn::nex::PID types. Only 4 and 8 are valid
|
||||
UseStructureHeader() bool // Returns whether or not Structure types should use a header
|
||||
Remaining() uint64 // Returns the number of bytes left unread in the buffer
|
||||
ReadRemaining() []byte // Reads the remaining data from the buffer
|
||||
Read(length uint64) ([]byte, error) // Reads up to length bytes of data from the buffer. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadUInt8() (uint8, error) // Reads a primitive Go uint8. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadUInt16LE() (uint16, error) // Reads a primitive Go uint16. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadUInt32LE() (uint32, error) // Reads a primitive Go uint32. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadUInt64LE() (uint64, error) // Reads a primitive Go uint64. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadInt8() (int8, error) // Reads a primitive Go int8. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadInt16LE() (int16, error) // Reads a primitive Go int16. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadInt32LE() (int32, error) // Reads a primitive Go int32. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadInt64LE() (int64, error) // Reads a primitive Go int64. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadFloat32LE() (float32, error) // Reads a primitive Go float32. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadFloat64LE() (float64, error) // Reads a primitive Go float64. Returns an error if the read failed, such as if there was not enough data to read
|
||||
ReadBool() (bool, error) // Reads a primitive Go bool. Returns an error if the read failed, such as if there was not enough data to read
|
||||
}
|
||||
|
|
|
@ -9,12 +9,12 @@ import (
|
|||
// Holds information about how to make queries which may return large data.
|
||||
type ResultRange struct {
|
||||
Structure
|
||||
Offset *PrimitiveU32 // Offset into the dataset
|
||||
Length *PrimitiveU32 // Number of items to return
|
||||
Offset UInt32 `json:"offset" db:"offset" bson:"offset" xml:"Offset"` // * Offset into the dataset
|
||||
Length UInt32 `json:"length" db:"length" bson:"length" xml:"Length"` // * Number of items to return
|
||||
}
|
||||
|
||||
// WriteTo writes the ResultRange to the given writable
|
||||
func (rr *ResultRange) WriteTo(writable Writable) {
|
||||
func (rr ResultRange) WriteTo(writable Writable) {
|
||||
contentWritable := writable.CopyNew()
|
||||
|
||||
rr.Offset.WriteTo(contentWritable)
|
||||
|
@ -49,42 +49,56 @@ func (rr *ResultRange) ExtractFrom(readable Readable) error {
|
|||
}
|
||||
|
||||
// Copy returns a new copied instance of ResultRange
|
||||
func (rr *ResultRange) Copy() RVType {
|
||||
func (rr ResultRange) Copy() RVType {
|
||||
copied := NewResultRange()
|
||||
|
||||
copied.StructureVersion = rr.StructureVersion
|
||||
copied.Offset = rr.Offset.Copy().(*PrimitiveU32)
|
||||
copied.Length = rr.Length.Copy().(*PrimitiveU32)
|
||||
copied.Offset = rr.Offset.Copy().(UInt32)
|
||||
copied.Length = rr.Length.Copy().(UInt32)
|
||||
|
||||
return copied
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (rr *ResultRange) Equals(o RVType) bool {
|
||||
if _, ok := o.(*ResultRange); !ok {
|
||||
func (rr ResultRange) Equals(o RVType) bool {
|
||||
if _, ok := o.(ResultRange); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
other := o.(*ResultRange)
|
||||
other := o.(ResultRange)
|
||||
|
||||
if rr.StructureVersion != other.StructureVersion {
|
||||
return false
|
||||
}
|
||||
|
||||
if !rr.Offset.Equals(other.Offset) {
|
||||
if !rr.Offset.Equals(&other.Offset) {
|
||||
return false
|
||||
}
|
||||
|
||||
return rr.Length.Equals(other.Length)
|
||||
return rr.Length.Equals(&other.Length)
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the ResultRange
|
||||
// and returns a pointer to the new copy
|
||||
func (rr ResultRange) CopyRef() RVTypePtr {
|
||||
copied := rr.Copy().(ResultRange)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the ResultRange
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (rr *ResultRange) Deref() RVType {
|
||||
return *rr
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (rr *ResultRange) String() string {
|
||||
func (rr ResultRange) String() string {
|
||||
return rr.FormatToString(0)
|
||||
}
|
||||
|
||||
// FormatToString pretty-prints the struct data using the provided indentation level
|
||||
func (rr *ResultRange) FormatToString(indentationLevel int) string {
|
||||
func (rr ResultRange) FormatToString(indentationLevel int) string {
|
||||
indentationValues := strings.Repeat("\t", indentationLevel+1)
|
||||
indentationEnd := strings.Repeat("\t", indentationLevel)
|
||||
|
||||
|
@ -100,9 +114,9 @@ func (rr *ResultRange) FormatToString(indentationLevel int) string {
|
|||
}
|
||||
|
||||
// NewResultRange returns a new ResultRange
|
||||
func NewResultRange() *ResultRange {
|
||||
return &ResultRange{
|
||||
Offset: NewPrimitiveU32(0),
|
||||
Length: NewPrimitiveU32(0),
|
||||
func NewResultRange() ResultRange {
|
||||
return ResultRange{
|
||||
Offset: NewUInt32(0),
|
||||
Length: NewUInt32(0),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,14 +9,14 @@ import (
|
|||
// Contains the locations and data of Rendez-Vous connection.
|
||||
type RVConnectionData struct {
|
||||
Structure
|
||||
StationURL *StationURL
|
||||
SpecialProtocols *List[*PrimitiveU8]
|
||||
StationURLSpecialProtocols *StationURL
|
||||
Time *DateTime
|
||||
StationURL StationURL `json:"station_url" db:"station_url" bson:"station_url" xml:"StationURL"`
|
||||
SpecialProtocols List[UInt8] `json:"special_protocols" db:"special_protocols" bson:"special_protocols" xml:"SpecialProtocols"`
|
||||
StationURLSpecialProtocols StationURL `json:"station_url_special_protocols" db:"station_url_special_protocols" bson:"station_url_special_protocols" xml:"StationURLSpecialProtocols"`
|
||||
Time DateTime `json:"time" db:"time" bson:"time" xml:"Time"`
|
||||
}
|
||||
|
||||
// WriteTo writes the RVConnectionData to the given writable
|
||||
func (rvcd *RVConnectionData) WriteTo(writable Writable) {
|
||||
func (rvcd RVConnectionData) WriteTo(writable Writable) {
|
||||
contentWritable := writable.CopyNew()
|
||||
|
||||
rvcd.StationURL.WriteTo(contentWritable)
|
||||
|
@ -67,28 +67,28 @@ func (rvcd *RVConnectionData) ExtractFrom(readable Readable) error {
|
|||
}
|
||||
|
||||
// Copy returns a new copied instance of RVConnectionData
|
||||
func (rvcd *RVConnectionData) Copy() RVType {
|
||||
func (rvcd RVConnectionData) Copy() RVType {
|
||||
copied := NewRVConnectionData()
|
||||
|
||||
copied.StructureVersion = rvcd.StructureVersion
|
||||
copied.StationURL = rvcd.StationURL.Copy().(*StationURL)
|
||||
copied.SpecialProtocols = rvcd.SpecialProtocols.Copy().(*List[*PrimitiveU8])
|
||||
copied.StationURLSpecialProtocols = rvcd.StationURLSpecialProtocols.Copy().(*StationURL)
|
||||
copied.StationURL = rvcd.StationURL.Copy().(StationURL)
|
||||
copied.SpecialProtocols = rvcd.SpecialProtocols.Copy().(List[UInt8])
|
||||
copied.StationURLSpecialProtocols = rvcd.StationURLSpecialProtocols.Copy().(StationURL)
|
||||
|
||||
if rvcd.StructureVersion >= 1 {
|
||||
copied.Time = rvcd.Time.Copy().(*DateTime)
|
||||
copied.Time = *rvcd.Time.Copy().(*DateTime)
|
||||
}
|
||||
|
||||
return copied
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (rvcd *RVConnectionData) Equals(o RVType) bool {
|
||||
if _, ok := o.(*RVConnectionData); !ok {
|
||||
func (rvcd RVConnectionData) Equals(o RVType) bool {
|
||||
if _, ok := o.(RVConnectionData); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
other := o.(*RVConnectionData)
|
||||
other := o.(RVConnectionData)
|
||||
|
||||
if rvcd.StructureVersion != other.StructureVersion {
|
||||
return false
|
||||
|
@ -107,21 +107,33 @@ func (rvcd *RVConnectionData) Equals(o RVType) bool {
|
|||
}
|
||||
|
||||
if rvcd.StructureVersion >= 1 {
|
||||
if !rvcd.Time.Equals(other.Time) {
|
||||
return false
|
||||
}
|
||||
return rvcd.Time.Equals(other.Time)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the RVConnectionData
|
||||
// and returns a pointer to the new copy
|
||||
func (rvcd RVConnectionData) CopyRef() RVTypePtr {
|
||||
copied := rvcd.Copy().(RVConnectionData)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the RVConnectionData
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (rvcd *RVConnectionData) Deref() RVType {
|
||||
return *rvcd
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (rvcd *RVConnectionData) String() string {
|
||||
func (rvcd RVConnectionData) String() string {
|
||||
return rvcd.FormatToString(0)
|
||||
}
|
||||
|
||||
// FormatToString pretty-prints the struct data using the provided indentation level
|
||||
func (rvcd *RVConnectionData) FormatToString(indentationLevel int) string {
|
||||
func (rvcd RVConnectionData) FormatToString(indentationLevel int) string {
|
||||
indentationValues := strings.Repeat("\t", indentationLevel+1)
|
||||
indentationEnd := strings.Repeat("\t", indentationLevel)
|
||||
|
||||
|
@ -139,15 +151,13 @@ func (rvcd *RVConnectionData) FormatToString(indentationLevel int) string {
|
|||
}
|
||||
|
||||
// NewRVConnectionData returns a new RVConnectionData
|
||||
func NewRVConnectionData() *RVConnectionData {
|
||||
rvcd := &RVConnectionData{
|
||||
func NewRVConnectionData() RVConnectionData {
|
||||
rvcd := RVConnectionData{
|
||||
StationURL: NewStationURL(""),
|
||||
SpecialProtocols: NewList[*PrimitiveU8](),
|
||||
SpecialProtocols: NewList[UInt8](),
|
||||
StationURLSpecialProtocols: NewStationURL(""),
|
||||
Time: NewDateTime(0),
|
||||
}
|
||||
|
||||
rvcd.SpecialProtocols.Type = NewPrimitiveU8(0)
|
||||
|
||||
return rvcd
|
||||
}
|
||||
|
|
|
@ -4,8 +4,16 @@ package types
|
|||
// RVType represents a Quazal Rendez-Vous/NEX type.
|
||||
// This includes primitives and custom types.
|
||||
type RVType interface {
|
||||
WriteTo(writable Writable)
|
||||
ExtractFrom(readable Readable) error
|
||||
Copy() RVType
|
||||
Equals(other RVType) bool
|
||||
WriteTo(writable Writable) // Writes the type data to the given Writable stream
|
||||
Copy() RVType // Returns a non-pointer copy of the type data. Complex types are deeply copied
|
||||
CopyRef() RVTypePtr // Returns a pointer to a copy of the type data. Complex types are deeply copied. Useful for obtaining a pointer without reflection, though limited to copies
|
||||
Equals(other RVType) bool // Checks if the input type is strictly equal to the current type
|
||||
}
|
||||
|
||||
// RVTypePtr represents a pointer to an RVType.
|
||||
// Used to separate pointer receivers for easier type checking.
|
||||
type RVTypePtr interface {
|
||||
RVType
|
||||
ExtractFrom(readable Readable) error // Reads the type data to the given Readable stream
|
||||
Deref() RVType // Returns the raw type data from a pointer. Useful for ensuring you have raw data without reflection
|
||||
}
|
||||
|
|
|
@ -12,12 +12,24 @@ import (
|
|||
//
|
||||
// Contains location of a station to connect to, with data about how to connect.
|
||||
type StationURL struct {
|
||||
urlType constants.StationURLType
|
||||
flags uint8
|
||||
params map[string]string
|
||||
urlType constants.StationURLType
|
||||
url string
|
||||
flags uint8
|
||||
standardParams map[string]string
|
||||
customParams map[string]string
|
||||
}
|
||||
|
||||
func (s *StationURL) numberParamValue(name string, bits int) (uint64, bool) {
|
||||
func (s *StationURL) ensureFields() {
|
||||
if s.standardParams == nil {
|
||||
s.standardParams = make(map[string]string)
|
||||
}
|
||||
|
||||
if s.customParams == nil {
|
||||
s.customParams = make(map[string]string)
|
||||
}
|
||||
}
|
||||
|
||||
func (s StationURL) numberParamValue(name string, bits int) (uint64, bool) {
|
||||
valueString, ok := s.ParamValue(name)
|
||||
if !ok {
|
||||
return 0, false
|
||||
|
@ -31,7 +43,7 @@ func (s *StationURL) numberParamValue(name string, bits int) (uint64, bool) {
|
|||
return value, true
|
||||
}
|
||||
|
||||
func (s *StationURL) uint8ParamValue(name string) (uint8, bool) {
|
||||
func (s StationURL) uint8ParamValue(name string) (uint8, bool) {
|
||||
value, ok := s.numberParamValue(name, 8)
|
||||
if !ok {
|
||||
return 0, false
|
||||
|
@ -40,7 +52,7 @@ func (s *StationURL) uint8ParamValue(name string) (uint8, bool) {
|
|||
return uint8(value), true
|
||||
}
|
||||
|
||||
func (s *StationURL) uint16ParamValue(name string) (uint16, bool) {
|
||||
func (s StationURL) uint16ParamValue(name string) (uint16, bool) {
|
||||
value, ok := s.numberParamValue(name, 16)
|
||||
if !ok {
|
||||
return 0, false
|
||||
|
@ -49,7 +61,7 @@ func (s *StationURL) uint16ParamValue(name string) (uint16, bool) {
|
|||
return uint16(value), true
|
||||
}
|
||||
|
||||
func (s *StationURL) uint32ParamValue(name string) (uint32, bool) {
|
||||
func (s StationURL) uint32ParamValue(name string) (uint32, bool) {
|
||||
value, ok := s.numberParamValue(name, 32)
|
||||
if !ok {
|
||||
return 0, false
|
||||
|
@ -58,11 +70,11 @@ func (s *StationURL) uint32ParamValue(name string) (uint32, bool) {
|
|||
return uint32(value), true
|
||||
}
|
||||
|
||||
func (s *StationURL) uint64ParamValue(name string) (uint64, bool) {
|
||||
func (s StationURL) uint64ParamValue(name string) (uint64, bool) {
|
||||
return s.numberParamValue(name, 64)
|
||||
}
|
||||
|
||||
func (s *StationURL) boolParamValue(name string) bool {
|
||||
func (s StationURL) boolParamValue(name string) bool {
|
||||
valueString, ok := s.ParamValue(name)
|
||||
if !ok {
|
||||
return false
|
||||
|
@ -72,37 +84,40 @@ func (s *StationURL) boolParamValue(name string) bool {
|
|||
}
|
||||
|
||||
// WriteTo writes the StationURL to the given writable
|
||||
func (s *StationURL) WriteTo(writable Writable) {
|
||||
str := NewString(s.EncodeToString())
|
||||
func (s StationURL) WriteTo(writable Writable) {
|
||||
url := NewString(s.URL())
|
||||
|
||||
str.WriteTo(writable)
|
||||
url.WriteTo(writable)
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the StationURL from the given readable
|
||||
func (s *StationURL) ExtractFrom(readable Readable) error {
|
||||
str := NewString("")
|
||||
s.ensureFields()
|
||||
|
||||
if err := str.ExtractFrom(readable); err != nil {
|
||||
url := NewString("")
|
||||
|
||||
if err := url.ExtractFrom(readable); err != nil {
|
||||
return fmt.Errorf("Failed to read StationURL. %s", err.Error())
|
||||
}
|
||||
|
||||
s.FromString(str.Value)
|
||||
s.SetURL(string(url))
|
||||
s.Parse()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a new copied instance of StationURL
|
||||
func (s *StationURL) Copy() RVType {
|
||||
return NewStationURL(s.EncodeToString())
|
||||
func (s StationURL) Copy() RVType {
|
||||
return NewStationURL(String(s.URL()))
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (s *StationURL) Equals(o RVType) bool {
|
||||
if _, ok := o.(*StationURL); !ok {
|
||||
func (s StationURL) Equals(o RVType) bool {
|
||||
if _, ok := o.(StationURL); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
other := o.(*StationURL)
|
||||
other := o.(StationURL)
|
||||
|
||||
if s.urlType != other.urlType {
|
||||
return false
|
||||
|
@ -112,12 +127,12 @@ func (s *StationURL) Equals(o RVType) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
if len(s.params) != len(other.params) {
|
||||
if len(s.standardParams) != len(other.standardParams) {
|
||||
return false
|
||||
}
|
||||
|
||||
for key, value1 := range s.params {
|
||||
value2, ok := other.params[key]
|
||||
for key, value1 := range s.standardParams {
|
||||
value2, ok := other.standardParams[key]
|
||||
if !ok || value1 != value2 {
|
||||
return false
|
||||
}
|
||||
|
@ -126,16 +141,64 @@ func (s *StationURL) Equals(o RVType) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the StationURL
|
||||
// and returns a pointer to the new copy
|
||||
func (s StationURL) CopyRef() RVTypePtr {
|
||||
copied := s.Copy().(StationURL)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the StationURL
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (s *StationURL) Deref() RVType {
|
||||
return *s
|
||||
}
|
||||
|
||||
// Set sets a StationURL parameter.
|
||||
//
|
||||
// "custom" determines whether or not the parameter is a standard
|
||||
// parameter or an application-specific parameter
|
||||
func (s *StationURL) Set(name, value string, custom bool) {
|
||||
if custom {
|
||||
s.customParams[name] = value
|
||||
} else {
|
||||
s.standardParams[name] = value
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the value of the requested param.
|
||||
//
|
||||
// Returns the string value and a bool indicating if the value existed or not.
|
||||
//
|
||||
// "custom" determines whether or not the parameter is a standard
|
||||
// parameter or an application-specific parameter
|
||||
func (s *StationURL) Get(name string, custom bool) (string, bool) {
|
||||
var m map[string]string
|
||||
|
||||
if custom {
|
||||
m = s.customParams
|
||||
} else {
|
||||
m = s.standardParams
|
||||
}
|
||||
|
||||
if value, ok := m[name]; ok {
|
||||
return value, true
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
// SetParamValue sets a StationURL parameter
|
||||
func (s *StationURL) SetParamValue(name, value string) {
|
||||
s.params[name] = value
|
||||
s.standardParams[name] = value
|
||||
}
|
||||
|
||||
// RemoveParam removes a StationURL parameter.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::Remove
|
||||
func (s *StationURL) RemoveParam(name string) {
|
||||
delete(s.params, name)
|
||||
delete(s.standardParams, name)
|
||||
}
|
||||
|
||||
// ParamValue returns the value of the requested param.
|
||||
|
@ -143,8 +206,8 @@ func (s *StationURL) RemoveParam(name string) {
|
|||
// Returns the string value and a bool indicating if the value existed or not.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetParamValue
|
||||
func (s *StationURL) ParamValue(name string) (string, bool) {
|
||||
if value, ok := s.params[name]; ok {
|
||||
func (s StationURL) ParamValue(name string) (string, bool) {
|
||||
if value, ok := s.standardParams[name]; ok {
|
||||
return value, true
|
||||
}
|
||||
|
||||
|
@ -159,7 +222,7 @@ func (s *StationURL) SetAddress(address string) {
|
|||
// Address gets the stations IP address.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetAddress
|
||||
func (s *StationURL) Address() (string, bool) {
|
||||
func (s StationURL) Address() (string, bool) {
|
||||
return s.ParamValue("address")
|
||||
}
|
||||
|
||||
|
@ -185,7 +248,7 @@ func (s *StationURL) SetURLType(urlType constants.StationURLType) {
|
|||
// URLType returns the stations scheme type
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetURLType
|
||||
func (s *StationURL) URLType() constants.StationURLType {
|
||||
func (s StationURL) URLType() constants.StationURLType {
|
||||
return s.urlType
|
||||
}
|
||||
|
||||
|
@ -203,7 +266,7 @@ func (s *StationURL) SetStreamID(streamID uint8) {
|
|||
// Returns a bool indicating if the parameter existed or not.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetStreamID
|
||||
func (s *StationURL) StreamID() (uint8, bool) {
|
||||
func (s StationURL) StreamID() (uint8, bool) {
|
||||
return s.uint8ParamValue("sid")
|
||||
}
|
||||
|
||||
|
@ -221,7 +284,7 @@ func (s *StationURL) SetStreamType(streamType constants.StreamType) {
|
|||
// Returns a bool indicating if the parameter existed or not.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetStreamType
|
||||
func (s *StationURL) StreamType() (constants.StreamType, bool) {
|
||||
func (s StationURL) StreamType() (constants.StreamType, bool) {
|
||||
streamType, ok := s.uint8ParamValue("stream")
|
||||
|
||||
// TODO - Range check on the enum?
|
||||
|
@ -241,13 +304,13 @@ func (s *StationURL) SetNodeID(nodeID uint16) {
|
|||
// Returns a bool indicating if the parameter existed or not.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetNodeId
|
||||
func (s *StationURL) NodeID() (uint16, bool) {
|
||||
func (s StationURL) NodeID() (uint16, bool) {
|
||||
return s.uint16ParamValue("NodeID")
|
||||
}
|
||||
|
||||
// SetPrincipalID sets the stations target PID
|
||||
func (s *StationURL) SetPrincipalID(pid *PID) {
|
||||
s.SetParamValue("PID", strconv.FormatUint(pid.Value(), 10))
|
||||
func (s *StationURL) SetPrincipalID(pid PID) {
|
||||
s.SetParamValue("PID", strconv.FormatUint(uint64(pid), 10))
|
||||
}
|
||||
|
||||
// PrincipalID gets the stations target PID.
|
||||
|
@ -255,10 +318,10 @@ func (s *StationURL) SetPrincipalID(pid *PID) {
|
|||
// Returns a bool indicating if the parameter existed or not.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetPrincipalID
|
||||
func (s *StationURL) PrincipalID() (*PID, bool) {
|
||||
func (s StationURL) PrincipalID() (PID, bool) {
|
||||
pid, ok := s.uint64ParamValue("PID")
|
||||
if !ok {
|
||||
return nil, false
|
||||
return NewPID(0), false
|
||||
}
|
||||
|
||||
return NewPID(pid), true
|
||||
|
@ -276,7 +339,7 @@ func (s *StationURL) SetConnectionID(connectionID uint32) {
|
|||
// Returns a bool indicating if the parameter existed or not.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetConnectionID
|
||||
func (s *StationURL) ConnectionID() (uint32, bool) {
|
||||
func (s StationURL) ConnectionID() (uint32, bool) {
|
||||
return s.uint32ParamValue("CID")
|
||||
}
|
||||
|
||||
|
@ -292,7 +355,7 @@ func (s *StationURL) SetRVConnectionID(connectionID uint32) {
|
|||
// Returns a bool indicating if the parameter existed or not.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetRVConnectionID
|
||||
func (s *StationURL) RVConnectionID() (uint32, bool) {
|
||||
func (s StationURL) RVConnectionID() (uint32, bool) {
|
||||
return s.uint32ParamValue("RVCID")
|
||||
}
|
||||
|
||||
|
@ -306,7 +369,7 @@ func (s *StationURL) SetProbeRequestID(probeRequestID uint32) {
|
|||
// Returns a bool indicating if the parameter existed or not.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetProbeRequestID
|
||||
func (s *StationURL) ProbeRequestID() (uint32, bool) {
|
||||
func (s StationURL) ProbeRequestID() (uint32, bool) {
|
||||
return s.uint32ParamValue("PRID")
|
||||
}
|
||||
|
||||
|
@ -322,7 +385,7 @@ func (s *StationURL) SetFastProbeResponse(fast bool) {
|
|||
// IsFastProbeResponseEnabled checks if fast probe response is enabled
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetFastProbeResponse
|
||||
func (s *StationURL) IsFastProbeResponseEnabled() bool {
|
||||
func (s StationURL) IsFastProbeResponseEnabled() bool {
|
||||
return s.boolParamValue("fastproberesponse")
|
||||
}
|
||||
|
||||
|
@ -336,7 +399,7 @@ func (s *StationURL) SetNATMapping(mapping constants.NATMappingProperties) {
|
|||
// Returns a bool indicating if the parameter existed or not.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetNATMapping
|
||||
func (s *StationURL) NATMapping() (constants.NATMappingProperties, bool) {
|
||||
func (s StationURL) NATMapping() (constants.NATMappingProperties, bool) {
|
||||
natm, ok := s.uint8ParamValue("natm")
|
||||
|
||||
// TODO - Range check on the enum?
|
||||
|
@ -354,7 +417,7 @@ func (s *StationURL) SetNATFiltering(filtering constants.NATFilteringProperties)
|
|||
// Returns a bool indicating if the parameter existed or not.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetNATFiltering
|
||||
func (s *StationURL) NATFiltering() (constants.NATFilteringProperties, bool) {
|
||||
func (s StationURL) NATFiltering() (constants.NATFilteringProperties, bool) {
|
||||
natf, ok := s.uint8ParamValue("natf")
|
||||
|
||||
// TODO - Range check on the enum?
|
||||
|
@ -374,7 +437,7 @@ func (s *StationURL) SetProbeRequestInitiation(probeinit bool) {
|
|||
// IsProbeRequestInitiationEnabled checks wheteher probing should be initiated.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetProbeRequestInitiation
|
||||
func (s *StationURL) IsProbeRequestInitiationEnabled() bool {
|
||||
func (s StationURL) IsProbeRequestInitiationEnabled() bool {
|
||||
return s.boolParamValue("probeinit")
|
||||
}
|
||||
|
||||
|
@ -390,7 +453,7 @@ func (s *StationURL) SetUPnPSupport(supported bool) {
|
|||
// IsUPnPSupported checks whether UPnP is enabled on the station.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetUPnPSupport
|
||||
func (s *StationURL) IsUPnPSupported() bool {
|
||||
func (s StationURL) IsUPnPSupported() bool {
|
||||
return s.boolParamValue("upnp")
|
||||
}
|
||||
|
||||
|
@ -408,10 +471,23 @@ func (s *StationURL) SetNATPMPSupport(supported bool) {
|
|||
// IsNATPMPSupported checks whether PMP is enabled on the station.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetNatPMPSupport
|
||||
func (s *StationURL) IsNATPMPSupported() bool {
|
||||
func (s StationURL) IsNATPMPSupported() bool {
|
||||
return s.boolParamValue("pmp")
|
||||
}
|
||||
|
||||
// SetURL sets the internal url string used for parsing
|
||||
func (s *StationURL) SetURL(url string) {
|
||||
s.url = url
|
||||
}
|
||||
|
||||
// URL returns the string formatted URL.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetURL
|
||||
func (s StationURL) URL() string {
|
||||
s.Format()
|
||||
return s.url
|
||||
}
|
||||
|
||||
// SetType sets the stations type flags
|
||||
func (s *StationURL) SetType(flags uint8) {
|
||||
s.flags = flags // * This normally isn't done, but makes IsPublic and IsBehindNAT simpler
|
||||
|
@ -423,7 +499,7 @@ func (s *StationURL) SetType(flags uint8) {
|
|||
// Returns a bool indicating if the parameter existed or not.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetType
|
||||
func (s *StationURL) Type() (uint8, bool) {
|
||||
func (s StationURL) Type() (uint8, bool) {
|
||||
return s.uint8ParamValue("type")
|
||||
}
|
||||
|
||||
|
@ -435,7 +511,7 @@ func (s *StationURL) SetRelayServerAddress(address string) {
|
|||
// RelayServerAddress gets the address for the relay server
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetRelayServerAddress
|
||||
func (s *StationURL) RelayServerAddress() (string, bool) {
|
||||
func (s StationURL) RelayServerAddress() (string, bool) {
|
||||
return s.ParamValue("Rsa")
|
||||
}
|
||||
|
||||
|
@ -449,7 +525,7 @@ func (s *StationURL) SetRelayServerPort(port uint16) {
|
|||
// Returns a bool indicating if the parameter existed or not.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetRelayServerPort
|
||||
func (s *StationURL) RelayServerPort() (uint16, bool) {
|
||||
func (s StationURL) RelayServerPort() (uint16, bool) {
|
||||
return s.uint16ParamValue("Rsp")
|
||||
}
|
||||
|
||||
|
@ -461,7 +537,7 @@ func (s *StationURL) SetRelayAddress(address string) {
|
|||
// RelayAddress gets the address for the relay
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetRelayAddress
|
||||
func (s *StationURL) RelayAddress() (string, bool) {
|
||||
func (s StationURL) RelayAddress() (string, bool) {
|
||||
return s.ParamValue("Ra")
|
||||
}
|
||||
|
||||
|
@ -475,7 +551,7 @@ func (s *StationURL) SetRelayPort(port uint16) {
|
|||
// Returns a bool indicating if the parameter existed or not.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetRelayPort
|
||||
func (s *StationURL) RelayPort() (uint16, bool) {
|
||||
func (s StationURL) RelayPort() (uint16, bool) {
|
||||
return s.uint16ParamValue("Rp")
|
||||
}
|
||||
|
||||
|
@ -491,7 +567,7 @@ func (s *StationURL) SetUseRelayServer(useRelayServer bool) {
|
|||
// IsRelayServerEnabled checks whether the connection should use a relay server.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetUseRelayServer
|
||||
func (s *StationURL) IsRelayServerEnabled() bool {
|
||||
func (s StationURL) IsRelayServerEnabled() bool {
|
||||
return s.boolParamValue("R")
|
||||
}
|
||||
|
||||
|
@ -508,64 +584,92 @@ func (s *StationURL) SetPlatformType(platformType uint8) {
|
|||
// Returns a bool indicating if the parameter existed or not.
|
||||
//
|
||||
// Originally called nn::nex::StationURL::GetPlatformType
|
||||
func (s *StationURL) PlatformType() (uint8, bool) {
|
||||
func (s StationURL) PlatformType() (uint8, bool) {
|
||||
return s.uint8ParamValue("Pl")
|
||||
}
|
||||
|
||||
// IsPublic checks if the station is a public address
|
||||
func (s *StationURL) IsPublic() bool {
|
||||
func (s StationURL) IsPublic() bool {
|
||||
return s.flags&uint8(constants.StationURLFlagPublic) == uint8(constants.StationURLFlagPublic)
|
||||
}
|
||||
|
||||
// IsBehindNAT checks if the user is behind NAT
|
||||
func (s *StationURL) IsBehindNAT() bool {
|
||||
func (s StationURL) IsBehindNAT() bool {
|
||||
return s.flags&uint8(constants.StationURLFlagBehindNAT) == uint8(constants.StationURLFlagBehindNAT)
|
||||
}
|
||||
|
||||
// FromString parses the StationURL data from a string
|
||||
func (s *StationURL) FromString(str string) {
|
||||
if str == "" {
|
||||
// Parse parses the StationURL data from a string
|
||||
func (s *StationURL) Parse() {
|
||||
url := s.url
|
||||
if url == "" || len(url) > 1024 {
|
||||
// TODO - Should we return an error here?
|
||||
return
|
||||
}
|
||||
|
||||
parts := strings.Split(str, ":/")
|
||||
parametersString := ""
|
||||
parts := strings.SplitN(string(url), ":/", 2)
|
||||
|
||||
// * Unknown schemes seem to be supported based on
|
||||
// * Format__Q3_2nn3nex10StationURLFv
|
||||
if len(parts) == 1 {
|
||||
parametersString = parts[0]
|
||||
s.SetURLType(constants.UnknownStationURLType)
|
||||
} else if len(parts) == 2 {
|
||||
scheme := parts[0]
|
||||
parametersString = parts[1]
|
||||
|
||||
if scheme == "prudp" {
|
||||
s.SetURLType(constants.StationURLPRUDP)
|
||||
} else if scheme == "prudps" {
|
||||
s.SetURLType(constants.StationURLPRUDPS)
|
||||
} else if scheme == "udp" {
|
||||
s.SetURLType(constants.StationURLUDP)
|
||||
} else {
|
||||
s.SetURLType(constants.UnknownStationURLType)
|
||||
}
|
||||
} else {
|
||||
// * Badly formatted station
|
||||
// * Unknown schemes are disallowed to be parsed
|
||||
// * according to Parse__Q3_2nn3nex10StationURLFv
|
||||
if len(parts) != 2 {
|
||||
return
|
||||
}
|
||||
|
||||
scheme := parts[0]
|
||||
parametersString := parts[1]
|
||||
|
||||
switch scheme {
|
||||
case "prudp":
|
||||
s.SetURLType(constants.StationURLPRUDP)
|
||||
case "prudps":
|
||||
s.SetURLType(constants.StationURLPRUDPS)
|
||||
case "udp":
|
||||
s.SetURLType(constants.StationURLUDP)
|
||||
default:
|
||||
return // * Unknown scheme
|
||||
}
|
||||
|
||||
// * Return if there are no fields
|
||||
if parametersString == "" {
|
||||
return
|
||||
}
|
||||
|
||||
parameters := strings.Split(parametersString, ";")
|
||||
parts = strings.SplitN(parametersString, "#", 2)
|
||||
standardSection := parts[0]
|
||||
customSection := ""
|
||||
|
||||
for i := 0; i < len(parameters); i++ {
|
||||
// TODO - StationURL parameters support extra data through the # delimiter. What is that? Need to support it somehow
|
||||
name, value, _ := strings.Cut(parameters[i], "=")
|
||||
if len(parts) == 2 {
|
||||
customSection = parts[1]
|
||||
}
|
||||
|
||||
s.SetParamValue(name, value)
|
||||
standardParameters := strings.Split(standardSection, ";")
|
||||
|
||||
for i := range standardParameters {
|
||||
key, value, _ := strings.Cut(standardParameters[i], "=")
|
||||
|
||||
if key == "address" && len(value) > 256 {
|
||||
// * The client can only hold a host name of up to 256 characters
|
||||
// TODO - Should we return an error here?
|
||||
return
|
||||
}
|
||||
|
||||
if key == "port" {
|
||||
if port, err := strconv.Atoi(value); err != nil || (port < 0 || port > 65535) {
|
||||
// TODO - Should we return an error here?
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
s.Set(key, value, false)
|
||||
}
|
||||
|
||||
if len(customSection) != 0 {
|
||||
customParameters := strings.Split(customSection, ";")
|
||||
|
||||
for i := range customParameters {
|
||||
key, value, _ := strings.Cut(customParameters[i], "=")
|
||||
|
||||
s.Set(key, value, true)
|
||||
}
|
||||
}
|
||||
|
||||
if flags, ok := s.uint8ParamValue("type"); ok {
|
||||
|
@ -573,8 +677,8 @@ func (s *StationURL) FromString(str string) {
|
|||
}
|
||||
}
|
||||
|
||||
// EncodeToString encodes the StationURL into a string
|
||||
func (s *StationURL) EncodeToString() string {
|
||||
// Format encodes the StationURL into a string
|
||||
func (s *StationURL) Format() {
|
||||
scheme := ""
|
||||
|
||||
// * Unknown schemes seem to be supported based on
|
||||
|
@ -589,40 +693,71 @@ func (s *StationURL) EncodeToString() string {
|
|||
|
||||
fields := make([]string, 0)
|
||||
|
||||
for key, value := range s.params {
|
||||
// TODO - StationURL parameters support extra data through the # delimiter. What is that? Need to support it somehow
|
||||
for key, value := range s.standardParams {
|
||||
if key == "address" && len(value) > 256 {
|
||||
// * The client can only hold a host name of up to 256 characters
|
||||
// TODO - Should we return an error here?
|
||||
return
|
||||
}
|
||||
|
||||
if key == "port" {
|
||||
if port, err := strconv.Atoi(value); err != nil || (port < 0 || port > 65535) {
|
||||
// TODO - Should we return an error here?
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fields = append(fields, fmt.Sprintf("%s=%s", key, value))
|
||||
}
|
||||
|
||||
return scheme + strings.Join(fields, ";")
|
||||
url := scheme + strings.Join(fields, ";")
|
||||
|
||||
if len(s.customParams) != 0 {
|
||||
customFields := make([]string, 0)
|
||||
|
||||
for key, value := range s.customParams {
|
||||
customFields = append(customFields, fmt.Sprintf("%s=%s", key, value))
|
||||
}
|
||||
|
||||
url = url + "#" + strings.Join(customFields, ";")
|
||||
}
|
||||
|
||||
if len(url) > 1024 {
|
||||
// TODO - Should we return an error here?
|
||||
return
|
||||
}
|
||||
|
||||
s.url = url
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (s *StationURL) String() string {
|
||||
func (s StationURL) String() string {
|
||||
return s.FormatToString(0)
|
||||
}
|
||||
|
||||
// FormatToString pretty-prints the struct data using the provided indentation level
|
||||
func (s *StationURL) FormatToString(indentationLevel int) string {
|
||||
func (s StationURL) FormatToString(indentationLevel int) string {
|
||||
indentationValues := strings.Repeat("\t", indentationLevel+1)
|
||||
indentationEnd := strings.Repeat("\t", indentationLevel)
|
||||
|
||||
var b strings.Builder
|
||||
|
||||
b.WriteString("StationURL{\n")
|
||||
b.WriteString(fmt.Sprintf("%surl: %q\n", indentationValues, s.EncodeToString()))
|
||||
b.WriteString(fmt.Sprintf("%surl: %q\n", indentationValues, s.URL()))
|
||||
b.WriteString(fmt.Sprintf("%s}", indentationEnd))
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// NewStationURL returns a new StationURL
|
||||
func NewStationURL(str string) *StationURL {
|
||||
stationURL := &StationURL{
|
||||
params: make(map[string]string),
|
||||
func NewStationURL(url String) StationURL {
|
||||
stationURL := StationURL{
|
||||
url: string(url),
|
||||
standardParams: make(map[string]string),
|
||||
customParams: make(map[string]string),
|
||||
}
|
||||
|
||||
stationURL.FromString(str)
|
||||
stationURL.Parse()
|
||||
|
||||
return stationURL
|
||||
}
|
||||
|
|
|
@ -7,23 +7,21 @@ import (
|
|||
)
|
||||
|
||||
// String is an implementation of rdv::String.
|
||||
// Wraps a primitive Go string.
|
||||
type String struct {
|
||||
Value string
|
||||
}
|
||||
// Type alias of string
|
||||
type String string
|
||||
|
||||
// WriteTo writes the String to the given writable
|
||||
func (s *String) WriteTo(writable Writable) {
|
||||
str := s.Value + "\x00"
|
||||
strLength := len(str)
|
||||
func (s String) WriteTo(writable Writable) {
|
||||
s = s + "\x00"
|
||||
strLength := len(s)
|
||||
|
||||
if writable.StringLengthSize() == 4 {
|
||||
writable.WritePrimitiveUInt32LE(uint32(strLength))
|
||||
writable.WriteUInt32LE(uint32(strLength))
|
||||
} else {
|
||||
writable.WritePrimitiveUInt16LE(uint16(strLength))
|
||||
writable.WriteUInt16LE(uint16(strLength))
|
||||
}
|
||||
|
||||
writable.Write([]byte(str))
|
||||
writable.Write([]byte(s))
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the String from the given readable
|
||||
|
@ -32,11 +30,11 @@ func (s *String) ExtractFrom(readable Readable) error {
|
|||
var err error
|
||||
|
||||
if readable.StringLengthSize() == 4 {
|
||||
l, e := readable.ReadPrimitiveUInt32LE()
|
||||
l, e := readable.ReadUInt32LE()
|
||||
length = uint64(l)
|
||||
err = e
|
||||
} else {
|
||||
l, e := readable.ReadPrimitiveUInt16LE()
|
||||
l, e := readable.ReadUInt16LE()
|
||||
length = uint64(l)
|
||||
err = e
|
||||
}
|
||||
|
@ -56,31 +54,45 @@ func (s *String) ExtractFrom(readable Readable) error {
|
|||
|
||||
str := strings.TrimRight(string(stringData), "\x00")
|
||||
|
||||
s.Value = str
|
||||
|
||||
*s = String(str)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the String. Requires type assertion when used
|
||||
func (s *String) Copy() RVType {
|
||||
return NewString(s.Value)
|
||||
func (s String) Copy() RVType {
|
||||
return NewString(string(s))
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (s *String) Equals(o RVType) bool {
|
||||
if _, ok := o.(*String); !ok {
|
||||
func (s String) Equals(o RVType) bool {
|
||||
if _, ok := o.(String); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return s.Value == o.(*String).Value
|
||||
return s == o.(String)
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the String
|
||||
// and returns a pointer to the new copy
|
||||
func (s String) CopyRef() RVTypePtr {
|
||||
copied := s.Copy().(String)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the String
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (s *String) Deref() RVType {
|
||||
return *s
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (s *String) String() string {
|
||||
return fmt.Sprintf("%q", s.Value)
|
||||
func (s String) String() string {
|
||||
return fmt.Sprintf("%q", string(s))
|
||||
}
|
||||
|
||||
// NewString returns a new String
|
||||
func NewString(str string) *String {
|
||||
return &String{Value: str}
|
||||
func NewString(input string) String {
|
||||
s := String(input)
|
||||
return s
|
||||
}
|
||||
|
|
|
@ -7,18 +7,18 @@ import (
|
|||
|
||||
// Structure represents a Quazal Rendez-Vous/NEX Structure (custom class) base struct.
|
||||
type Structure struct {
|
||||
StructureVersion uint8
|
||||
StructureVersion uint8 `json:"structure_version" db:"structure_version" bson:"structure_version" xml:"StructureVersion"`
|
||||
}
|
||||
|
||||
// ExtractHeaderFrom extracts the structure header from the given readable
|
||||
func (s *Structure) ExtractHeaderFrom(readable Readable) error {
|
||||
if readable.UseStructureHeader() {
|
||||
version, err := readable.ReadPrimitiveUInt8()
|
||||
version, err := readable.ReadUInt8()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read Structure version. %s", err.Error())
|
||||
}
|
||||
|
||||
contentLength, err := readable.ReadPrimitiveUInt32LE()
|
||||
contentLength, err := readable.ReadUInt32LE()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read Structure content length. %s", err.Error())
|
||||
}
|
||||
|
@ -34,9 +34,9 @@ func (s *Structure) ExtractHeaderFrom(readable Readable) error {
|
|||
}
|
||||
|
||||
// WriteHeaderTo writes the structure header to the given writable
|
||||
func (s *Structure) WriteHeaderTo(writable Writable, contentLength uint32) {
|
||||
func (s Structure) WriteHeaderTo(writable Writable, contentLength uint32) {
|
||||
if writable.UseStructureHeader() {
|
||||
writable.WritePrimitiveUInt8(s.StructureVersion)
|
||||
writable.WritePrimitiveUInt32LE(contentLength)
|
||||
writable.WriteUInt8(s.StructureVersion)
|
||||
writable.WriteUInt32LE(contentLength)
|
||||
}
|
||||
}
|
||||
|
|
62
types/uint16.go
Normal file
62
types/uint16.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// UInt16 is a type alias for the Go basic type uint16 for use as an RVType
|
||||
type UInt16 uint16
|
||||
|
||||
// WriteTo writes the UInt16 to the given writable
|
||||
func (u16 UInt16) WriteTo(writable Writable) {
|
||||
writable.WriteUInt16LE(uint16(u16))
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the UInt16 value from the given readable
|
||||
func (u16 *UInt16) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadUInt16LE()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*u16 = UInt16(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the UInt16. Requires type assertion when used
|
||||
func (u16 UInt16) Copy() RVType {
|
||||
return NewUInt16(uint16(u16))
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (u16 UInt16) Equals(o RVType) bool {
|
||||
other, ok := o.(UInt16)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return u16 == other
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the UInt16
|
||||
// and returns a pointer to the new copy
|
||||
func (u16 UInt16) CopyRef() RVTypePtr {
|
||||
copied := u16.Copy().(UInt16)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the UInt16
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (u16 *UInt16) Deref() RVType {
|
||||
return *u16
|
||||
}
|
||||
|
||||
// String returns a string representation of the UInt16
|
||||
func (u16 UInt16) String() string {
|
||||
return fmt.Sprintf("%d", u16)
|
||||
}
|
||||
|
||||
// NewUInt16 returns a new UInt16
|
||||
func NewUInt16(input uint16) UInt16 {
|
||||
u16 := UInt16(input)
|
||||
return u16
|
||||
}
|
62
types/uint32.go
Normal file
62
types/uint32.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// UInt32 is a type alias for the Go basic type uint32 for use as an RVType
|
||||
type UInt32 uint32
|
||||
|
||||
// WriteTo writes the UInt32 to the given writable
|
||||
func (u32 UInt32) WriteTo(writable Writable) {
|
||||
writable.WriteUInt32LE(uint32(u32))
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the UInt32 value from the given readable
|
||||
func (u32 *UInt32) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadUInt32LE()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*u32 = UInt32(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the UInt32. Requires type assertion when used
|
||||
func (u32 UInt32) Copy() RVType {
|
||||
return NewUInt32(uint32(u32))
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (u32 UInt32) Equals(o RVType) bool {
|
||||
other, ok := o.(UInt32)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return u32 == other
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the UInt32
|
||||
// and returns a pointer to the new copy
|
||||
func (u32 UInt32) CopyRef() RVTypePtr {
|
||||
copied := u32.Copy().(UInt32)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the UInt32
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (u32 *UInt32) Deref() RVType {
|
||||
return *u32
|
||||
}
|
||||
|
||||
// String returns a string representation of the UInt32
|
||||
func (u32 UInt32) String() string {
|
||||
return fmt.Sprintf("%d", u32)
|
||||
}
|
||||
|
||||
// NewUInt32 returns a new UInt32
|
||||
func NewUInt32(input uint32) UInt32 {
|
||||
u32 := UInt32(input)
|
||||
return u32
|
||||
}
|
62
types/uint64.go
Normal file
62
types/uint64.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// UInt64 is a type alias for the Go basic type uint64 for use as an RVType
|
||||
type UInt64 uint64
|
||||
|
||||
// WriteTo writes the UInt64 to the given writable
|
||||
func (u64 UInt64) WriteTo(writable Writable) {
|
||||
writable.WriteUInt64LE(uint64(u64))
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the UInt64 value from the given readable
|
||||
func (u64 *UInt64) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadUInt64LE()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*u64 = UInt64(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the UInt64. Requires type assertion when used
|
||||
func (u64 UInt64) Copy() RVType {
|
||||
return NewUInt64(uint64(u64))
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (u64 UInt64) Equals(o RVType) bool {
|
||||
other, ok := o.(UInt64)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return u64 == other
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the UInt64
|
||||
// and returns a pointer to the new copy
|
||||
func (u64 UInt64) CopyRef() RVTypePtr {
|
||||
copied := u64.Copy().(UInt64)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the UInt64
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (u64 *UInt64) Deref() RVType {
|
||||
return *u64
|
||||
}
|
||||
|
||||
// String returns a string representation of the UInt64
|
||||
func (u64 UInt64) String() string {
|
||||
return fmt.Sprintf("%d", u64)
|
||||
}
|
||||
|
||||
// NewUInt64 returns a new UInt64
|
||||
func NewUInt64(input uint64) UInt64 {
|
||||
u64 := UInt64(input)
|
||||
return u64
|
||||
}
|
62
types/uint8.go
Normal file
62
types/uint8.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package types
|
||||
|
||||
import "fmt"
|
||||
|
||||
// UInt8 is a type alias for the Go basic type uint8 for use as an RVType
|
||||
type UInt8 uint8
|
||||
|
||||
// WriteTo writes the UInt8 to the given writable
|
||||
func (u8 UInt8) WriteTo(writable Writable) {
|
||||
writable.WriteUInt8(uint8(u8))
|
||||
}
|
||||
|
||||
// ExtractFrom extracts the UInt8 value from the given readable
|
||||
func (u8 *UInt8) ExtractFrom(readable Readable) error {
|
||||
value, err := readable.ReadUInt8()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*u8 = UInt8(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the UInt8. Requires type assertion when used
|
||||
func (u8 UInt8) Copy() RVType {
|
||||
return NewUInt8(uint8(u8))
|
||||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (u8 UInt8) Equals(o RVType) bool {
|
||||
other, ok := o.(UInt8)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return u8 == other
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the UInt8
|
||||
// and returns a pointer to the new copy
|
||||
func (u8 UInt8) CopyRef() RVTypePtr {
|
||||
copied := u8.Copy().(UInt8)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the UInt8
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (u8 *UInt8) Deref() RVType {
|
||||
return *u8
|
||||
}
|
||||
|
||||
// String returns a string representation of the UInt8
|
||||
func (u8 UInt8) String() string {
|
||||
return fmt.Sprintf("%d", u8)
|
||||
}
|
||||
|
||||
// NewUInt8 returns a new UInt8
|
||||
func NewUInt8(input uint8) UInt8 {
|
||||
u8 := UInt8(input)
|
||||
return u8
|
||||
}
|
|
@ -6,22 +6,22 @@ import (
|
|||
)
|
||||
|
||||
// VariantTypes holds a mapping of RVTypes that are accessible in a Variant
|
||||
var VariantTypes = make(map[uint8]RVType)
|
||||
var VariantTypes = make(map[UInt8]RVType)
|
||||
|
||||
// RegisterVariantType registers a RVType to be accessible in a Variant
|
||||
func RegisterVariantType(id uint8, rvType RVType) {
|
||||
func RegisterVariantType(id UInt8, rvType RVType) {
|
||||
VariantTypes[id] = rvType
|
||||
}
|
||||
|
||||
// Variant is an implementation of rdv::Variant.
|
||||
// This type can hold many other types, denoted by a type ID.
|
||||
type Variant struct {
|
||||
TypeID *PrimitiveU8
|
||||
Type RVType
|
||||
TypeID UInt8 `json:"type_id" db:"type_id" bson:"type_id" xml:"TypeID"`
|
||||
Type RVType `json:"type" db:"type" bson:"type" xml:"Type"`
|
||||
}
|
||||
|
||||
// WriteTo writes the Variant to the given writable
|
||||
func (v *Variant) WriteTo(writable Writable) {
|
||||
func (v Variant) WriteTo(writable Writable) {
|
||||
v.TypeID.WriteTo(writable)
|
||||
|
||||
if v.Type != nil {
|
||||
|
@ -36,25 +36,34 @@ func (v *Variant) ExtractFrom(readable Readable) error {
|
|||
return fmt.Errorf("Failed to read Variant type ID. %s", err.Error())
|
||||
}
|
||||
|
||||
typeID := v.TypeID
|
||||
|
||||
// * Type ID of 0 is a "None" type. There is no data
|
||||
if v.TypeID.Value == 0 {
|
||||
if typeID == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, ok := VariantTypes[v.TypeID.Value]; !ok {
|
||||
return fmt.Errorf("Invalid Variant type ID %d", v.TypeID)
|
||||
if _, ok := VariantTypes[typeID]; !ok {
|
||||
return fmt.Errorf("Invalid Variant type ID %d", typeID)
|
||||
}
|
||||
|
||||
v.Type = VariantTypes[v.TypeID.Value].Copy()
|
||||
// * Create a new copy and get a pointer to it.
|
||||
// * Required so that we have access to ExtractFrom
|
||||
ptr := VariantTypes[typeID].CopyRef()
|
||||
if err := ptr.ExtractFrom(readable); err != nil {
|
||||
return fmt.Errorf("Failed to read Variant type data. %s", err.Error())
|
||||
}
|
||||
|
||||
return v.Type.ExtractFrom(readable)
|
||||
v.Type = ptr.Deref() // * Dereference the RVTypePtr pointer back into a non-pointer type
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a pointer to a copy of the Variant. Requires type assertion when used
|
||||
func (v *Variant) Copy() RVType {
|
||||
func (v Variant) Copy() RVType {
|
||||
copied := NewVariant()
|
||||
|
||||
copied.TypeID = v.TypeID.Copy().(*PrimitiveU8)
|
||||
copied.TypeID = v.TypeID.Copy().(UInt8)
|
||||
|
||||
if v.Type != nil {
|
||||
copied.Type = v.Type.Copy()
|
||||
|
@ -64,12 +73,12 @@ func (v *Variant) Copy() RVType {
|
|||
}
|
||||
|
||||
// Equals checks if the input is equal in value to the current instance
|
||||
func (v *Variant) Equals(o RVType) bool {
|
||||
if _, ok := o.(*Variant); !ok {
|
||||
func (v Variant) Equals(o RVType) bool {
|
||||
if _, ok := o.(Variant); !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
other := o.(*Variant)
|
||||
other := o.(Variant)
|
||||
|
||||
if !v.TypeID.Equals(other.TypeID) {
|
||||
return false
|
||||
|
@ -82,13 +91,27 @@ func (v *Variant) Equals(o RVType) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// CopyRef copies the current value of the Variant
|
||||
// and returns a pointer to the new copy
|
||||
func (v Variant) CopyRef() RVTypePtr {
|
||||
copied := v.Copy().(Variant)
|
||||
return &copied
|
||||
}
|
||||
|
||||
// Deref takes a pointer to the Variant
|
||||
// and dereferences it to the raw value.
|
||||
// Only useful when working with an instance of RVTypePtr
|
||||
func (v *Variant) Deref() RVType {
|
||||
return *v
|
||||
}
|
||||
|
||||
// String returns a string representation of the struct
|
||||
func (v *Variant) String() string {
|
||||
func (v Variant) String() string {
|
||||
return v.FormatToString(0)
|
||||
}
|
||||
|
||||
// FormatToString pretty-prints the struct data using the provided indentation level
|
||||
func (v *Variant) FormatToString(indentationLevel int) string {
|
||||
func (v Variant) FormatToString(indentationLevel int) string {
|
||||
indentationValues := strings.Repeat("\t", indentationLevel+1)
|
||||
indentationEnd := strings.Repeat("\t", indentationLevel)
|
||||
|
||||
|
@ -109,10 +132,10 @@ func (v *Variant) FormatToString(indentationLevel int) string {
|
|||
}
|
||||
|
||||
// NewVariant returns a new Variant
|
||||
func NewVariant() *Variant {
|
||||
func NewVariant() Variant {
|
||||
// * Type ID of 0 is a "None" type. There is no data
|
||||
return &Variant{
|
||||
TypeID: NewPrimitiveU8(0),
|
||||
return Variant{
|
||||
TypeID: NewUInt8(0),
|
||||
Type: nil,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,21 +2,21 @@ package types
|
|||
|
||||
// Writable represents a struct that types can write to
|
||||
type Writable interface {
|
||||
StringLengthSize() int // Returns the size of the length field for rdv::String types. Only 2 and 4 are valid
|
||||
PIDSize() int // Returns the size of the length fields for nn::nex::PID types. Only 4 and 8 are valid
|
||||
UseStructureHeader() bool // Returns whether or not Structure types should use a header
|
||||
CopyNew() Writable // Returns a new Writable with the same settings, but an empty buffer
|
||||
Write(data []byte) // Writes the provided data to the buffer
|
||||
WritePrimitiveUInt8(value uint8) // Writes a primitive Go uint8
|
||||
WritePrimitiveUInt16LE(value uint16) // Writes a primitive Go uint16
|
||||
WritePrimitiveUInt32LE(value uint32) // Writes a primitive Go uint32
|
||||
WritePrimitiveUInt64LE(value uint64) // Writes a primitive Go uint64
|
||||
WritePrimitiveInt8(value int8) // Writes a primitive Go int8
|
||||
WritePrimitiveInt16LE(value int16) // Writes a primitive Go int16
|
||||
WritePrimitiveInt32LE(value int32) // Writes a primitive Go int32
|
||||
WritePrimitiveInt64LE(value int64) // Writes a primitive Go int64
|
||||
WritePrimitiveFloat32LE(value float32) // Writes a primitive Go float32
|
||||
WritePrimitiveFloat64LE(value float64) // Writes a primitive Go float64
|
||||
WritePrimitiveBool(value bool) // Writes a primitive Go bool
|
||||
Bytes() []byte // Returns the data written t othe buffer
|
||||
StringLengthSize() int // Returns the size of the length field for rdv::String types. Only 2 and 4 are valid
|
||||
PIDSize() int // Returns the size of the length fields for nn::nex::PID types. Only 4 and 8 are valid
|
||||
UseStructureHeader() bool // Returns whether or not Structure types should use a header
|
||||
CopyNew() Writable // Returns a new Writable with the same settings, but an empty buffer
|
||||
Write(data []byte) // Writes the provided data to the buffer
|
||||
WriteUInt8(value uint8) // Writes a primitive Go uint8
|
||||
WriteUInt16LE(value uint16) // Writes a primitive Go uint16
|
||||
WriteUInt32LE(value uint32) // Writes a primitive Go uint32
|
||||
WriteUInt64LE(value uint64) // Writes a primitive Go uint64
|
||||
WriteInt8(value int8) // Writes a primitive Go int8
|
||||
WriteInt16LE(value int16) // Writes a primitive Go int16
|
||||
WriteInt32LE(value int32) // Writes a primitive Go int32
|
||||
WriteInt64LE(value int64) // Writes a primitive Go int64
|
||||
WriteFloat32LE(value float32) // Writes a primitive Go float32
|
||||
WriteFloat64LE(value float64) // Writes a primitive Go float64
|
||||
WriteBool(value bool) // Writes a primitive Go bool
|
||||
Bytes() []byte // Returns the data written to the buffer
|
||||
}
|
||||
|
|
|
@ -21,27 +21,27 @@ func (wseh *wsEventHandler) OnOpen(socket *gws.Conn) {
|
|||
_ = socket.SetDeadline(time.Now().Add(pingInterval + pingWait))
|
||||
}
|
||||
|
||||
func (wseh *wsEventHandler) OnClose(wsConn *gws.Conn, err error) {
|
||||
connections := make([]*PRUDPConnection, 0)
|
||||
func (wseh *wsEventHandler) OnClose(wsConn *gws.Conn, _ error) {
|
||||
// * Loop over all connections on all endpoints
|
||||
wseh.prudpServer.Endpoints.Each(func(streamid uint8, pep *PRUDPEndPoint) bool {
|
||||
connections := make([]*PRUDPConnection, 0)
|
||||
|
||||
socket, ok := wseh.prudpServer.Connections.Get(wsConn.RemoteAddr().String())
|
||||
if !ok {
|
||||
// TODO - Error?
|
||||
return
|
||||
}
|
||||
pep.Connections.Each(func(discriminator string, pc *PRUDPConnection) bool {
|
||||
if pc.Socket.Address == wsConn.RemoteAddr() {
|
||||
connections = append(connections, pc)
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
socket.Connections.Each(func(_ uint8, connection *PRUDPConnection) bool {
|
||||
connections = append(connections, connection)
|
||||
// * We cannot modify a MutexMap while looping over it
|
||||
// * since the mutex is locked. We first need to grab
|
||||
// * the entries we want to delete, and then loop over
|
||||
// * them here to actually clean them up
|
||||
for _, connection := range connections {
|
||||
pep.cleanupConnection(connection) // * "removed" event is dispatched here
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// * We cannot modify a MutexMap while looping over it
|
||||
// * since the mutex is locked. We first need to grab
|
||||
// * the entries we want to delete, and then loop over
|
||||
// * them here to actually clean them up
|
||||
for _, connection := range connections {
|
||||
connection.cleanup() // * "removed" event is dispatched here
|
||||
}
|
||||
}
|
||||
|
||||
func (wseh *wsEventHandler) OnPing(socket *gws.Conn, payload []byte) {
|
||||
|
|
Loading…
Add table
Reference in a new issue