Files @ r4381:c965d1f3016a
Branch filter:

Location: cpp/openttd-patchpack/source/network_udp.c - annotation

belugas
(svn r6131) -Codechange : Complete all missing _ttdpatch_flags entries
-Feature : both unifiedmaglevmode are now set.
Maglev and monorail are not allowed to run on each other tracks and will not be.
Setting those flags will allow grfsets as the Norvegian one to be loaded
-Codechange : link the TTDPatch's irregularstations with OTTD's nonuniform_stations
-Codechange : Reformat the whole array (thanks Rubidium, it sure looks better now)
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
r2186:5ee653b1b5e1
r2186:5ee653b1b5e1
r543:efdb197f91ad
r1299:07d5483b3f76
r1317:3783af524858
r543:efdb197f91ad
r4261:8c2d0c75e37a
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r850:c4cda82a6179
r543:efdb197f91ad
r764:b278ee9c19c0
r2153:b45e3461c6c4
r543:efdb197f91ad
r543:efdb197f91ad
r638:40bffd805a4c
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r638:40bffd805a4c
r543:efdb197f91ad
r638:40bffd805a4c
r4344:bff007ae1fd1
r4344:bff007ae1fd1
r4344:bff007ae1fd1
r4344:bff007ae1fd1
r764:b278ee9c19c0
r4344:bff007ae1fd1
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r764:b278ee9c19c0
r2861:2a5e8951e185
r4344:bff007ae1fd1
r4344:bff007ae1fd1
r764:b278ee9c19c0
r543:efdb197f91ad
r543:efdb197f91ad
r2817:7ad46034031f
r543:efdb197f91ad
r2789:3cb9cc8fe3d8
r903:61e47f5f59a3
r638:40bffd805a4c
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r850:c4cda82a6179
r850:c4cda82a6179
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r2879:e5a04d608a3a
r4326:7102aa7f4fb2
r4326:7102aa7f4fb2
r4326:7102aa7f4fb2
r4326:7102aa7f4fb2
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r2944:25bed4e89f1d
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r2944:25bed4e89f1d
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r764:b278ee9c19c0
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r2881:c5bcb1dd0332
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r903:61e47f5f59a3
r543:efdb197f91ad
r2881:c5bcb1dd0332
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r2881:c5bcb1dd0332
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r4326:7102aa7f4fb2
r4326:7102aa7f4fb2
r4326:7102aa7f4fb2
r4326:7102aa7f4fb2
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r4326:7102aa7f4fb2
r4326:7102aa7f4fb2
r4326:7102aa7f4fb2
r4326:7102aa7f4fb2
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r543:efdb197f91ad
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r2775:3e6efcedc84d
r2879:e5a04d608a3a
r2879:e5a04d608a3a
r2881:c5bcb1dd0332
r2881:c5bcb1dd0332
r4077:259c4c4aacad
r4077:259c4c4aacad
r4077:259c4c4aacad
r2879:e5a04d608a3a
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r638:40bffd805a4c
r638:40bffd805a4c
r716:ae3edbaa50e7
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r4077:259c4c4aacad
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r2944:25bed4e89f1d
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r4077:259c4c4aacad
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r4077:259c4c4aacad
r638:40bffd805a4c
r638:40bffd805a4c
r4326:7102aa7f4fb2
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r4077:259c4c4aacad
r1011:51e33a299a51
r4077:259c4c4aacad
r1011:51e33a299a51
r4077:259c4c4aacad
r1019:90da3857ff29
r1019:90da3857ff29
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r4077:259c4c4aacad
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r4326:7102aa7f4fb2
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r4077:259c4c4aacad
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r4326:7102aa7f4fb2
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r4077:259c4c4aacad
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r4326:7102aa7f4fb2
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r4077:259c4c4aacad
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r4326:7102aa7f4fb2
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r764:b278ee9c19c0
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r638:40bffd805a4c
r4077:259c4c4aacad
r4077:259c4c4aacad
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r903:61e47f5f59a3
r903:61e47f5f59a3
r903:61e47f5f59a3
r903:61e47f5f59a3
r764:b278ee9c19c0
r764:b278ee9c19c0
r903:61e47f5f59a3
r903:61e47f5f59a3
r903:61e47f5f59a3
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r4077:259c4c4aacad
r4077:259c4c4aacad
r764:b278ee9c19c0
r765:0c323ba44a44
r765:0c323ba44a44
r4077:259c4c4aacad
r765:0c323ba44a44
r765:0c323ba44a44
r4077:259c4c4aacad
r764:b278ee9c19c0
r764:b278ee9c19c0
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r638:40bffd805a4c
r543:efdb197f91ad
r638:40bffd805a4c
r638:40bffd805a4c
r668:c0236804c7cc
r764:b278ee9c19c0
r764:b278ee9c19c0
r765:0c323ba44a44
r765:0c323ba44a44
r543:efdb197f91ad
r543:efdb197f91ad
r764:b278ee9c19c0
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r2817:7ad46034031f
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r903:61e47f5f59a3
r903:61e47f5f59a3
r903:61e47f5f59a3
r543:efdb197f91ad
r903:61e47f5f59a3
r903:61e47f5f59a3
r903:61e47f5f59a3
r543:efdb197f91ad
r3547:ccc3d5684050
r3547:ccc3d5684050
r3547:ccc3d5684050
r3547:ccc3d5684050
r3547:ccc3d5684050
r3547:ccc3d5684050
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r2817:7ad46034031f
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r764:b278ee9c19c0
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r764:b278ee9c19c0
r764:b278ee9c19c0
r543:efdb197f91ad
r764:b278ee9c19c0
r764:b278ee9c19c0
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r2497:4f8fde59a2e8
r764:b278ee9c19c0
r2497:4f8fde59a2e8
r2524:32c2a040967b
r2497:4f8fde59a2e8
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r764:b278ee9c19c0
r629:806084a01de5
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r764:b278ee9c19c0
r764:b278ee9c19c0
r543:efdb197f91ad
r2497:4f8fde59a2e8
r764:b278ee9c19c0
r2497:4f8fde59a2e8
r543:efdb197f91ad
r543:efdb197f91ad
r629:806084a01de5
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r764:b278ee9c19c0
r543:efdb197f91ad
r543:efdb197f91ad
r985:8ad134f58255
r985:8ad134f58255
r985:8ad134f58255
r985:8ad134f58255
r764:b278ee9c19c0
r985:8ad134f58255
r985:8ad134f58255
r985:8ad134f58255
r985:8ad134f58255
r764:b278ee9c19c0
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r985:8ad134f58255
r985:8ad134f58255
r985:8ad134f58255
r985:8ad134f58255
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r764:b278ee9c19c0
r543:efdb197f91ad
r543:efdb197f91ad
r1343:ea7fb505ae74
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r4077:259c4c4aacad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r2817:7ad46034031f
r543:efdb197f91ad
r4077:259c4c4aacad
r4077:259c4c4aacad
r543:efdb197f91ad
r4077:259c4c4aacad
r4077:259c4c4aacad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r4034:5118c28087cc
r4034:5118c28087cc
r4034:5118c28087cc
r543:efdb197f91ad
r764:b278ee9c19c0
r543:efdb197f91ad
r4035:f931c00c6c00
r4035:f931c00c6c00
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r764:b278ee9c19c0
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r764:b278ee9c19c0
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r1329:88de95f6e7d3
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r764:b278ee9c19c0
r738:f65fde6f029e
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r3550:39e3b0af0f1a
r3550:39e3b0af0f1a
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r638:40bffd805a4c
r543:efdb197f91ad
r764:b278ee9c19c0
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r738:f65fde6f029e
r543:efdb197f91ad
r543:efdb197f91ad
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r1832:c0ba10abbd05
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r805:9d095497ea45
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r765:0c323ba44a44
r668:c0236804c7cc
r668:c0236804c7cc
r716:ae3edbaa50e7
r668:c0236804c7cc
r668:c0236804c7cc
r668:c0236804c7cc
r668:c0236804c7cc
r668:c0236804c7cc
r668:c0236804c7cc
r668:c0236804c7cc
r668:c0236804c7cc
r764:b278ee9c19c0
r764:b278ee9c19c0
r805:9d095497ea45
r764:b278ee9c19c0
r764:b278ee9c19c0
r2861:2a5e8951e185
r2861:2a5e8951e185
r2861:2a5e8951e185
r2861:2a5e8951e185
r2861:2a5e8951e185
r2861:2a5e8951e185
r2861:2a5e8951e185
r2861:2a5e8951e185
r2861:2a5e8951e185
r2861:2a5e8951e185
r2861:2a5e8951e185
r2861:2a5e8951e185
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r764:b278ee9c19c0
r2861:2a5e8951e185
r668:c0236804c7cc
r668:c0236804c7cc
r668:c0236804c7cc
r668:c0236804c7cc
r668:c0236804c7cc
r668:c0236804c7cc
r668:c0236804c7cc
r668:c0236804c7cc
r668:c0236804c7cc
r668:c0236804c7cc
r668:c0236804c7cc
r668:c0236804c7cc
r668:c0236804c7cc
r668:c0236804c7cc
r764:b278ee9c19c0
r764:b278ee9c19c0
r668:c0236804c7cc
r668:c0236804c7cc
r668:c0236804c7cc
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r764:b278ee9c19c0
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
r543:efdb197f91ad
/* $Id$ */

