#define UDPC_DEFINES_HPP
#define UDPC_CONTEXT_IDENTIFIER 0x902F4DB3
+#define UDPC_TIMEOUT_SECONDS 10.0f
#include <atomic>
#include <bitset>
#include <cstdint>
+#include <deque>
+#include <chrono>
+#include <unordered_map>
#include "UDPConnection.h"
+#include "TSQueue.hpp"
namespace UDPC {
+struct ConnectionData {
+ /*
+ * 0 - trigger send
+ * 1 - is good mode
+ * 2 - is good rtt
+ * 3 - initiating connection to server
+ * 4 - is id set
+ */
+ std::bitset<32> flags;
+ uint32_t id;
+ uint32_t lseq;
+ uint32_t rseq;
+ uint32_t ack;
+ float timer;
+ float toggleT;
+ float toggleTimer;
+ float toggledTimer;
+ uint32_t addr; // in network order
+ uint16_t port;
+ std::deque<PacketInfo> sentPkts;
+ TSQueue<PacketInfo> sendPkts;
+ TSQueue<PacketInfo> priorityPkts;
+ std::chrono::steady_clock::time_point received;
+ std::chrono::steady_clock::time_point sent;
+ float rtt;
+}; // struct ConnectionData
+
struct Context {
Context(bool isThreaded);
std::atomic_uint32_t protocolID;
std::atomic_uint_fast8_t loggingType;
char atostrBuf[16];
+
+ int socketHandle;
+ struct sockaddr_in socketInfo;
+
+ std::chrono::steady_clock::time_point lastUpdated;
+ std::unordered_map<uint32_t, ConnectionData> conMap;
}; // struct Context
Context* verifyContext(void *ctx);
#include "UDPC_Defines.hpp"
#include "UDPConnection.h"
+#include <chrono>
#include <optional>
+#include <vector>
UDPC::Context::Context(bool isThreaded)
: _contextIdentifier(UDPC_CONTEXT_IDENTIFIER), flags(),
void *UDPC_init(uint16_t listenPort, uint32_t listenAddr, int isClient) {
UDPC::Context *ctx = new UDPC::Context(false);
+ // create socket
+ ctx->socketHandle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if(ctx->socketHandle <= 0) {
+ // TODO maybe different way of handling init fail
+ delete ctx;
+ return nullptr;
+ }
+
+ // bind socket
+ ctx->socketInfo.sin_family = AF_INET;
+ ctx->socketInfo.sin_addr.s_addr =
+ (listenAddr == 0 ? INADDR_ANY : listenAddr);
+ ctx->socketInfo.sin_port = htons(listenPort);
+ if(bind(ctx->socketHandle, (const struct sockaddr *)&ctx->socketInfo,
+ sizeof(struct sockaddr_in)) < 0) {
+ // TODO maybe different way of handling init fail
+ CleanupSocket(ctx->socketHandle);
+ delete ctx;
+ return nullptr;
+ }
+
+ // 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 ctx;
}
void *UDPC_init_threaded_update(uint16_t listenPort, uint32_t listenAddr,
int isClient) {
- UDPC::Context *ctx = new UDPC::Context(true);
+ UDPC::Context *ctx =
+ (UDPC::Context *)UDPC_init(listenPort, listenAddr, isClient);
+ if(!ctx) {
+ return nullptr;
+ }
+ ctx->flags.set(0);
return ctx;
}
void UDPC_update(void *ctx) {
UDPC::Context *c = UDPC::verifyContext(ctx);
- if(!c) {
+ if(!c || c->flags.test(0)) {
+ // invalid or is threaded, update should not be called
return;
}
- if(c->flags.test(0)) {
- // is threaded, update should not be called
- return;
+
+ const auto now = std::chrono::steady_clock::now();
+ const auto dt = now - c->lastUpdated;
+ const float dt_fs = (float)dt.count() * (float)decltype(dt)::period::num /
+ (float)decltype(dt)::period::den;
+
+ std::chrono::steady_clock::duration temp_dt;
+ float temp_dt_fs;
+ std::vector<uint32_t> removed;
+ for(auto iter = c->conMap.begin(); iter != c->conMap.end(); ++iter) {
+ temp_dt = now - iter->second.received;
+ temp_dt_fs = (float)temp_dt.count() *
+ (float)decltype(temp_dt)::period::num /
+ (float)decltype(temp_dt)::period::den;
+ if(temp_dt_fs >= UDPC_TIMEOUT_SECONDS) {
+ removed.push_back(iter->first);
+ // TODO log timed out connection
+ }
+
+ // check good/bad mode
+ iter->second.toggleTimer += temp_dt_fs;
+ iter->second.toggledTimer += temp_dt_fs;
+ if(iter->second.flags.test(1) && !iter->second.flags.test(2)) {
+ // good mode, bad rtt
+ // TODO
+ }
}
// TODO impl