1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#include <base/logger.h>
4#include <base/system.h>
5
6#include <cstdlib>
7#include <iterator> // std::size
8#include <thread>
9
10struct SPacket
11{
12 SPacket *m_pPrev;
13 SPacket *m_pNext;
14
15 NETADDR m_SendTo;
16 int64_t m_Timestamp;
17 int m_Id;
18 int m_DataSize;
19 char m_aData[1];
20};
21
22static SPacket *g_pFirst = (SPacket *)nullptr;
23static SPacket *g_pLast = (SPacket *)nullptr;
24static int g_CurrentLatency = 0;
25
26struct SPingConfig
27{
28 int m_Base;
29 int m_Flux;
30 int m_Spike;
31 int m_Loss;
32 int m_Delay;
33 int m_DelayFreq;
34};
35
36static SPingConfig g_aConfigPings[] = {
37 // base flux spike loss delay delayfreq
38 {.m_Base: 0, .m_Flux: 0, .m_Spike: 0, .m_Loss: 0, .m_Delay: 0, .m_DelayFreq: 0},
39 {.m_Base: 40, .m_Flux: 20, .m_Spike: 100, .m_Loss: 0, .m_Delay: 0, .m_DelayFreq: 0},
40 {.m_Base: 140, .m_Flux: 40, .m_Spike: 200, .m_Loss: 0, .m_Delay: 0, .m_DelayFreq: 0},
41};
42
43static int g_ConfigNumpingconfs = std::size(g_aConfigPings);
44static int g_ConfigInterval = 10; // seconds between different pingconfigs
45static int g_ConfigLog = 0;
46static int g_ConfigReorder = 0;
47
48static void Run(unsigned short Port, NETADDR Dest)
49{
50 NETADDR Src = {.type: NETTYPE_IPV4, .ip: {0, 0, 0, 0}, .port: Port};
51 NETSOCKET Socket = net_udp_create(bindaddr: Src);
52
53 int Id = 0;
54 int Delaycounter = 0;
55
56 while(true)
57 {
58 static int s_Lastcfg = 0;
59 int n = ((time_get() / time_freq()) / g_ConfigInterval) % g_ConfigNumpingconfs;
60 SPingConfig Ping = g_aConfigPings[n];
61
62 if(n != s_Lastcfg)
63 dbg_msg(sys: "crapnet", fmt: "cfg = %d", n);
64 s_Lastcfg = n;
65
66 // handle incoming packets
67 while(true)
68 {
69 // fetch data
70 int DataTrash = 0;
71 NETADDR From;
72 unsigned char *pData;
73 int Bytes = net_udp_recv(sock: Socket, addr: &From, data: &pData);
74 if(Bytes <= 0)
75 break;
76
77 if((rand() % 100) < Ping.m_Loss) // drop the packet
78 {
79 if(g_ConfigLog)
80 dbg_msg(sys: "crapnet", fmt: "dropped packet");
81 continue;
82 }
83
84 // create new packet
85 SPacket *p = (SPacket *)malloc(size: sizeof(SPacket) + Bytes);
86
87 if(net_addr_comp(a: &From, b: &Dest) == 0)
88 p->m_SendTo = Src; // from the server
89 else
90 {
91 Src = From; // from the client
92 p->m_SendTo = Dest;
93 }
94
95 // queue packet
96 p->m_pPrev = g_pLast;
97 p->m_pNext = nullptr;
98 if(g_pLast)
99 g_pLast->m_pNext = p;
100 else
101 {
102 g_pFirst = p;
103 g_pLast = p;
104 }
105 g_pLast = p;
106
107 // set data in packet
108 p->m_Timestamp = time_get();
109 p->m_DataSize = Bytes;
110 p->m_Id = Id++;
111 mem_copy(dest: p->m_aData, source: pData, size: Bytes);
112
113 if(Id > 20 && Bytes > 6 && DataTrash)
114 {
115 p->m_aData[6 + (rand() % (Bytes - 6))] = rand() & 255; // modify a byte
116 if((rand() % 10) == 0)
117 {
118 p->m_DataSize -= rand() % 32;
119 if(p->m_DataSize < 6)
120 p->m_DataSize = 6;
121 }
122 }
123
124 if(Delaycounter <= 0)
125 {
126 if(Ping.m_Delay)
127 p->m_Timestamp += (time_freq() * 1000) / Ping.m_Delay;
128 Delaycounter = Ping.m_DelayFreq;
129 }
130 Delaycounter--;
131
132 if(g_ConfigLog)
133 {
134 char aAddrStr[NETADDR_MAXSTRSIZE];
135 net_addr_str(addr: &From, string: aAddrStr, max_length: sizeof(aAddrStr), add_port: true);
136 dbg_msg(sys: "crapnet", fmt: "<< %08d %s (%d)", p->m_Id, aAddrStr, p->m_DataSize);
137 }
138 }
139
140 SPacket *pNext = g_pFirst;
141 while(true)
142 {
143 SPacket *p = pNext;
144 if(!p)
145 break;
146 pNext = p->m_pNext;
147
148 if((time_get() - p->m_Timestamp) > g_CurrentLatency)
149 {
150 char aFlags[] = " ";
151
152 if(g_ConfigReorder && (rand() % 2) == 0 && p->m_pNext)
153 {
154 aFlags[0] = 'R';
155 p = g_pFirst->m_pNext;
156 }
157
158 if(p->m_pNext)
159 p->m_pNext->m_pPrev = p->m_pPrev;
160 else
161 g_pLast = p->m_pPrev;
162
163 if(p->m_pPrev)
164 p->m_pPrev->m_pNext = p->m_pNext;
165 else
166 g_pFirst = p->m_pNext;
167
168 // send and remove packet
169 net_udp_send(sock: Socket, addr: &p->m_SendTo, data: p->m_aData, size: p->m_DataSize);
170
171 // update lag
172 double Flux = rand() / (double)RAND_MAX;
173 int MsSpike = Ping.m_Spike;
174 int MsFlux = Ping.m_Flux;
175 int MsPing = Ping.m_Base;
176 g_CurrentLatency = ((time_freq() * MsPing) / 1000) + (int64_t)(((time_freq() * MsFlux) / 1000) * Flux); // 50ms
177
178 if(MsSpike && (p->m_Id % 100) == 0)
179 {
180 g_CurrentLatency += (time_freq() * MsSpike) / 1000;
181 aFlags[1] = 'S';
182 }
183
184 if(g_ConfigLog)
185 {
186 char aAddrStr[NETADDR_MAXSTRSIZE];
187 net_addr_str(addr: &p->m_SendTo, string: aAddrStr, max_length: sizeof(aAddrStr), add_port: true);
188 dbg_msg(sys: "crapnet", fmt: ">> %08d %s (%d) %s", p->m_Id, aAddrStr, p->m_DataSize, aFlags);
189 }
190
191 free(ptr: p);
192 }
193 }
194
195 std::this_thread::sleep_for(rtime: std::chrono::microseconds(1000));
196 }
197}
198
199int main(int argc, const char **argv)
200{
201 CCmdlineFix CmdlineFix(&argc, &argv);
202 log_set_global_logger_default();
203 NETADDR Addr = {.type: NETTYPE_IPV4, .ip: {127, 0, 0, 1}, .port: 8303};
204 Run(Port: 8302, Dest: Addr);
205 return 0;
206}
207