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