#include "stdafx.h"
#include "debug.h"
#include "string.h"
#include "network_data.h"
#include "date.h"

#ifdef ENABLE_NETWORK

#include "map.h"
#include "network_gamelist.h"
#include "network_udp.h"
#include "variables.h"

extern void UpdateNetworkGameWindow(bool unselect);
extern void NetworkPopulateCompanyInfo(void);

//
// This file handles all the LAN-stuff
// Stuff like:
//   - UDP search over the network
//

typedef enum {
	PACKET_UDP_CLIENT_FIND_SERVER,
	PACKET_UDP_SERVER_RESPONSE,
	PACKET_UDP_CLIENT_DETAIL_INFO,
	PACKET_UDP_SERVER_DETAIL_INFO,   // Is not used in OpenTTD itself, only for external querying
	PACKET_UDP_SERVER_REGISTER,      // Packet to register itself to the master server
	PACKET_UDP_MASTER_ACK_REGISTER,  // Packet indicating registration has succedeed
	PACKET_UDP_CLIENT_GET_LIST,      // Request for serverlist from master server
	PACKET_UDP_MASTER_RESPONSE_LIST, // Response from master server with server ip's + port's
	PACKET_UDP_SERVER_UNREGISTER,    // Request to be removed from the server-list
	PACKET_UDP_END
} PacketUDPType;

