}
rng_engine.seed(std::chrono::system_clock::now().time_since_epoch().count());
-}
-
-UDPC::Context *UDPC::verifyContext(UDPC_HContext ctx) {
- if(ctx == nullptr) {
- return nullptr;
- }
- UDPC::Context *c = (UDPC::Context *)ctx;
- if(c->_contextIdentifier == UDPC_CONTEXT_IDENTIFIER) {
- return c;
- } else {
- return nullptr;
- }
-}
-
-bool UDPC::isBigEndian() {
- static std::optional<bool> isBigEndian = std::nullopt;
- if(isBigEndian) {
- return *isBigEndian;
- }
- union {
- uint32_t i;
- char c[4];
- } bint = {0x01020304};
-
- isBigEndian = (bint.c[0] == 1);
- return *isBigEndian;
-}
-
-void UDPC::preparePacket(char *data, uint32_t protocolID, uint32_t conID,
- uint32_t rseq, uint32_t ack, uint32_t *seqID,
- int flags) {
- uint32_t temp;
-
- temp = htonl(protocolID);
- std::memcpy(data, &temp, 4);
- temp = htonl(conID | ((flags & 0x1) != 0 ? UDPC_ID_CONNECT : 0) |
- ((flags & 0x2) != 0 ? UDPC_ID_PING : 0) |
- ((flags & 0x4) != 0 ? UDPC_ID_NO_REC_CHK : 0) |
- ((flags & 0x8) != 0 ? UDPC_ID_RESENDING : 0));
- std::memcpy(data + 4, &temp, 4);
-
- if(seqID) {
- temp = htonl(*seqID);
- ++(*seqID);
- } else {
- temp = 0;
- }
- std::memcpy(data + 8, &temp, 4);
-
- temp = htonl(rseq);
- std::memcpy(data + 12, &temp, 4);
- temp = htonl(ack);
- std::memcpy(data + 16, &temp, 4);
-}
-
-uint32_t UDPC::generateConnectionID(Context &ctx) {
- auto dist = std::uniform_int_distribution<uint32_t>(0, 0xFFFFFFFF);
- uint32_t id = dist(ctx.rng_engine);
- while(ctx.idMap.find(id) != ctx.idMap.end()) {
- id = dist(ctx.rng_engine);
- }
- return id;
-}
-
-float UDPC::durationToFSec(const std::chrono::steady_clock::duration& duration) {
- return (float)duration.count()
- * (float)std::decay_t<decltype(duration)>::period::num
- / (float)std::decay_t<decltype(duration)>::period::den;
-}
-
-float UDPC::timePointsToFSec(
- const std::chrono::steady_clock::time_point& older,
- const std::chrono::steady_clock::time_point& newer) {
- const auto dt = newer - older;
- return (float)dt.count()
- * (float)decltype(dt)::period::num / (float)decltype(dt)::period::den;
-}
-
-UDPC_PacketInfo UDPC::get_empty_pinfo() {
- return UDPC_PacketInfo {
- {0}, // data (array)
- 0, // flags
- 0, // dataSize
- { // sender
- {0}, // ipv6 addr
- 0 // port
- },
- { // receiver
- {0}, // ipv6 addr
- 0 // port
- },
- };
-}
-
-UDPC_ConnectionId UDPC_create_id(struct in6_addr addr, uint16_t port) {
- return UDPC_ConnectionId{addr, port};
-}
-
-UDPC_ConnectionId UDPC_create_id_anyaddr(uint16_t port) {
- return UDPC_ConnectionId{in6addr_any, port};
-}
-
-UDPC_HContext UDPC_init(UDPC_ConnectionId listenId, int isClient) {
- UDPC::Context *ctx = new UDPC::Context(false);
- ctx->flags.set(1, isClient);
-
- // create socket
- ctx->socketHandle = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
- if(ctx->socketHandle <= 0) {
- // TODO maybe different way of handling init fail
- delete ctx;
- return nullptr;
- }
-
- // allow ipv4 connections on ipv6 socket
- {
- int no = 0;
- setsockopt(ctx->socketHandle, IPPROTO_IPV6, IPV6_V6ONLY, &no, sizeof(no));
- }
-
- // bind socket
- ctx->socketInfo.sin6_family = AF_INET6;
- ctx->socketInfo.sin6_addr = listenId.addr;
- ctx->socketInfo.sin6_port = htons(listenId.port);
- if(bind(ctx->socketHandle, (const struct sockaddr *)&ctx->socketInfo,
- sizeof(struct sockaddr_in6)) < 0) {
- // TODO maybe different way of handling init fail
- CleanupSocket(ctx->socketHandle);
- delete ctx;
- return nullptr;
- }
-
- // TODO verify this is necessary to get the listen port
- if(ctx->socketInfo.sin6_port == 0) {
- struct sockaddr_in6 getInfo;
- socklen_t size = sizeof(struct sockaddr_in6);
- if(getsockname(ctx->socketHandle, (struct sockaddr *)&getInfo, &size) == 0) {
- ctx->socketInfo.sin6_port = getInfo.sin6_port;
- }
- }
-
- // set non-blocking on socket
-#if UDPC_PLATFORM == UDPC_PLATFORM_MAC || UDPC_PLATFORM == UDPC_PLATFORM_LINUX
- int nonblocking = 1;
- if(fcntl(ctx->socketHandle, F_SETFL, O_NONBLOCK, nonblocking) == -1) {
-#elif UDPC_PLATFORM == UDPC_PLATFORM_WINDOWS
- DWORD nonblocking = 1;
- if(ioctlsocket(ctx->socketHandle, FIONBIO, &nonblocking) != 0) {
-#else
- {
-#endif
- // TODO maybe different way of handling init fail
- CleanupSocket(ctx->socketHandle);
- delete ctx;
- return nullptr;
- }
- return (UDPC_HContext) ctx;
+ threadRunning.store(true);
}
-UDPC_HContext UDPC_init_threaded_update(UDPC_ConnectionId listenId,
- int isClient) {
- UDPC::Context *ctx = (UDPC::Context *)UDPC_init(listenId, isClient);
- if(!ctx) {
- return nullptr;
- }
- ctx->flags.set(0);
-
- return (UDPC_HContext) ctx;
-}
-
-void UDPC_destroy(UDPC_HContext ctx) {
- UDPC::Context *UDPC_ctx = UDPC::verifyContext(ctx);
- if(UDPC_ctx) {
- delete UDPC_ctx;
- }
-}
-
-void UDPC_update(UDPC_HContext ctx) {
- UDPC::Context *c = UDPC::verifyContext(ctx);
- if(!c || c->flags.test(0)) {
- // invalid or is threaded, update should not be called
- return;
- }
-
+void UDPC::Context::update_impl() {
const auto now = std::chrono::steady_clock::now();
- c->lastUpdated = now;
+ lastUpdated = now;
std::chrono::steady_clock::duration temp_dt_fs;
{
// check timed out, check good/bad mode with rtt, remove timed out
std::vector<UDPC_ConnectionId> removed;
- for(auto iter = c->conMap.begin(); iter != c->conMap.end(); ++iter) {
+ for(auto iter = conMap.begin(); iter != conMap.end(); ++iter) {
temp_dt_fs = now - iter->second.received;
if(temp_dt_fs >= UDPC::CONNECTION_TIMEOUT) {
removed.push_back(iter->first);
- c->log(
+ log(
UDPC_LoggingType::VERBOSE,
"Timed out connection with ",
- UDPC_atostr(ctx, iter->first),
+ UDPC_atostr((UDPC_HContext)this, iter->first),
":",
iter->second.port);
continue;
iter->second.toggledTimer += temp_dt_fs;
if(iter->second.flags.test(1) && !iter->second.flags.test(2)) {
// good mode, bad rtt
- c->log(
+ log(
UDPC_LoggingType::INFO,
"Switching to bad mode in connection with ",
- UDPC_atostr(ctx, iter->first),
+ UDPC_atostr((UDPC_HContext)this, iter->first),
":",
iter->second.port);
iter->second.flags.reset(1);
if(iter->second.toggledTimer >= iter->second.toggleT) {
iter->second.toggleTimer = std::chrono::steady_clock::duration::zero();
iter->second.toggledTimer = std::chrono::steady_clock::duration::zero();
- c->log(
+ log(
UDPC_LoggingType::INFO,
"Switching to good mode in connection with ",
- UDPC_atostr(ctx, iter->first),
+ UDPC_atostr((UDPC_HContext)this, iter->first),
":",
iter->second.port);
iter->second.flags.set(1);
}
}
for(auto iter = removed.begin(); iter != removed.end(); ++iter) {
- auto addrConIter = c->addrConMap.find(iter->addr);
- assert(addrConIter != c->addrConMap.end()
+ auto addrConIter = addrConMap.find(iter->addr);
+ assert(addrConIter != addrConMap.end()
&& "addrConMap must have an entry for a current connection");
auto addrConSetIter = addrConIter->second.find(*iter);
assert(addrConSetIter != addrConIter->second.end()
&& "nested set in addrConMap must have an entry for a current connection");
addrConIter->second.erase(addrConSetIter);
if(addrConIter->second.empty()) {
- c->addrConMap.erase(addrConIter);
+ addrConMap.erase(addrConIter);
}
- auto cIter = c->conMap.find(*iter);
- assert(cIter != c->conMap.end()
+ auto cIter = conMap.find(*iter);
+ assert(cIter != conMap.end()
&& "conMap must have the entry set to be removed");
if(cIter->second.flags.test(4)) {
- c->idMap.erase(cIter->second.id);
+ idMap.erase(cIter->second.id);
}
- c->conMap.erase(cIter);
+ conMap.erase(cIter);
}
}
// update send (only if triggerSend flag is set)
- for(auto iter = c->conMap.begin(); iter != c->conMap.end(); ++iter) {
+ for(auto iter = conMap.begin(); iter != conMap.end(); ++iter) {
if(!iter->second.flags.test(0)) {
continue;
}
iter->second.flags.reset(0);
if(iter->second.flags.test(3)) {
- if(c->flags.test(1)) {
+ if(flags.test(1)) {
// is initiating connection to server
auto initDT = now - iter->second.sent;
if(initDT < UDPC::INIT_PKT_INTERVAL_DT) {
std::unique_ptr<char[]> buf = std::make_unique<char[]>(20);
UDPC::preparePacket(
buf.get(),
- c->protocolID,
+ protocolID,
0,
0,
0xFFFFFFFF,
std::memcpy(destinationInfo.sin6_addr.s6_addr, iter->first.addr.s6_addr, 16);
destinationInfo.sin6_port = htons(iter->second.port);
long int sentBytes = sendto(
- c->socketHandle,
+ socketHandle,
buf.get(),
20,
0,
(struct sockaddr*) &destinationInfo,
sizeof(struct sockaddr_in6));
if(sentBytes != 20) {
- c->log(
+ log(
UDPC_LoggingType::ERROR,
"Failed to send packet to initiate connection to ",
- UDPC_atostr(ctx, iter->first),
+ UDPC_atostr((UDPC_HContext)this, iter->first),
":",
iter->second.port);
continue;
std::unique_ptr<char[]> buf = std::make_unique<char[]>(20);
UDPC::preparePacket(
buf.get(),
- c->protocolID,
+ protocolID,
iter->second.id,
iter->second.rseq,
iter->second.ack,
std::memcpy(destinationInfo.sin6_addr.s6_addr, iter->first.addr.s6_addr, 16);
destinationInfo.sin6_port = htons(iter->second.port);
long int sentBytes = sendto(
- c->socketHandle,
+ socketHandle,
buf.get(),
20,
0,
(struct sockaddr*) &destinationInfo,
sizeof(struct sockaddr_in6));
if(sentBytes != 20) {
- c->log(
+ log(
UDPC_LoggingType::ERROR,
"Failed to send packet to initiate connection to ",
- UDPC_atostr(ctx, iter->first),
+ UDPC_atostr((UDPC_HContext)this, iter->first),
":",
iter->second.port);
continue;
std::unique_ptr<char[]> buf = std::make_unique<char[]>(20);
UDPC::preparePacket(
buf.get(),
- c->protocolID,
+ protocolID,
iter->second.id,
iter->second.rseq,
iter->second.ack,
std::memcpy(destinationInfo.sin6_addr.s6_addr, iter->first.addr.s6_addr, 16);
destinationInfo.sin6_port = htons(iter->second.port);
long int sentBytes = sendto(
- c->socketHandle,
+ socketHandle,
buf.get(),
20,
0,
(struct sockaddr*) &destinationInfo,
sizeof(struct sockaddr_in6));
if(sentBytes != 20) {
- c->log(
+ log(
UDPC_LoggingType::ERROR,
"Failed to send heartbeat packet to ",
- UDPC_atostr(ctx, iter->first),
+ UDPC_atostr((UDPC_HContext)this, iter->first),
":",
iter->second.port);
continue;
UDPC_PacketInfo pInfo = UDPC::get_empty_pinfo();
pInfo.sender.addr = in6addr_loopback;
pInfo.receiver.addr = iter->first.addr;
- pInfo.sender.port = c->socketInfo.sin6_port;
+ pInfo.sender.port = socketInfo.sin6_port;
pInfo.receiver.port = iter->second.port;
*((uint32_t*)(pInfo.data + 8)) = iter->second.lseq - 1;
std::unique_ptr<char[]> buf = std::make_unique<char[]>(20 + pInfo.dataSize);
UDPC::preparePacket(
buf.get(),
- c->protocolID,
+ protocolID,
iter->second.id,
iter->second.rseq,
iter->second.ack,
std::memcpy(destinationInfo.sin6_addr.s6_addr, iter->first.addr.s6_addr, 16);
destinationInfo.sin6_port = htons(iter->second.port);
long int sentBytes = sendto(
- c->socketHandle,
+ socketHandle,
buf.get(),
pInfo.dataSize + 20,
0,
(struct sockaddr*) &destinationInfo,
sizeof(struct sockaddr_in6));
if(sentBytes != 20 + pInfo.dataSize) {
- c->log(
+ log(
UDPC_LoggingType::ERROR,
"Failed to send packet to ",
- UDPC_atostr(ctx, iter->first),
+ UDPC_atostr((UDPC_HContext)this, iter->first),
":",
iter->second.port);
continue;
sentPInfo.dataSize = 20 + pInfo.dataSize;
sentPInfo.sender.addr = in6addr_loopback;
sentPInfo.receiver.addr = iter->first.addr;
- sentPInfo.sender.port = c->socketInfo.sin6_port;
+ sentPInfo.sender.port = socketInfo.sin6_port;
sentPInfo.receiver.port = iter->second.port;
iter->second.sentPkts.push_back(std::move(pInfo));
sentPInfo.dataSize = 0;
sentPInfo.sender.addr = in6addr_loopback;
sentPInfo.receiver.addr = iter->first.addr;
- sentPInfo.sender.port = c->socketInfo.sin6_port;
+ sentPInfo.sender.port = socketInfo.sin6_port;
sentPInfo.receiver.port = iter->second.port;
*((uint32_t*)(sentPInfo.data + 8)) = iter->second.lseq - 1;
struct sockaddr_in6 receivedData;
socklen_t receivedDataSize = sizeof(receivedData);
int bytes = recvfrom(
- c->socketHandle,
- c->recvBuf,
+ socketHandle,
+ recvBuf,
UDPC_PACKET_MAX_SIZE,
0,
(struct sockaddr*) &receivedData,
return;
} else if(bytes < 20) {
// packet size is too small, invalid packet
- c->log(
+ log(
UDPC_LoggingType::INFO,
"Received packet is smaller than header, ignoring packet from ",
- UDPC_atostr(ctx, UDPC_ConnectionId{receivedData.sin6_addr, 0}),
+ UDPC_atostr((UDPC_HContext)this, UDPC_ConnectionId{receivedData.sin6_addr, 0}),
":",
receivedData.sin6_port);
return;
}
- uint32_t temp = ntohl(*((uint32_t*)c->recvBuf));
- if(temp != c->protocolID) {
+ uint32_t temp = ntohl(*((uint32_t*)recvBuf));
+ if(temp != protocolID) {
// Invalid protocol id in packet
- c->log(
+ log(
UDPC_LoggingType::INFO,
"Received packet has invalid protocol id, ignoring packet from ",
- UDPC_atostr(ctx, UDPC_ConnectionId{receivedData.sin6_addr, 0}),
+ UDPC_atostr((UDPC_HContext)this, UDPC_ConnectionId{receivedData.sin6_addr, 0}),
":",
receivedData.sin6_port);
return;
}
- uint32_t conID = ntohl(*((uint32_t*)(c->recvBuf + 4)));
- uint32_t seqID = ntohl(*((uint32_t*)(c->recvBuf + 8)));
- uint32_t rseq = ntohl(*((uint32_t*)(c->recvBuf + 12)));
- uint32_t ack = htonl(*((uint32_t*)(c->recvBuf + 16)));
+ uint32_t conID = ntohl(*((uint32_t*)(recvBuf + 4)));
+ uint32_t seqID = ntohl(*((uint32_t*)(recvBuf + 8)));
+ uint32_t rseq = ntohl(*((uint32_t*)(recvBuf + 12)));
+ uint32_t ack = htonl(*((uint32_t*)(recvBuf + 16)));
bool isConnect = conID & UDPC_ID_CONNECT;
bool isPing = conID & UDPC_ID_PING;
UDPC_ConnectionId identifier{receivedData.sin6_addr, ntohs(receivedData.sin6_port)};
- if(isConnect && c->flags.test(2)) {
+ if(isConnect && flags.test(2)) {
// is connect packet and is accepting new connections
- if(!c->flags.test(1)
- && c->conMap.find(identifier) == c->conMap.end()) {
+ if(!flags.test(1)
+ && conMap.find(identifier) == conMap.end()) {
// is receiving as server, connection did not already exist
- c->log(
+ log(
UDPC_LoggingType::VERBOSE,
"Establishing connection with client ",
- UDPC_atostr(ctx, UDPC_ConnectionId{receivedData.sin6_addr, 0}),
+ UDPC_atostr((UDPC_HContext)this, UDPC_ConnectionId{receivedData.sin6_addr, 0}),
":",
receivedData.sin6_port);
- UDPC::ConnectionData newConnection(true, c);
+ UDPC::ConnectionData newConnection(true, this);
newConnection.addr = receivedData.sin6_addr;
newConnection.port = ntohs(receivedData.sin6_port);
- c->idMap.insert(std::make_pair(newConnection.id, identifier));
- c->conMap.insert(std::make_pair(identifier, std::move(newConnection)));
- auto addrConIter = c->addrConMap.find(identifier.addr);
- if(addrConIter == c->addrConMap.end()) {
- auto insertResult = c->addrConMap.insert(
+ idMap.insert(std::make_pair(newConnection.id, identifier));
+ conMap.insert(std::make_pair(identifier, std::move(newConnection)));
+ auto addrConIter = addrConMap.find(identifier.addr);
+ if(addrConIter == addrConMap.end()) {
+ auto insertResult = addrConMap.insert(
std::make_pair(
identifier.addr,
std::unordered_set<UDPC_ConnectionId, UDPC::ConnectionIdHasher>{}
}
addrConIter->second.insert(identifier);
// TODO trigger event server established connection with client
- } else if (c->flags.test(1)) {
+ } else if (flags.test(1)) {
// is client
- auto iter = c->conMap.find(identifier);
- if(iter == c->conMap.end() || !iter->second.flags.test(3)) {
+ auto iter = conMap.find(identifier);
+ if(iter == conMap.end() || !iter->second.flags.test(3)) {
return;
}
iter->second.flags.reset(3);
iter->second.id = conID;
iter->second.flags.set(4);
- c->log(
+ log(
UDPC_LoggingType::VERBOSE,
"Established connection with server ",
- UDPC_atostr(ctx, UDPC_ConnectionId{receivedData.sin6_addr, 0}),
+ UDPC_atostr((UDPC_HContext)this, UDPC_ConnectionId{receivedData.sin6_addr, 0}),
":",
receivedData.sin6_port);
// TODO trigger event client established connection with server
return;
}
- auto iter = c->conMap.find(identifier);
- if(iter == c->conMap.end() || iter->second.flags.test(3)
+ auto iter = conMap.find(identifier);
+ if(iter == conMap.end() || iter->second.flags.test(3)
|| !iter->second.flags.test(4) || iter->second.id != conID) {
return;
}
}
// packet is valid
- c->log(
+ log(
UDPC_LoggingType::INFO,
"Received valid packet from ",
- UDPC_atostr(ctx, UDPC_ConnectionId{receivedData.sin6_addr, 0}),
+ UDPC_atostr((UDPC_HContext)this, UDPC_ConnectionId{receivedData.sin6_addr, 0}),
":",
receivedData.sin6_port);
iter->second.flags.set(2, iter->second.rtt <= UDPC::GOOD_RTT_LIMIT);
- c->log(
+ log(
UDPC_LoggingType::INFO,
"RTT: ",
UDPC::durationToFSec(iter->second.rtt));
auto duration = now - sentInfoIter->second->sentTime;
if(duration > UDPC::PACKET_TIMEOUT_TIME) {
if(sentIter->dataSize <= 20) {
- c->log(
+ log(
UDPC_LoggingType::INFO,
"Timed out packet has no payload (probably "
"heartbeat packet), ignoring it");
diff = 0xFFFFFFFF - seqID + 1 + iter->second.rseq;
if((iter->second.ack & (0x80000000 >> (diff - 1))) != 0) {
// already received packet
- c->log(
+ log(
UDPC_LoggingType::INFO,
"Received packet is already marked as received, ignoring it");
return;
// sequence is older
if((iter->second.ack & (0x80000000 >> (diff - 1))) != 0) {
// already received packet
- c->log(
+ log(
UDPC_LoggingType::INFO,
"Received packet is already marked as received, ignoring it");
return;
}
} else {
// already received packet
- c->log(
+ log(
UDPC_LoggingType::INFO,
"Received packet is already marked as received, ignoring it");
return;
}
if(isOutOfOrder) {
- c->log(
+ log(
UDPC_LoggingType::VERBOSE,
"Received packet is out of order");
}
if(bytes > 20) {
UDPC_PacketInfo recPktInfo = UDPC::get_empty_pinfo();
- std::memcpy(recPktInfo.data, c->recvBuf, bytes);
+ std::memcpy(recPktInfo.data, recvBuf, bytes);
recPktInfo.dataSize = bytes;
recPktInfo.flags =
(isConnect ? 0x1 : 0)
recPktInfo.sender.addr = receivedData.sin6_addr;
recPktInfo.receiver.addr = in6addr_loopback;
recPktInfo.sender.port = receivedData.sin6_port;
- recPktInfo.receiver.port = c->socketInfo.sin6_port;
+ recPktInfo.receiver.port = socketInfo.sin6_port;
if(iter->second.receivedPkts.size() == iter->second.receivedPkts.capacity()) {
- c->log(
+ log(
UDPC_LoggingType::WARNING,
"receivedPkts is full, removing oldest entry to make room");
iter->second.receivedPkts.pop();
iter->second.receivedPkts.push(recPktInfo);
} else if(bytes == 20) {
- c->log(
+ log(
UDPC_LoggingType::VERBOSE,
"Received packet has no payload");
}
}
+UDPC::Context *UDPC::verifyContext(UDPC_HContext ctx) {
+ if(ctx == nullptr) {
+ return nullptr;
+ }
+ UDPC::Context *c = (UDPC::Context *)ctx;
+ if(c->_contextIdentifier == UDPC_CONTEXT_IDENTIFIER) {
+ return c;
+ } else {
+ return nullptr;
+ }
+}
+
+bool UDPC::isBigEndian() {
+ static std::optional<bool> isBigEndian = std::nullopt;
+ if(isBigEndian) {
+ return *isBigEndian;
+ }
+ union {
+ uint32_t i;
+ char c[4];
+ } bint = {0x01020304};
+
+ isBigEndian = (bint.c[0] == 1);
+ return *isBigEndian;
+}
+
+void UDPC::preparePacket(char *data, uint32_t protocolID, uint32_t conID,
+ uint32_t rseq, uint32_t ack, uint32_t *seqID,
+ int flags) {
+ uint32_t temp;
+
+ temp = htonl(protocolID);
+ std::memcpy(data, &temp, 4);
+ temp = htonl(conID | ((flags & 0x1) != 0 ? UDPC_ID_CONNECT : 0) |
+ ((flags & 0x2) != 0 ? UDPC_ID_PING : 0) |
+ ((flags & 0x4) != 0 ? UDPC_ID_NO_REC_CHK : 0) |
+ ((flags & 0x8) != 0 ? UDPC_ID_RESENDING : 0));
+ std::memcpy(data + 4, &temp, 4);
+
+ if(seqID) {
+ temp = htonl(*seqID);
+ ++(*seqID);
+ } else {
+ temp = 0;
+ }
+ std::memcpy(data + 8, &temp, 4);
+
+ temp = htonl(rseq);
+ std::memcpy(data + 12, &temp, 4);
+ temp = htonl(ack);
+ std::memcpy(data + 16, &temp, 4);
+}
+
+uint32_t UDPC::generateConnectionID(Context &ctx) {
+ auto dist = std::uniform_int_distribution<uint32_t>(0, 0xFFFFFFFF);
+ uint32_t id = dist(ctx.rng_engine);
+ while(ctx.idMap.find(id) != ctx.idMap.end()) {
+ id = dist(ctx.rng_engine);
+ }
+ return id;
+}
+
+float UDPC::durationToFSec(const std::chrono::steady_clock::duration& duration) {
+ return (float)duration.count()
+ * (float)std::decay_t<decltype(duration)>::period::num
+ / (float)std::decay_t<decltype(duration)>::period::den;
+}
+
+float UDPC::timePointsToFSec(
+ const std::chrono::steady_clock::time_point& older,
+ const std::chrono::steady_clock::time_point& newer) {
+ const auto dt = newer - older;
+ return (float)dt.count()
+ * (float)decltype(dt)::period::num / (float)decltype(dt)::period::den;
+}
+
+UDPC_PacketInfo UDPC::get_empty_pinfo() {
+ return UDPC_PacketInfo {
+ {0}, // data (array)
+ 0, // flags
+ 0, // dataSize
+ { // sender
+ {0}, // ipv6 addr
+ 0 // port
+ },
+ { // receiver
+ {0}, // ipv6 addr
+ 0 // port
+ },
+ };
+}
+
+void UDPC::threadedUpdate(Context *ctx) {
+ auto now = std::chrono::steady_clock::now();
+ decltype(now) nextNow;
+ while(ctx->threadRunning.load()) {
+ now = std::chrono::steady_clock::now();
+ ctx->update_impl();
+ nextNow = std::chrono::steady_clock::now();
+ std::this_thread::sleep_for(std::chrono::milliseconds(33) - (nextNow - now));
+ }
+}
+
+UDPC_ConnectionId UDPC_create_id(struct in6_addr addr, uint16_t port) {
+ return UDPC_ConnectionId{addr, port};
+}
+
+UDPC_ConnectionId UDPC_create_id_anyaddr(uint16_t port) {
+ return UDPC_ConnectionId{in6addr_any, port};
+}
+
+UDPC_HContext UDPC_init(UDPC_ConnectionId listenId, int isClient) {
+ UDPC::Context *ctx = new UDPC::Context(false);
+ ctx->flags.set(1, isClient);
+
+ // create socket
+ ctx->socketHandle = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if(ctx->socketHandle <= 0) {
+ // TODO maybe different way of handling init fail
+ delete ctx;
+ return nullptr;
+ }
+
+ // allow ipv4 connections on ipv6 socket
+ {
+ int no = 0;
+ setsockopt(ctx->socketHandle, IPPROTO_IPV6, IPV6_V6ONLY, &no, sizeof(no));
+ }
+
+ // bind socket
+ ctx->socketInfo.sin6_family = AF_INET6;
+ ctx->socketInfo.sin6_addr = listenId.addr;
+ ctx->socketInfo.sin6_port = htons(listenId.port);
+ if(bind(ctx->socketHandle, (const struct sockaddr *)&ctx->socketInfo,
+ sizeof(struct sockaddr_in6)) < 0) {
+ // TODO maybe different way of handling init fail
+ CleanupSocket(ctx->socketHandle);
+ delete ctx;
+ return nullptr;
+ }
+
+ // TODO verify this is necessary to get the listen port
+ if(ctx->socketInfo.sin6_port == 0) {
+ struct sockaddr_in6 getInfo;
+ socklen_t size = sizeof(struct sockaddr_in6);
+ if(getsockname(ctx->socketHandle, (struct sockaddr *)&getInfo, &size) == 0) {
+ ctx->socketInfo.sin6_port = getInfo.sin6_port;
+ }
+ }
+
+ // set non-blocking on socket
+#if UDPC_PLATFORM == UDPC_PLATFORM_MAC || UDPC_PLATFORM == UDPC_PLATFORM_LINUX
+ int nonblocking = 1;
+ if(fcntl(ctx->socketHandle, F_SETFL, O_NONBLOCK, nonblocking) == -1) {
+#elif UDPC_PLATFORM == UDPC_PLATFORM_WINDOWS
+ DWORD nonblocking = 1;
+ if(ioctlsocket(ctx->socketHandle, FIONBIO, &nonblocking) != 0) {
+#else
+ {
+#endif
+ // TODO maybe different way of handling init fail
+ CleanupSocket(ctx->socketHandle);
+ delete ctx;
+ return nullptr;
+ }
+
+ return (UDPC_HContext) ctx;
+}
+
+UDPC_HContext UDPC_init_threaded_update(UDPC_ConnectionId listenId,
+ int isClient) {
+ UDPC::Context *ctx = (UDPC::Context *)UDPC_init(listenId, isClient);
+ if(!ctx) {
+ return nullptr;
+ }
+ ctx->flags.set(0);
+ ctx->thread = std::thread(UDPC::threadedUpdate, ctx);
+
+ return (UDPC_HContext) ctx;
+}
+
+void UDPC_destroy(UDPC_HContext ctx) {
+ UDPC::Context *UDPC_ctx = UDPC::verifyContext(ctx);
+ if(UDPC_ctx) {
+ if(UDPC_ctx->flags.test(0)) {
+ UDPC_ctx->threadRunning.store(false);
+ UDPC_ctx->thread.join();
+ }
+ delete UDPC_ctx;
+ }
+}
+
+void UDPC_update(UDPC_HContext ctx) {
+ UDPC::Context *c = UDPC::verifyContext(ctx);
+ if(!c || c->flags.test(0)) {
+ // invalid or is threaded, update should not be called
+ return;
+ }
+
+ c->update_impl();
+}
+
void UDPC_client_initiate_connection(UDPC_HContext ctx, UDPC_ConnectionId connectionId) {
UDPC::Context *c = UDPC::verifyContext(ctx);
if(!c || !c->flags.test(1)) {