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