enum {
	ADVERTISE_NORMAL_INTERVAL = 30000, // interval between advertising in ticks (15 minutes)
	ADVERTISE_RETRY_INTERVAL  =   300, // readvertise when no response after this many ticks (9 seconds)
	ADVERTISE_RETRY_TIMES     =     3  // give up readvertising after this much failed retries
};

#define DEF_UDP_RECEIVE_COMMAND(type) void NetworkPacketReceive_ ## type ## _command(Packet *p, struct sockaddr_in *client_addr)
static void NetworkSendUDP_Packet(SOCKET udp, Packet* p, struct sockaddr_in* recv);

static NetworkClientState _udp_cs;

DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_FIND_SERVER)
{
	Packet *packet;
	// Just a fail-safe.. should never happen
	if (!_network_udp_server)
		return;

	packet = NetworkSend_Init(PACKET_UDP_SERVER_RESPONSE);

	// Update some game_info
	_network_game_info.game_date = _date;
	_network_game_info.map_width = MapSizeX();
	_network_game_info.map_height = MapSizeY();
	_network_game_info.map_set = _opt.landscape;

	NetworkSend_uint8 (packet, NETWORK_GAME_INFO_VERSION);

	/* NETWORK_GAME_INFO_VERSION = 3 */
	NetworkSend_uint32(packet, _network_game_info.game_date);
	NetworkSend_uint32(packet, _network_game_info.start_date);

	/* NETWORK_GAME_INFO_VERSION = 2 */
	NetworkSend_uint8 (packet, _network_game_info.companies_max);
	NetworkSend_uint8 (packet, ActivePlayerCount());
	NetworkSend_uint8 (packet, _network_game_info.spectators_max);

	/* NETWORK_GAME_INFO_VERSION = 1 */
	NetworkSend_string(packet, _network_game_info.server_name);
	NetworkSend_string(packet, _network_game_info.server_revision);
	NetworkSend_uint8 (packet, _network_game_info.server_lang);
	NetworkSend_uint8 (packet, _network_game_info.use_password);
	NetworkSend_uint8 (packet, _network_game_info.clients_max);
	NetworkSend_uint8 (packet, _network_game_info.clients_on);
	NetworkSend_uint8 (packet, NetworkSpectatorCount());
	NetworkSend_string(packet, _network_game_info.map_name);
	NetworkSend_uint16(packet, _network_game_info.map_width);
	NetworkSend_uint16(packet, _network_game_info.map_height);
	NetworkSend_uint8 (packet, _network_game_info.map_set);
	NetworkSend_uint8 (packet, _network_game_info.dedicated);

	// Let the client know that we are here
	NetworkSendUDP_Packet(_udp_server_socket, packet, client_addr);

	free(packet);

	DEBUG(net, 2)("[NET][UDP] Queried from %s", inet_ntoa(client_addr->sin_addr));
}

DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVER_RESPONSE)
{
	extern const char _openttd_revision[];
	NetworkGameList *item;
	byte game_info_version;

	// Just a fail-safe.. should never happen
	if (_network_udp_server)
		return;

	game_info_version = NetworkRecv_uint8(&_udp_cs, p);

	if (_udp_cs.quited) return;

	DEBUG(net, 6)("[NET][UDP] Server response from %s:%d", inet_ntoa(client_addr->sin_addr),ntohs(client_addr->sin_port));

	// Find next item
	item = NetworkGameListAddItem(inet_addr(inet_ntoa(client_addr->sin_addr)), ntohs(client_addr->sin_port));

	/* Please observer the order. In the order in which packets are sent
	 * they are to be received */
	switch (game_info_version) {
		case 3:
			item->info.game_date     = NetworkRecv_uint32(&_udp_cs, p);
			item->info.start_date    = NetworkRecv_uint32(&_udp_cs, p);
			/* Fallthrough */
		case 2:
			item->info.companies_max = NetworkRecv_uint8(&_udp_cs, p);
			item->info.companies_on = NetworkRecv_uint8(&_udp_cs, p);
			item->info.spectators_max = NetworkRecv_uint8(&_udp_cs, p);
			/* Fallthrough */
		case 1:
			NetworkRecv_string(&_udp_cs, p, item->info.server_name, sizeof(item->info.server_name));
			NetworkRecv_string(&_udp_cs, p, item->info.server_revision, sizeof(item->info.server_revision));
			item->info.server_lang   = NetworkRecv_uint8(&_udp_cs, p);
			item->info.use_password  = NetworkRecv_uint8(&_udp_cs, p);
			item->info.clients_max   = NetworkRecv_uint8(&_udp_cs, p);
			item->info.clients_on    = NetworkRecv_uint8(&_udp_cs, p);
			item->info.spectators_on = NetworkRecv_uint8(&_udp_cs, p);
			if (game_info_version < 3) { // 16 bits dates got scrapped and are read earlier
				item->info.game_date     = NetworkRecv_uint16(&_udp_cs, p) + DAYS_TILL_ORIGINAL_BASE_YEAR;
				item->info.start_date    = NetworkRecv_uint16(&_udp_cs, p) + DAYS_TILL_ORIGINAL_BASE_YEAR;
			}
			NetworkRecv_string(&_udp_cs, p, item->info.map_name, sizeof(item->info.map_name));
			item->info.map_width     = NetworkRecv_uint16(&_udp_cs, p);
			item->info.map_height    = NetworkRecv_uint16(&_udp_cs, p);
			item->info.map_set       = NetworkRecv_uint8(&_udp_cs, p);
			item->info.dedicated     = NetworkRecv_uint8(&_udp_cs, p);

			if (item->info.server_lang >= NETWORK_NUM_LANGUAGES) item->info.server_lang = 0;
			if (item->info.map_set >= NUM_LANDSCAPE ) item->info.map_set = 0;

			if (item->info.hostname[0] == '\0')
				snprintf(item->info.hostname, sizeof(item->info.hostname), "%s", inet_ntoa(client_addr->sin_addr));

			/* Check if we are allowed on this server based on the revision-match */
			item->info.compatible =
				strcmp(item->info.server_revision, _openttd_revision) == 0 ||
				strcmp(item->info.server_revision, NOREV_STRING) == 0;
			break;
	}

	item->online = true;

	UpdateNetworkGameWindow(false);
}

DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_DETAIL_INFO)
{
	NetworkClientState *cs;
	NetworkClientInfo *ci;
	Packet *packet;
	Player *player;
	byte current = 0;
	int i;

	// Just a fail-safe.. should never happen
	if (!_network_udp_server) return;

	packet = NetworkSend_Init(PACKET_UDP_SERVER_DETAIL_INFO);

	/* Send the amount of active companies */
	NetworkSend_uint8 (packet, NETWORK_COMPANY_INFO_VERSION);
	NetworkSend_uint8 (packet, ActivePlayerCount());

	/* Fetch the latest version of everything */
	NetworkPopulateCompanyInfo();

	/* Go through all the players */
	FOR_ALL_PLAYERS(player) {
		/* Skip non-active players */
		if (!player->is_active) continue;

		current++;

		/* Send the information */
		NetworkSend_uint8(packet, current);

		NetworkSend_string(packet, _network_player_info[player->index].company_name);
		NetworkSend_uint32(packet, _network_player_info[player->index].inaugurated_year);
		NetworkSend_uint64(packet, _network_player_info[player->index].company_value);
		NetworkSend_uint64(packet, _network_player_info[player->index].money);
		NetworkSend_uint64(packet, _network_player_info[player->index].income);
		NetworkSend_uint16(packet, _network_player_info[player->index].performance);

		/* Send 1 if there is a passord for the company else send 0 */
		if (_network_player_info[player->index].password[0] != '\0') {
			NetworkSend_uint8(packet, 1);
		} else {
			NetworkSend_uint8(packet, 0);
		}

		for (i = 0; i < NETWORK_VEHICLE_TYPES; i++)
			NetworkSend_uint16(packet, _network_player_info[player->index].num_vehicle[i]);

		for (i = 0; i < NETWORK_STATION_TYPES; i++)
			NetworkSend_uint16(packet, _network_player_info[player->index].num_station[i]);

		/* Find the clients that are connected to this player */
		FOR_ALL_CLIENTS(cs) {
			ci = DEREF_CLIENT_INFO(cs);
			if (ci->client_playas - 1 == player->index) {
				/* The uint8 == 1 indicates that a client is following */
				NetworkSend_uint8(packet, 1);
				NetworkSend_string(packet, ci->client_name);
				NetworkSend_string(packet, ci->unique_id);
				NetworkSend_uint32(packet, ci->join_date);
			}
		}
		/* Also check for the server itself */
		ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
		if (ci->client_playas - 1 == player->index) {
			/* The uint8 == 1 indicates that a client is following */
			NetworkSend_uint8(packet, 1);
			NetworkSend_string(packet, ci->client_name);
			NetworkSend_string(packet, ci->unique_id);
			NetworkSend_uint32(packet, ci->join_date);
		}

		/* Indicates end of client list */
		NetworkSend_uint8(packet, 0);
	}

	/* And check if we have any spectators */
	FOR_ALL_CLIENTS(cs) {
		ci = DEREF_CLIENT_INFO(cs);
		if (ci->client_playas - 1 > MAX_PLAYERS) {
			/* The uint8 == 1 indicates that a client is following */
			NetworkSend_uint8(packet, 1);
			NetworkSend_string(packet, ci->client_name);
			NetworkSend_string(packet, ci->unique_id);
			NetworkSend_uint32(packet, ci->join_date);
		}
	}
	/* Also check for the server itself */
	ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
	if (ci->client_playas - 1 > MAX_PLAYERS) {
		/* The uint8 == 1 indicates that a client is following */
		NetworkSend_uint8(packet, 1);
		NetworkSend_string(packet, ci->client_name);
		NetworkSend_string(packet, ci->unique_id);
		NetworkSend_uint32(packet, ci->join_date);
	}

	/* Indicates end of client list */
	NetworkSend_uint8(packet, 0);

	NetworkSendUDP_Packet(_udp_server_socket, packet, client_addr);

	free(packet);
}

DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_MASTER_RESPONSE_LIST)
{
	int i;
	struct in_addr ip;
	uint16 port;
	uint8 ver;

	/* packet begins with the protocol version (uint8)
	 * then an uint16 which indicates how many
	 * ip:port pairs are in this packet, after that
	 * an uint32 (ip) and an uint16 (port) for each pair
	 */

	ver = NetworkRecv_uint8(&_udp_cs, p);

	if (_udp_cs.quited)
		return;

	if (ver == 1) {
		for (i = NetworkRecv_uint16(&_udp_cs, p); i != 0 ; i--) {
			ip.s_addr = TO_LE32(NetworkRecv_uint32(&_udp_cs, p));
			port = NetworkRecv_uint16(&_udp_cs, p);
			NetworkUDPQueryServer(inet_ntoa(ip), port);
		}
	}
}

DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_MASTER_ACK_REGISTER)
{
	_network_advertise_retries = 0;
	DEBUG(net, 2)("[NET][UDP] We are advertised on the master-server!");

	if (!_network_advertise) {
		/* We are advertised, but we don't want to! */
		NetworkUDPRemoveAdvertise();
	}
}


// The layout for the receive-functions by UDP
typedef void NetworkUDPPacket(Packet *p, struct sockaddr_in *client_addr);

static NetworkUDPPacket* const _network_udp_packet[] = {
	RECEIVE_COMMAND(PACKET_UDP_CLIENT_FIND_SERVER),
	RECEIVE_COMMAND(PACKET_UDP_SERVER_RESPONSE),
	RECEIVE_COMMAND(PACKET_UDP_CLIENT_DETAIL_INFO),
	NULL,
	NULL,
	RECEIVE_COMMAND(PACKET_UDP_MASTER_ACK_REGISTER),
	NULL,
	RECEIVE_COMMAND(PACKET_UDP_MASTER_RESPONSE_LIST),
	NULL
};


// If this fails, check the array above with network_data.h
assert_compile(lengthof(_network_udp_packet) == PACKET_UDP_END);


static void NetworkHandleUDPPacket(Packet* p, struct sockaddr_in* client_addr)
{
	byte type;

	/* Fake a client, so we can see when there is an illegal packet */
	_udp_cs.socket = INVALID_SOCKET;
	_udp_cs.quited = false;

	type = NetworkRecv_uint8(&_udp_cs, p);

	if (type < PACKET_UDP_END && _network_udp_packet[type] != NULL && !_udp_cs.quited) {
		_network_udp_packet[type](p, client_addr);
	} else {
		if (!_udp_cs.quited) {
			DEBUG(net, 0)("[NET][UDP] Received invalid packet type %d", type);
		} else {
			DEBUG(net, 0)("[NET][UDP] Received illegal packet");
		}
	}
}


// Send a packet over UDP
static void NetworkSendUDP_Packet(SOCKET udp, Packet* p, struct sockaddr_in* recv)
{
	int res;

	// Put the length in the buffer
	p->buffer[0] = p->size & 0xFF;
	p->buffer[1] = p->size >> 8;

	// Send the buffer
	res = sendto(udp, p->buffer, p->size, 0, (struct sockaddr *)recv, sizeof(*recv));

	// Check for any errors, but ignore it for the rest
	if (res == -1) {
		DEBUG(net, 1)("[NET][UDP] Send error: %i", GET_LAST_ERROR());
	}
}

// Start UDP listener
bool NetworkUDPListen(SOCKET *udp, uint32 host, uint16 port, bool broadcast)
{
	struct sockaddr_in sin;

	// Make sure socket is closed
	closesocket(*udp);

	*udp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (*udp == INVALID_SOCKET) {
		DEBUG(net, 1)("[NET][UDP] Failed to start UDP support");
		return false;
	}

	// set nonblocking mode for socket
	{
		unsigned long blocking = 1;
#ifndef BEOS_NET_SERVER
		ioctlsocket(*udp, FIONBIO, &blocking);
#else
		setsockopt(*udp, SOL_SOCKET, SO_NONBLOCK, &blocking, NULL);
#endif
	}

	sin.sin_family = AF_INET;
	// Listen on all IPs
	sin.sin_addr.s_addr = host;
	sin.sin_port = htons(port);

	if (bind(*udp, (struct sockaddr*)&sin, sizeof(sin)) != 0) {
		DEBUG(net, 1) ("[NET][UDP] error: bind failed on %s:%i", inet_ntoa(*(struct in_addr *)&host), port);
		return false;
	}

	if (broadcast) {
		/* Enable broadcast */
		unsigned long val = 1;
#ifndef BEOS_NET_SERVER // will work around this, some day; maybe.
		setsockopt(*udp, SOL_SOCKET, SO_BROADCAST, (char *) &val , sizeof(val));
#endif
	}

	DEBUG(net, 1)("[NET][UDP] Listening on port %s:%d", inet_ntoa(*(struct in_addr *)&host), port);

	return true;
}

