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
|
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file network_gamelist.cpp This file handles the GameList
* Also, it handles the request to a server for data about the server
*/
#ifdef ENABLE_NETWORK
#include "../stdafx.h"
#include "../debug.h"
#include "../core/alloc_func.hpp"
#include "../thread.h"
#include "../string_func.h"
#include "network_internal.h"
#include "network_udp.h"
#include "network_gamelist.h"
NetworkGameList *_network_game_list = NULL;
ThreadMutex *_network_game_list_mutex = ThreadMutex::New();
NetworkGameList *_network_game_delayed_insertion_list = NULL;
/** Add a new item to the linked gamelist, but do it delayed in the next tick
* or so to prevent race conditions.
* @param item the item to add. Will be freed once added.
*/
void NetworkGameListAddItemDelayed(NetworkGameList *item)
{
_network_game_list_mutex->BeginCritical();
item->next = _network_game_delayed_insertion_list;
_network_game_delayed_insertion_list = item;
_network_game_list_mutex->EndCritical();
}
/** Perform the delayed (thread safe) insertion into the game list */
static void NetworkGameListHandleDelayedInsert()
{
_network_game_list_mutex->BeginCritical();
while (_network_game_delayed_insertion_list != NULL) {
NetworkGameList *ins_item = _network_game_delayed_insertion_list;
_network_game_delayed_insertion_list = ins_item->next;
NetworkGameList *item = NetworkGameListAddItem(ins_item->address);
if (item != NULL) {
ClearGRFConfigList(&item->info.grfconfig);
if (StrEmpty(item->info.server_name)) {
memset(&item->info, 0, sizeof(item->info));
strecpy(item->info.server_name, ins_item->info.server_name, lastof(item->info.server_name));
strecpy(item->info.hostname, ins_item->info.hostname, lastof(item->info.hostname));
item->online = false;
}
item->manually |= ins_item->manually;
if (item->manually) NetworkRebuildHostList();
UpdateNetworkGameWindow(false);
}
free(ins_item);
}
_network_game_list_mutex->EndCritical();
}
/** Add a new item to the linked gamelist. If the IP and Port match
* return the existing item instead of adding it again
* @param address the address of the to-be added item
* @param port the port the server is running on
* @return a point to the newly added or already existing item */
NetworkGameList *NetworkGameListAddItem(NetworkAddress address)
{
const char *hostname = address.GetHostname();
/* Do not query the 'any' address. */
if (StrEmpty(hostname) ||
strcmp(hostname, "0.0.0.0") == 0 ||
strcmp(hostname, "::") == 0) {
return NULL;
}
NetworkGameList *item, *prev_item;
prev_item = NULL;
for (item = _network_game_list; item != NULL; item = item->next) {
if (item->address == address) return item;
prev_item = item;
}
item = CallocT<NetworkGameList>(1);
item->next = NULL;
item->address = address;
if (prev_item == NULL) {
_network_game_list = item;
} else {
prev_item->next = item;
}
DEBUG(net, 4, "[gamelist] added server to list");
UpdateNetworkGameWindow(false);
return item;
}
/** Remove an item from the gamelist linked list
* @param remove pointer to the item to be removed */
void NetworkGameListRemoveItem(NetworkGameList *remove)
{
NetworkGameList *prev_item = NULL;
for (NetworkGameList *item = _network_game_list; item != NULL; item = item->next) {
if (remove == item) {
if (prev_item == NULL) {
_network_game_list = remove->next;
} else {
prev_item->next = remove->next;
}
/* Remove GRFConfig information */
ClearGRFConfigList(&remove->info.grfconfig);
free(remove);
remove = NULL;
DEBUG(net, 4, "[gamelist] removed server from list");
NetworkRebuildHostList();
UpdateNetworkGameWindow(false);
return;
}
prev_item = item;
}
}
enum {
MAX_GAME_LIST_REQUERY_COUNT = 10, ///< How often do we requery in number of times per server?
REQUERY_EVERY_X_GAMELOOPS = 60, ///< How often do we requery in time?
REFRESH_GAMEINFO_X_REQUERIES = 50, ///< Refresh the game info itself after REFRESH_GAMEINFO_X_REQUERIES * REQUERY_EVERY_X_GAMELOOPS game loops
};
/** Requeries the (game) servers we have not gotten a reply from */
void NetworkGameListRequery()
{
NetworkGameListHandleDelayedInsert();
static uint8 requery_cnt = 0;
if (++requery_cnt < REQUERY_EVERY_X_GAMELOOPS) return;
requery_cnt = 0;
for (NetworkGameList *item = _network_game_list; item != NULL; item = item->next) {
item->retries++;
if (item->retries < REFRESH_GAMEINFO_X_REQUERIES && (item->online || item->retries >= MAX_GAME_LIST_REQUERY_COUNT)) continue;
/* item gets mostly zeroed by NetworkUDPQueryServer */
uint8 retries = item->retries;
NetworkUDPQueryServer(NetworkAddress(item->address));
item->retries = (retries >= REFRESH_GAMEINFO_X_REQUERIES) ? 0 : retries;
}
}
#endif /* ENABLE_NETWORK */
|