From 2d5b304453943ca32b885f16f1d7cbbe38c23fdc Mon Sep 17 00:00:00 2001 From: Yu-Vitaqua-fer-Chronos Date: Sun, 3 Mar 2024 23:02:12 +0000 Subject: [PATCH] sans-io approach :) --- .vscode/settings.json | 3 + modernnet.nimble | 2 +- src/modernnet.nim | 8 +-- src/modernnet/buffer.nim | 40 ++++-------- src/modernnet/io.nim | 61 +++++++++++++++++ src/modernnet/network.nim | 79 ----------------------- src/modernnet/packets/common.nim | 24 ------- src/modernnet/{ => private}/constants.nim | 0 src/modernnet/private/utils.nim | 51 +++++++++++++++ src/modernnet/serialisation.nim | 16 ----- tests/test1.nim | 16 ++--- 11 files changed, 140 insertions(+), 160 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/modernnet/io.nim delete mode 100644 src/modernnet/network.nim delete mode 100644 src/modernnet/packets/common.nim rename src/modernnet/{ => private}/constants.nim (100%) create mode 100644 src/modernnet/private/utils.nim delete mode 100644 src/modernnet/serialisation.nim diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9661658 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "nim.project": ["src/modernnet.nim"] +} \ No newline at end of file diff --git a/modernnet.nimble b/modernnet.nimble index 7c5f004..578046b 100644 --- a/modernnet.nimble +++ b/modernnet.nimble @@ -1,6 +1,6 @@ # Package -version = "2.1.0" +version = "3.0.0" author = "Yu-Vitaqua-fer-Chronos" description = "ModernNet implements a packet reading and writing system, as well as some useful tools for implementing this into your own project!" license = "Apache-2.0" diff --git a/src/modernnet.nim b/src/modernnet.nim index c6ac173..f5a9b1f 100644 --- a/src/modernnet.nim +++ b/src/modernnet.nim @@ -15,14 +15,14 @@ import "."/modernnet/[ exceptions, helpers, - network, buffer, - types + types, + io ] export exceptions, helpers, - network, buffer, - types \ No newline at end of file + types, + io \ No newline at end of file diff --git a/src/modernnet/buffer.nim b/src/modernnet/buffer.nim index 4a10c54..af033c5 100644 --- a/src/modernnet/buffer.nim +++ b/src/modernnet/buffer.nim @@ -1,37 +1,30 @@ import ./[ exceptions, - constants, types ] -import ./private/stew/endians2 +import ./private/[ + constants, + utils +] type Buffer* = ref object buf*: seq[byte] pos*: int - NumberN* = byte | int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | float32 | float64 +func len*(b: Buffer): int = b.buf.len func newBuffer*(data: openArray[byte] = newSeq[byte]()): Buffer = Buffer(buf: @data) # Writing -func writeNum*[R: NumberN | bool](b: Buffer, value: R) = +func writeNum*[R: SizedNum](b: Buffer, value: R) = ## Writes any numeric type or boolean to a buffer - if (b.pos + sizeof(R)) > b.buf.len: + if (b.pos + sizeof(R)) > b.len: b.buf.setLen(b.pos + sizeof(R)) - when sizeof(R) == 1: - b.buf[b.pos] = cast[byte](value) - elif sizeof(R) == 2: - b.buf[b.pos..<(b.pos+sizeof(R))] = cast[uint16](value).toBytesBE - elif sizeof(R) == 4: - b.buf[b.pos..<(b.pos+sizeof(R))] = cast[uint32](value).toBytesBE - elif sizeof(R) == 8: - b.buf[b.pos..<(b.pos+sizeof(R))] = cast[uint64](value).toBytesBE - else: - {.error: "Serialisation of `" & $R & "` is not implemented!".} + deposit(value, b.buf.toOpenArray(b.pos, b.pos + sizeof(R) - 1)) b.pos += sizeof(R) @@ -71,25 +64,16 @@ template writePosition*(b: Buffer, p: Position, format = XZY) = # Reading -func readNum*[R: NumberN | bool](b: Buffer): R {.raises: [MnEndOfBufferError].} = +func readNum*[R: SizedNum](b: Buffer): R {.raises: [MnEndOfBufferError, ValueError].} = ## Reads any numeric type or boolean from a buffer if (b.pos + sizeof(R)) > b.buf.len: raise newException(MnEndOfBufferError, "Reached the end of the buffer while trying to read a " & $R & '!') - result = when sizeof(R) == 1: - cast[R](b.buf[b.pos]) - elif sizeof(R) == 2: - cast[R](fromBytesBE(uint16, b.buf[b.pos..<(b.pos+sizeof(R))])) - elif sizeof(R) == 4: - cast[R](fromBytesBE(uint32, b.buf[b.pos..<(b.pos+sizeof(R))])) - elif sizeof(R) == 8: - cast[R](fromBytesBE(uint64, b.buf[b.pos..<(b.pos+sizeof(R))])) - else: - {.error: "Deserialisation of `" & $R & "` is not implemented!".} + result = b.buf.toOpenArray(b.pos, b.pos + sizeof(R) - 1).extract(R) b.pos += sizeof(R) -func readVarNum*[R: int32 | int64](b: Buffer): R {.raises: [MnEndOfBufferError, MnPacketParsingError].} = +func readVarNum*[R: int32 | int64](b: Buffer): R {.raises: [MnEndOfBufferError, ValueError, MnPacketParsingError].} = ## Reads a VarInt or a VarLong from a buffer var position: int8 = 0 @@ -119,7 +103,7 @@ func readUUID*(b: Buffer): UUID = ## Reads a UUID from a buffer initUUID(b.readNum[:int64](), b.readNum[:int64]()) -func readString*(b: Buffer, maxLength = 32767): string {.raises: [MnEndOfBufferError, MnPacketParsingError].} = +func readString*(b: Buffer, maxLength = 32767): string {.raises: [MnEndOfBufferError, ValueError, MnPacketParsingError].} = ## Reads a string from a buffer let length = b.readVarNum[:int32]() diff --git a/src/modernnet/io.nim b/src/modernnet/io.nim new file mode 100644 index 0000000..15a5535 --- /dev/null +++ b/src/modernnet/io.nim @@ -0,0 +1,61 @@ +#! Copyright 2024 Yu-Vitaqua-fer-Chronos +#! +#! Licensed under the Apache License, Version 2.0 (the "License"); +#! you may not use this file except in compliance with the License. +#! You may obtain a copy of the License at +#! +#! http://www.apache.org/licenses/LICENSE-2.0 +#! +#! Unless required by applicable law or agreed to in writing, software +#! distributed under the License is distributed on an "AS IS" BASIS, +#! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#! See the License for the specific language governing permissions and +#! limitations under the License. + +import ./[ + exceptions, + buffer +] +import ./private/[ + constants, + utils +] + +using receive_bytes: proc(_: int): seq[byte] + +proc read[R: SizedNum](_: typedesc[R], receive_bytes): R = + ## Reads any numeric type or boolean from the callback + let bytes = receive_bytes(sizeof(R)) + bytes.extract(R) + +proc readVar[R: int32 | int64](_: typedesc[R], receive_bytes): R = + ## Reads a VarInt or a VarLong from the callback + var + position: int8 = 0 + currentByte: int8 + + while true: + currentByte = read(int8, receive_bytes) + result = result or ((currentByte.R and SegmentBits) shl position) + + if (currentByte and ContinueBit) == 0: + break + + position += 7 + + when R is int32: + if position >= VarIntBits: + raise newException(MnPacketParsingError, "VarInt is too big!") + + elif R is int64: + if position >= VarLongBits: + raise newException(MnPacketParsingError, "VarLong is too big!") + + else: + {.error: "Deserialisation of `" & $R & "` is not implemented!".} + +proc readRawPacket*(receive_bytes): (int, Buffer) = + let packetId = readVar(int32, receive_bytes) + let length = readVar(int32, receive_bytes) + + return (packetId.int, newBuffer(receive_bytes(length))) \ No newline at end of file diff --git a/src/modernnet/network.nim b/src/modernnet/network.nim deleted file mode 100644 index aca31bc..0000000 --- a/src/modernnet/network.nim +++ /dev/null @@ -1,79 +0,0 @@ -#! Copyright 2023 Yu-Vitaqua-fer-Chronos -#! -#! Licensed under the Apache License, Version 2.0 (the "License"); -#! you may not use this file except in compliance with the License. -#! You may obtain a copy of the License at -#! -#! http://www.apache.org/licenses/LICENSE-2.0 -#! -#! Unless required by applicable law or agreed to in writing, software -#! distributed under the License is distributed on an "AS IS" BASIS, -#! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#! See the License for the specific language governing permissions and -#! limitations under the License. - -import std/[ - asyncdispatch, # Used for our asynchronous programming - asyncnet, # Used for the networking - net -] - -import "."/[ - exceptions, # Used for raising errors - constants, # Used for unchanging values - buffer # Used for encoding and decoding -] - -#[ Networking specific procs for serialising and deserialising, not all are implemented here as ]# -#[ the stream-oriented API is preferred ]# -proc write*(s: AsyncSocket | Socket, b: Buffer) {.multisync.} = - ## Writes all data from a buffer to a socket. - var buf = newBuffer() - buf.writeVarNum[:int32](b.buf.len.int32) - await s.send(cast[string](buf.buf & b.buf)) - - -proc readVarNum*[R: int32 | int64](s: AsyncSocket | Socket): Future[R] {.multisync.} = - ## Reads a VarInt or VarLong from a socket. - var - position: int8 = 0 - currentByte: int8 - - while true: - when s is AsyncSocket: - discard await s.recvInto(addr currentByte, 1) - else: - discard s.recv(addr currentByte, 1) - - result = result or ((currentByte.R and SEGMENT_BITS) shl position) - - if (currentByte and CONTINUE_BIT) == 0: - break - - position += 7 - - when R is int32: - if position >= VarIntBits: - raise newException(MnPacketParsingError, "VarInt is too big!") - - elif R is int64: - if position >= VarIntBits: - raise newException(MnPacketParsingError, "VarLong is too big!") - - else: - {.error: "Achievement Made: How did we get here?".} - - -proc readVarInt*(s: AsyncSocket | Socket): Future[int32] {.multisync, deprecated: "Use `readVarNum` instead".} = - ## Reads a VarInt from a socket. - await s.readVarNum[:int32]() - - -proc readVarLong*(s: AsyncSocket | Socket): Future[int64] {.multisync, deprecated: "Use `readVarNum` instead".} = - ## Reads a VarLong from a socket. - await s.readVarNum[:int64]() - - -proc read*(s: AsyncSocket | Socket, size: int): Future[Buffer] {.multisync.} = - ## Reads data from a socket and returns a stream. - newBuffer(cast[seq[byte]](await s.recv(size))) \ No newline at end of file diff --git a/src/modernnet/packets/common.nim b/src/modernnet/packets/common.nim deleted file mode 100644 index 273d59c..0000000 --- a/src/modernnet/packets/common.nim +++ /dev/null @@ -1,24 +0,0 @@ -#! Copyright 2023 Yu-Vitaqua-fer-Chronos -#! -#! Licensed under the Apache License, Version 2.0 (the "License"); -#! you may not use this file except in compliance with the License. -#! You may obtain a copy of the License at -#! -#! http://www.apache.org/licenses/LICENSE-2.0 -#! -#! Unless required by applicable law or agreed to in writing, software -#! distributed under the License is distributed on an "AS IS" BASIS, -#! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#! See the License for the specific language governing permissions and -#! limitations under the License. - -import ".."/buffer - -type Packet* = ref object of RootObj - ## Base packet type that's inherited from - id*: int32 - buffer*: Buffer - -proc new*(_: typedesc[Packet], id: int32, data: seq[byte]): Packet = Packet(id: id, buffer: newBuffer(data)) -proc new*(_: typedesc[Packet], buffer: Buffer): Packet = Packet(id: buffer.readVarNum[:int32](), - buffer: buffer) \ No newline at end of file diff --git a/src/modernnet/constants.nim b/src/modernnet/private/constants.nim similarity index 100% rename from src/modernnet/constants.nim rename to src/modernnet/private/constants.nim diff --git a/src/modernnet/private/utils.nim b/src/modernnet/private/utils.nim new file mode 100644 index 0000000..f9d0f68 --- /dev/null +++ b/src/modernnet/private/utils.nim @@ -0,0 +1,51 @@ +#! Copyright 2024 Yu-Vitaqua-fer-Chronos +#! +#! Licensed under the Apache License, Version 2.0 (the "License"); +#! you may not use this file except in compliance with the License. +#! You may obtain a copy of the License at +#! +#! http://www.apache.org/licenses/LICENSE-2.0 +#! +#! Unless required by applicable law or agreed to in writing, software +#! distributed under the License is distributed on an "AS IS" BASIS, +#! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#! See the License for the specific language governing permissions and +#! limitations under the License. + +import ./stew/endians2 + +type SizedNum* = bool | byte | int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | float32 | float64 + +template unsignedSize(T: typedesc): typedesc = + when sizeof(T) == 1: + uint8 + + elif sizeof(T) == 2: + uint16 + + elif sizeof(T) == 4: + uint32 + + elif sizeof(T) == 8: + uint64 + + else: + {.error: "Deserialisation of `" & $T & "` is not implemented!".} + +proc extract*[T: SizedNum](oa: openArray[byte], _: typedesc[T]): T = + if oa.len < sizeof(T): + raise newException(ValueError, "The buffer was to small to extract a " & $T & '!') + + elif oa.len > sizeof(T): + raise newException(ValueError, "The buffer was to big to extract a " & $T & '!') + + cast[T](unsignedSize(T).fromBytesBE(oa.toOpenArray(0, sizeof(T) - 1))) + +proc deposit*[T: SizedNum](value: T, oa: out openArray[byte]) = + if oa.len < sizeof(T): + raise newException(ValueError, "The buffer was to small to deposit a " & $T & '!') + + let res = cast[unsignedSize(T)](value).toBytesBE() + + for i in 0..