// Close UDP connection
void NetworkUDPClose(void)
{
	DEBUG(net, 1) ("[NET][UDP] Closed listeners");

	if (_network_udp_server) {
		if (_udp_server_socket != INVALID_SOCKET) {
			closesocket(_udp_server_socket);
			_udp_server_socket = INVALID_SOCKET;
		}

		if (_udp_master_socket != INVALID_SOCKET) {
			closesocket(_udp_master_socket);
			_udp_master_socket = INVALID_SOCKET;
		}

		_network_udp_server = false;
		_network_udp_broadcast = 0;
	} else {
		if (_udp_client_socket != INVALID_SOCKET) {
			closesocket(_udp_client_socket);
			_udp_client_socket = INVALID_SOCKET;
		}
		_network_udp_broadcast = 0;
	}
}

// Receive something on UDP level
void NetworkUDPReceive(SOCKET udp)
{
	struct sockaddr_in client_addr;
	socklen_t client_len;
	int nbytes;
	static Packet *p = NULL;
	int packet_len;

	// If p is NULL, malloc him.. this prevents unneeded mallocs
	if (p == NULL) p = malloc(sizeof(*p));

	packet_len = sizeof(p->buffer);
	client_len = sizeof(client_addr);

	// Try to receive anything
	nbytes = recvfrom(udp, p->buffer, packet_len, 0, (struct sockaddr *)&client_addr, &client_len);

	// We got some bytes.. just asume we receive the whole packet
	if (nbytes > 0) {
		// Get the size of the buffer
		p->size = (uint16)p->buffer[0];
		p->size += (uint16)p->buffer[1] << 8;
		// Put the position on the right place
		p->pos = 2;
		p->next = NULL;

		// Handle the packet
		NetworkHandleUDPPacket(p, &client_addr);

		// Free the packet
		free(p);
		p = NULL;
	}
}

// Broadcast to all ips
static void NetworkUDPBroadCast(SOCKET udp)
{
	Packet* p = NetworkSend_Init(PACKET_UDP_CLIENT_FIND_SERVER);
	uint i;

	for (i = 0; _broadcast_list[i] != 0; i++) {
		struct sockaddr_in out_addr;

		out_addr.sin_family = AF_INET;
		out_addr.sin_port = htons(_network_server_port);
		out_addr.sin_addr.s_addr = _broadcast_list[i];

		DEBUG(net, 6)("[NET][UDP] Broadcasting to %s", inet_ntoa(out_addr.sin_addr));

		NetworkSendUDP_Packet(udp, p, &out_addr);
	}

	free(p);
}


// Request the the server-list from the master server
void NetworkUDPQueryMasterServer(void)
{
	struct sockaddr_in out_addr;
	Packet *p;

	if (_udp_client_socket == INVALID_SOCKET)
		if (!NetworkUDPListen(&_udp_client_socket, 0, 0, true))
			return;

	p = NetworkSend_Init(PACKET_UDP_CLIENT_GET_LIST);

	out_addr.sin_family = AF_INET;
	out_addr.sin_port = htons(NETWORK_MASTER_SERVER_PORT);
	out_addr.sin_addr.s_addr = NetworkResolveHost(NETWORK_MASTER_SERVER_HOST);

	// packet only contains protocol version
	NetworkSend_uint8(p, NETWORK_MASTER_SERVER_VERSION);

	NetworkSendUDP_Packet(_udp_client_socket, p, &out_addr);

	DEBUG(net, 2)("[NET][UDP] Queried Master Server at %s:%d", inet_ntoa(out_addr.sin_addr),ntohs(out_addr.sin_port));

	free(p);
}

// Find all servers
void NetworkUDPSearchGame(void)
{
	// We are still searching..
	if (_network_udp_broadcast > 0)
		return;

	// No UDP-socket yet..
	if (_udp_client_socket == INVALID_SOCKET)
		if (!NetworkUDPListen(&_udp_client_socket, 0, 0, true))
			return;

	DEBUG(net, 0)("[NET][UDP] Searching server");

	NetworkUDPBroadCast(_udp_client_socket);
	_network_udp_broadcast = 300; // Stay searching for 300 ticks
}

