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 | |
11 | struct 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 | |
23 | static SPacket *g_pFirst = (SPacket *)0; |
24 | static SPacket *g_pLast = (SPacket *)0; |
25 | static int g_CurrentLatency = 0; |
26 | |
27 | struct 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 | |
37 | static 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 | |
44 | static int g_ConfigNumpingconfs = std::size(g_aConfigPings); |
45 | static int g_ConfigInterval = 10; // seconds between different pingconfigs |
46 | static int g_ConfigLog = 0; |
47 | static int g_ConfigReorder = 0; |
48 | |
49 | void 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 | |
200 | int 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 | |