mirror of https://github.com/jagt/clumsy.git
135 lines
4.0 KiB
C
135 lines
4.0 KiB
C
// lagging packets
|
|
#include "iup.h"
|
|
#include "common.h"
|
|
#define NAME "lag"
|
|
#define LAG_MIN "0"
|
|
#define LAG_MAX "3000"
|
|
#define KEEP_AT_MOST 2000
|
|
// send FLUSH_WHEN_FULL packets when buffer is full
|
|
#define FLUSH_WHEN_FULL 800
|
|
#define LAG_DEFAULT 50
|
|
|
|
// don't need a chance
|
|
static Ihandle *inboundCheckbox, *outboundCheckbox, *timeInput;
|
|
|
|
static volatile short lagEnabled = 0,
|
|
lagInbound = 1,
|
|
lagOutbound = 1,
|
|
lagTime = LAG_DEFAULT; // default for 50ms
|
|
|
|
static PacketNode lagHeadNode = {0}, lagTailNode = {0};
|
|
static PacketNode *bufHead = &lagHeadNode, *bufTail = &lagTailNode;
|
|
static int bufSize = 0;
|
|
|
|
static INLINE_FUNCTION short isBufEmpty() {
|
|
short ret = bufHead->next == bufTail;
|
|
if (ret) assert(bufSize == 0);
|
|
return ret;
|
|
}
|
|
|
|
static Ihandle *lagSetupUI() {
|
|
Ihandle *lagControlsBox = IupHbox(
|
|
inboundCheckbox = IupToggle("Inbound", NULL),
|
|
outboundCheckbox = IupToggle("Outbound", NULL),
|
|
IupLabel("Delay(ms):"),
|
|
timeInput = IupText(NULL),
|
|
NULL
|
|
);
|
|
|
|
IupSetAttribute(timeInput, "VISIBLECOLUMNS", "4");
|
|
IupSetAttribute(timeInput, "VALUE", STR(LAG_DEFAULT));
|
|
IupSetCallback(timeInput, "VALUECHANGED_CB", uiSyncInteger);
|
|
IupSetAttribute(timeInput, SYNCED_VALUE, (char*)&lagTime);
|
|
IupSetAttribute(timeInput, INTEGER_MAX, LAG_MAX);
|
|
IupSetAttribute(timeInput, INTEGER_MIN, LAG_MIN);
|
|
IupSetCallback(inboundCheckbox, "ACTION", (Icallback)uiSyncToggle);
|
|
IupSetAttribute(inboundCheckbox, SYNCED_VALUE, (char*)&lagInbound);
|
|
IupSetCallback(outboundCheckbox, "ACTION", (Icallback)uiSyncToggle);
|
|
IupSetAttribute(outboundCheckbox, SYNCED_VALUE, (char*)&lagOutbound);
|
|
|
|
// enable by default to avoid confusing
|
|
IupSetAttribute(inboundCheckbox, "VALUE", "ON");
|
|
IupSetAttribute(outboundCheckbox, "VALUE", "ON");
|
|
|
|
if (parameterized) {
|
|
setFromParameter(inboundCheckbox, "VALUE", NAME"-inbound");
|
|
setFromParameter(outboundCheckbox, "VALUE", NAME"-outbound");
|
|
setFromParameter(timeInput, "VALUE", NAME"-time");
|
|
}
|
|
|
|
return lagControlsBox;
|
|
}
|
|
|
|
static void lagStartUp() {
|
|
if (bufHead->next == NULL && bufTail->next == NULL) {
|
|
bufHead->next = bufTail;
|
|
bufTail->prev = bufHead;
|
|
bufSize = 0;
|
|
} else {
|
|
assert(isBufEmpty());
|
|
}
|
|
startTimePeriod();
|
|
}
|
|
|
|
static void lagCloseDown(PacketNode *head, PacketNode *tail) {
|
|
PacketNode *oldLast = tail->prev;
|
|
UNREFERENCED_PARAMETER(head);
|
|
// flush all buffered packets
|
|
LOG("Closing down lag, flushing %d packets", bufSize);
|
|
while(!isBufEmpty()) {
|
|
insertAfter(popNode(bufTail->prev), oldLast);
|
|
--bufSize;
|
|
}
|
|
endTimePeriod();
|
|
}
|
|
|
|
static short lagProcess(PacketNode *head, PacketNode *tail) {
|
|
DWORD currentTime = timeGetTime();
|
|
PacketNode *pac = tail->prev;
|
|
// pick up all packets and fill in the current time
|
|
while (bufSize < KEEP_AT_MOST && pac != head) {
|
|
if (checkDirection(pac->addr.Outbound, lagInbound, lagOutbound)) {
|
|
insertAfter(popNode(pac), bufHead)->timestamp = timeGetTime();
|
|
++bufSize;
|
|
pac = tail->prev;
|
|
} else {
|
|
pac = pac->prev;
|
|
}
|
|
}
|
|
|
|
// try sending overdue packets from buffer tail
|
|
while (!isBufEmpty()) {
|
|
pac = bufTail->prev;
|
|
if (currentTime > pac->timestamp + lagTime) {
|
|
insertAfter(popNode(bufTail->prev), head); // sending queue is already empty by now
|
|
--bufSize;
|
|
LOG("Send lagged packets.");
|
|
} else {
|
|
LOG("Sent some lagged packets, still have %d in buf", bufSize);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if buffer is full just flush things out
|
|
if (bufSize >= KEEP_AT_MOST) {
|
|
int flushCnt = FLUSH_WHEN_FULL;
|
|
while (flushCnt-- > 0) {
|
|
insertAfter(popNode(bufTail->prev), head);
|
|
--bufSize;
|
|
}
|
|
}
|
|
|
|
return bufSize > 0;
|
|
}
|
|
|
|
Module lagModule = {
|
|
"Lag",
|
|
NAME,
|
|
(short*)&lagEnabled,
|
|
lagSetupUI,
|
|
lagStartUp,
|
|
lagCloseDown,
|
|
lagProcess,
|
|
// runtime fields
|
|
0, 0, NULL
|
|
}; |