NetworkGameList *NetworkUDPQueryServer(const char* host, unsigned short port)
{
	struct sockaddr_in out_addr;
	Packet *p;
	NetworkGameList *item;

	// No UDP-socket yet..
	if (_udp_client_socket == INVALID_SOCKET)
		if (!NetworkUDPListen(&_udp_client_socket, 0, 0, true))
			return NULL;

	out_addr.sin_family = AF_INET;
	out_addr.sin_port = htons(port);
	out_addr.sin_addr.s_addr = NetworkResolveHost(host);

	// Clear item in gamelist
	item = NetworkGameListAddItem(inet_addr(inet_ntoa(out_addr.sin_addr)), ntohs(out_addr.sin_port));
	memset(&item->info, 0, sizeof(item->info));
	ttd_strlcpy(item->info.server_name, host, lengthof(item->info.server_name));
	ttd_strlcpy(item->info.hostname, host, lengthof(item->info.hostname));
	item->online = false;

	// Init the packet
	p = NetworkSend_Init(PACKET_UDP_CLIENT_FIND_SERVER);

	NetworkSendUDP_Packet(_udp_client_socket, p, &out_addr);

	free(p);

	UpdateNetworkGameWindow(false);
	return item;
}

/* Remove our advertise from the master-server */
void NetworkUDPRemoveAdvertise(void)
{
	struct sockaddr_in out_addr;
	Packet *p;

	/* Check if we are advertising */
	if (!_networking || !_network_server || !_network_udp_server)
		return;

	/* check for socket */
	if (_udp_master_socket == INVALID_SOCKET)
		if (!NetworkUDPListen(&_udp_master_socket, _network_server_bind_ip, 0, false))
			return;

	DEBUG(net, 2)("[NET][UDP] Removing advertise..");

	/* Find somewhere to send */
	out_addr.sin_family = AF_INET;
	out_addr.sin_port = htons(NETWORK_MASTER_SERVER_PORT);
	out_addr.sin_addr.s_addr = NetworkResolveHost(NETWORK_MASTER_SERVER_HOST);

	/* Send the packet */
	p = NetworkSend_Init(PACKET_UDP_SERVER_UNREGISTER);
	/* Packet is: Version, server_port */
	NetworkSend_uint8(p, NETWORK_MASTER_SERVER_VERSION);
	NetworkSend_uint16(p, _network_server_port);
	NetworkSendUDP_Packet(_udp_master_socket, p, &out_addr);

	free(p);
}

/* Register us to the master server
     This function checks if it needs to send an advertise */
void NetworkUDPAdvertise(void)
{
	struct sockaddr_in out_addr;
	Packet *p;

	/* Check if we should send an advertise */
	if (!_networking || !_network_server || !_network_udp_server || !_network_advertise)
		return;

	/* check for socket */
	if (_udp_master_socket == INVALID_SOCKET)
		if (!NetworkUDPListen(&_udp_master_socket, _network_server_bind_ip, 0, false))
			return;

	if (_network_need_advertise) {
		_network_need_advertise = false;
		_network_advertise_retries = ADVERTISE_RETRY_TIMES;
	} else {
		/* Only send once every ADVERTISE_NORMAL_INTERVAL ticks */
		if (_network_advertise_retries == 0) {
			if ((_network_last_advertise_frame + ADVERTISE_NORMAL_INTERVAL) > _frame_counter)
				return;
			_network_advertise_retries = ADVERTISE_RETRY_TIMES;
		}

		if ((_network_last_advertise_frame + ADVERTISE_RETRY_INTERVAL) > _frame_counter)
			return;
	}

	_network_advertise_retries--;
	_network_last_advertise_frame = _frame_counter;

	/* Find somewhere to send */
	out_addr.sin_family = AF_INET;
	out_addr.sin_port = htons(NETWORK_MASTER_SERVER_PORT);
	out_addr.sin_addr.s_addr = NetworkResolveHost(NETWORK_MASTER_SERVER_HOST);

	DEBUG(net, 1)("[NET][UDP] Advertising to master server");

	/* Send the packet */
	p = NetworkSend_Init(PACKET_UDP_SERVER_REGISTER);
	/* Packet is: WELCOME_MESSAGE, Version, server_port */
	NetworkSend_string(p, NETWORK_MASTER_SERVER_WELCOME_MESSAGE);
	NetworkSend_uint8(p, NETWORK_MASTER_SERVER_VERSION);
	NetworkSend_uint16(p, _network_server_port);
	NetworkSendUDP_Packet(_udp_master_socket, p, &out_addr);

	free(p);
}

void NetworkUDPInitialize(void)
{
	_udp_client_socket = INVALID_SOCKET;
	_udp_server_socket = INVALID_SOCKET;
	_udp_master_socket = INVALID_SOCKET;

	_network_udp_server = false;
	_network_udp_broadcast = 0;
}

#endif /* ENABLE_NETWORK */