|
@@ -509,98 +509,101 @@ void NetworkCloseClient(NetworkClientSta
|
|
|
char str[100];
|
|
|
char client_name[NETWORK_NAME_LENGTH];
|
|
|
NetworkClientState *new_cs;
|
|
|
|
|
|
NetworkGetClientName(client_name, sizeof(client_name), cs);
|
|
|
|
|
|
GetString(str, STR_NETWORK_ERR_CLIENT_GENERAL + errorno);
|
|
|
|
|
|
NetworkTextMessage(NETWORK_ACTION_LEAVE, 1, false, client_name, "%s", str);
|
|
|
|
|
|
// Inform other clients of this... strange leaving ;)
|
|
|
FOR_ALL_CLIENTS(new_cs) {
|
|
|
if (new_cs->status > STATUS_AUTH && cs != new_cs) {
|
|
|
SEND_COMMAND(PACKET_SERVER_ERROR_QUIT)(new_cs, cs->index, errorno);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* When the client was PRE_ACTIVE, the server was in pause mode, so unpause */
|
|
|
if (cs->status == STATUS_PRE_ACTIVE && _network_pause_on_join) {
|
|
|
DoCommandP(0, 0, 0, NULL, CMD_PAUSE);
|
|
|
NetworkServer_HandleChat(NETWORK_ACTION_CHAT, DESTTYPE_BROADCAST, 0, "Game unpaused", NETWORK_SERVER_INDEX);
|
|
|
}
|
|
|
|
|
|
closesocket(cs->socket);
|
|
|
cs->writable = false;
|
|
|
cs->quited = true;
|
|
|
|
|
|
// Free all pending and partially received packets
|
|
|
while (cs->packet_queue != NULL) {
|
|
|
Packet *p = cs->packet_queue->next;
|
|
|
free(cs->packet_queue);
|
|
|
cs->packet_queue = p;
|
|
|
}
|
|
|
free(cs->packet_recv);
|
|
|
cs->packet_recv = NULL;
|
|
|
|
|
|
while (cs->command_queue != NULL) {
|
|
|
CommandPacket *p = cs->command_queue->next;
|
|
|
free(cs->command_queue);
|
|
|
cs->command_queue = p;
|
|
|
}
|
|
|
|
|
|
// Close the gap in the client-list
|
|
|
ci = DEREF_CLIENT_INFO(cs);
|
|
|
|
|
|
if (_network_server) {
|
|
|
// We just lost one client :(
|
|
|
if (cs->status > STATUS_INACTIVE)
|
|
|
if (cs->status > STATUS_INACTIVE) {
|
|
|
_network_game_info.clients_on--;
|
|
|
if (DEREF_CLIENT_INFO(cs)->client_playas == OWNER_SPECTATOR)
|
|
|
_network_game_info.spectators_on--;
|
|
|
}
|
|
|
_network_clients_connected--;
|
|
|
|
|
|
while ((cs + 1) != DEREF_CLIENT(MAX_CLIENTS) && (cs + 1)->socket != INVALID_SOCKET) {
|
|
|
*cs = *(cs + 1);
|
|
|
*ci = *(ci + 1);
|
|
|
cs++;
|
|
|
ci++;
|
|
|
}
|
|
|
|
|
|
InvalidateWindow(WC_CLIENT_LIST, 0);
|
|
|
}
|
|
|
|
|
|
// Reset the status of the last socket
|
|
|
cs->socket = INVALID_SOCKET;
|
|
|
cs->status = STATUS_INACTIVE;
|
|
|
cs->index = NETWORK_EMPTY_INDEX;
|
|
|
ci->client_index = NETWORK_EMPTY_INDEX;
|
|
|
}
|
|
|
|
|
|
// A client wants to connect to a server
|
|
|
static bool NetworkConnect(const char *hostname, int port)
|
|
|
{
|
|
|
SOCKET s;
|
|
|
struct sockaddr_in sin;
|
|
|
|
|
|
DEBUG(net, 1) ("[NET] Connecting to %s %d", hostname, port);
|
|
|
|
|
|
s = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
if (s == INVALID_SOCKET) {
|
|
|
ClientStartError("socket() failed");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if (!SetNoDelay(s))
|
|
|
DEBUG(net, 1)("[NET] Setting TCP_NODELAY failed");
|
|
|
|
|
|
sin.sin_family = AF_INET;
|
|
|
sin.sin_addr.s_addr = NetworkResolveHost(hostname);
|
|
|
sin.sin_port = htons(port);
|
|
|
_network_last_host_ip = sin.sin_addr.s_addr;
|
|
|
|
|
|
if (connect(s, (struct sockaddr*) &sin, sizeof(sin)) != 0) {
|
|
|
// We failed to connect for which reason what so ever
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if (!SetNonBlocking(s))
|
|
|
DEBUG(net, 0)("[NET] Setting non-blocking failed"); // XXX should this be an error?
|
|
@@ -739,99 +742,98 @@ static bool NetworkListen(void)
|
|
|
if (listen(ls, 1) != 0) {
|
|
|
ServerStartError("listen() failed");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
_listensocket = ls;
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
// Close all current connections
|
|
|
static void NetworkClose(void)
|
|
|
{
|
|
|
NetworkClientState *cs;
|
|
|
|
|
|
FOR_ALL_CLIENTS(cs) {
|
|
|
if (!_network_server) {
|
|
|
SEND_COMMAND(PACKET_CLIENT_QUIT)("leaving");
|
|
|
NetworkSend_Packets(cs);
|
|
|
}
|
|
|
NetworkCloseClient(cs);
|
|
|
}
|
|
|
|
|
|
if (_network_server) {
|
|
|
// We are a server, also close the listensocket
|
|
|
closesocket(_listensocket);
|
|
|
_listensocket = INVALID_SOCKET;
|
|
|
DEBUG(net, 1) ("[NET] Closed listener");
|
|
|
NetworkUDPClose();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Inits the network (cleans sockets and stuff)
|
|
|
static void NetworkInitialize(void)
|
|
|
{
|
|
|
NetworkClientState *cs;
|
|
|
|
|
|
_local_command_queue = NULL;
|
|
|
|
|
|
// Clean all client-sockets
|
|
|
memset(_clients, 0, sizeof(_clients));
|
|
|
for (cs = _clients; cs != &_clients[MAX_CLIENTS]; cs++) {
|
|
|
cs->socket = INVALID_SOCKET;
|
|
|
cs->status = STATUS_INACTIVE;
|
|
|
cs->command_queue = NULL;
|
|
|
}
|
|
|
|
|
|
// Clean the client_info memory
|
|
|
memset(_network_client_info, 0, sizeof(_network_client_info));
|
|
|
memset(_network_player_info, 0, sizeof(_network_player_info));
|
|
|
_network_lobby_company_count = 0;
|
|
|
memset(&_network_client_info, 0, sizeof(_network_client_info));
|
|
|
memset(&_network_player_info, 0, sizeof(_network_player_info));
|
|
|
|
|
|
_sync_frame = 0;
|
|
|
_network_first_time = true;
|
|
|
|
|
|
_network_reconnect = 0;
|
|
|
|
|
|
NetworkUDPInitialize();
|
|
|
}
|
|
|
|
|
|
// Query a server to fetch his game-info
|
|
|
// If game_info is true, only the gameinfo is fetched,
|
|
|
// else only the client_info is fetched
|
|
|
NetworkGameList *NetworkQueryServer(const char* host, unsigned short port, bool game_info)
|
|
|
{
|
|
|
if (!_network_available) return NULL;
|
|
|
|
|
|
NetworkDisconnect();
|
|
|
|
|
|
if (game_info) {
|
|
|
return NetworkUDPQueryServer(host, port);
|
|
|
}
|
|
|
|
|
|
NetworkInitialize();
|
|
|
|
|
|
_network_server = false;
|
|
|
|
|
|
// Try to connect
|
|
|
_networking = NetworkConnect(host, port);
|
|
|
|
|
|
// ttd_strlcpy(_network_last_host, host, sizeof(_network_last_host));
|
|
|
// _network_last_port = port;
|
|
|
|
|
|
// We are connected
|
|
|
if (_networking) {
|
|
|
SEND_COMMAND(PACKET_CLIENT_COMPANY_INFO)();
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
// No networking, close everything down again
|
|
|
NetworkDisconnect();
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
/* Validates an address entered as a string and adds the server to
|
|
|
* the list. If you use this functions, the games will be marked
|
|
|
* as manually added. */
|
|
|
void NetworkAddServer(const char *b)
|
|
|
{
|
|
@@ -869,106 +871,111 @@ void NetworkRebuildHostList(void)
|
|
|
_network_host_list[i++] = str_fmt("%s:%i", item->info.hostname, item->port);
|
|
|
item = item->next;
|
|
|
}
|
|
|
|
|
|
for (; i < lengthof(_network_host_list); i++) {
|
|
|
_network_host_list[i] = strdup("");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Used by clients, to connect to a server
|
|
|
bool NetworkClientConnectGame(const char* host, unsigned short port)
|
|
|
{
|
|
|
if (!_network_available) return false;
|
|
|
|
|
|
if (port == 0) return false;
|
|
|
|
|
|
ttd_strlcpy(_network_last_host, host, sizeof(_network_last_host));
|
|
|
_network_last_port = port;
|
|
|
|
|
|
NetworkDisconnect();
|
|
|
NetworkUDPClose();
|
|
|
NetworkInitialize();
|
|
|
|
|
|
// Try to connect
|
|
|
_networking = NetworkConnect(host, port);
|
|
|
|
|
|
// We are connected
|
|
|
if (_networking) {
|
|
|
IConsoleCmdExec("exec scripts/on_client.scr 0");
|
|
|
NetworkClient_Connected();
|
|
|
} else {
|
|
|
// Connecting failed
|
|
|
NetworkError(STR_NETWORK_ERR_NOCONNECTION);
|
|
|
}
|
|
|
|
|
|
return _networking;
|
|
|
}
|
|
|
|
|
|
static void NetworkInitGameInfo(void)
|
|
|
{
|
|
|
NetworkClientInfo *ci;
|
|
|
|
|
|
ttd_strlcpy(_network_game_info.server_name, _network_server_name, sizeof(_network_game_info.server_name));
|
|
|
ttd_strlcpy(_network_game_info.server_password, _network_server_password, sizeof(_network_server_password));
|
|
|
ttd_strlcpy(_network_game_info.rcon_password, _network_rcon_password, sizeof(_network_rcon_password));
|
|
|
if (_network_game_info.server_name[0] == '\0')
|
|
|
snprintf(_network_game_info.server_name, sizeof(_network_game_info.server_name), "Unnamed Server");
|
|
|
|
|
|
ttd_strlcpy(_network_game_info.server_revision, _openttd_revision, sizeof(_network_game_info.server_revision));
|
|
|
|
|
|
// The server is a client too ;)
|
|
|
if (_network_dedicated) {
|
|
|
_network_game_info.clients_on = 0;
|
|
|
_network_game_info.companies_on = 0;
|
|
|
_network_game_info.dedicated = true;
|
|
|
} else {
|
|
|
_network_game_info.clients_on = 1;
|
|
|
_network_game_info.companies_on = 1;
|
|
|
_network_game_info.dedicated = false;
|
|
|
}
|
|
|
ttd_strlcpy(_network_game_info.server_revision, _openttd_revision, sizeof(_network_game_info.server_revision));
|
|
|
|
|
|
_network_game_info.spectators_on = 0;
|
|
|
|
|
|
_network_game_info.game_date = _date;
|
|
|
_network_game_info.start_date = ConvertIntDate(_patches.starting_date);
|
|
|
_network_game_info.map_width = MapSizeX();
|
|
|
_network_game_info.map_height = MapSizeY();
|
|
|
_network_game_info.map_set = _opt.landscape;
|
|
|
|
|
|
_network_game_info.use_password = (_network_server_password[0] != '\0');
|
|
|
|
|
|
// We use _network_client_info[MAX_CLIENT_INFO - 1] to store the server-data in it
|
|
|
// The index is NETWORK_SERVER_INDEX ( = 1)
|
|
|
ci = &_network_client_info[MAX_CLIENT_INFO - 1];
|
|
|
memset(ci, 0, sizeof(*ci));
|
|
|
|
|
|
ci->client_index = NETWORK_SERVER_INDEX;
|
|
|
if (_network_dedicated)
|
|
|
ci->client_playas = OWNER_SPECTATOR;
|
|
|
else
|
|
|
ci->client_playas = _local_player + 1;
|
|
|
ttd_strlcpy(ci->client_name, _network_player_name, sizeof(ci->client_name));
|
|
|
ttd_strlcpy(ci->unique_id, _network_unique_id, sizeof(ci->unique_id));
|
|
|
}
|
|
|
|
|
|
bool NetworkServerStart(void)
|
|
|
{
|
|
|
if (!_network_available) return false;
|
|
|
|
|
|
/* Call the pre-scripts */
|
|
|
IConsoleCmdExec("exec scripts/pre_server.scr 0");
|
|
|
if (_network_dedicated) IConsoleCmdExec("exec scripts/pre_dedicated.scr 0");
|
|
|
|
|
|
NetworkInitialize();
|
|
|
if (!NetworkListen())
|
|
|
return false;
|
|
|
|
|
|
// Try to start UDP-server
|
|
|
_network_udp_server = true;
|
|
|
_network_udp_server = NetworkUDPListen(&_udp_server_socket, _network_server_bind_ip, _network_server_port, false);
|
|
|
|
|
|
_network_server = true;
|
|
|
_networking = true;
|
|
|
_frame_counter = 0;
|
|
|
_frame_counter_server = 0;
|
|
|
_frame_counter_max = 0;
|
|
|
_last_sync_frame = 0;
|
|
|
_network_own_client_index = NETWORK_SERVER_INDEX;
|
|
|
|
|
|
if (!_network_dedicated)
|
|
|
_network_playas = 1;
|
|
@@ -1287,104 +1294,102 @@ static void NetworkGenerateUniqueId(void
|
|
|
void NetworkStartUp(void)
|
|
|
{
|
|
|
DEBUG(net, 3) ("[NET][Core] Starting network...");
|
|
|
|
|
|
#if defined(__MORPHOS__) || defined(__AMIGA__)
|
|
|
/*
|
|
|
* IMPORTANT NOTE: SocketBase needs to be initialized before we use _any_
|
|
|
* network related function, else: crash.
|
|
|
*/
|
|
|
{
|
|
|
DEBUG(misc,3) ("[NET][Core] Loading bsd socket library");
|
|
|
if (!(SocketBase = OpenLibrary("bsdsocket.library", 4))) {
|
|
|
DEBUG(net, 0) ("[NET][Core] Error: couldn't open bsdsocket.library version 4. Network not available.");
|
|
|
_network_available = false;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
#if defined(__AMIGA__)
|
|
|
// for usleep() implementation (only required for legacy AmigaOS builds)
|
|
|
if ( (TimerPort = CreateMsgPort()) ) {
|
|
|
if ( (TimerRequest = (struct timerequest *) CreateIORequest(TimerPort, sizeof(struct timerequest))) ) {
|
|
|
if ( OpenDevice("timer.device", UNIT_MICROHZ, (struct IORequest *) TimerRequest, 0) == 0 ) {
|
|
|
if ( !(TimerBase = TimerRequest->tr_node.io_Device) ) {
|
|
|
// free ressources...
|
|
|
DEBUG(net, 0) ("[NET][Core] Error: couldn't initialize timer. Network not available.");
|
|
|
_network_available = false;
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
#endif // __AMIGA__
|
|
|
}
|
|
|
#endif // __MORPHOS__ / __AMIGA__
|
|
|
|
|
|
// Network is available
|
|
|
_network_available = true;
|
|
|
_network_dedicated = false;
|
|
|
_network_last_advertise_frame = 0;
|
|
|
_network_need_advertise = true;
|
|
|
_network_advertise_retries = 0;
|
|
|
|
|
|
/* Load the ip from the openttd.cfg */
|
|
|
_network_server_bind_ip = inet_addr(_network_server_bind_ip_host);
|
|
|
/* And put the data back in it in case it was an invalid ip */
|
|
|
snprintf(_network_server_bind_ip_host, sizeof(_network_server_bind_ip_host), "%s", inet_ntoa(*(struct in_addr *)&_network_server_bind_ip));
|
|
|
|
|
|
/* Generate an unique id when there is none yet */
|
|
|
if (_network_unique_id[0] == '\0')
|
|
|
NetworkGenerateUniqueId();
|
|
|
if (_network_unique_id[0] == '\0') NetworkGenerateUniqueId();
|
|
|
|
|
|
memset(&_network_game_info, 0, sizeof(_network_game_info));
|
|
|
|
|
|
/* XXX - Hard number here, because the strings can currently handle no more
|
|
|
than 10 clients -- TrueLight */
|
|
|
_network_game_info.clients_max = 10;
|
|
|
_network_game_info.clients_max = 10; // XXX - hardcoded, string limiation -- TrueLight
|
|
|
_network_game_info.companies_max = MAX_PLAYERS;
|
|
|
_network_game_info.spectators_max = _network_game_info.clients_max;
|
|
|
|
|
|
// Let's load the network in windows
|
|
|
#if defined(WIN32)
|
|
|
{
|
|
|
WSADATA wsa;
|
|
|
DEBUG(net, 3) ("[NET][Core] Loading windows socket library");
|
|
|
if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) {
|
|
|
DEBUG(net, 0) ("[NET][Core] Error: WSAStartup failed. Network not available.");
|
|
|
_network_available = false;
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
#endif // WIN32
|
|
|
|
|
|
NetworkInitialize();
|
|
|
DEBUG(net, 3) ("[NET][Core] Network online. Multiplayer available.");
|
|
|
NetworkFindIPs();
|
|
|
}
|
|
|
|
|
|
// This shuts the network down
|
|
|
void NetworkShutDown(void)
|
|
|
{
|
|
|
DEBUG(net, 3) ("[NET][Core] Shutting down the network.");
|
|
|
|
|
|
_network_available = false;
|
|
|
|
|
|
#if defined(__MORPHOS__) || defined(__AMIGA__)
|
|
|
{
|
|
|
// free allocated ressources
|
|
|
#if defined(__AMIGA__)
|
|
|
if (TimerBase) { CloseDevice((struct IORequest *) TimerRequest); }
|
|
|
if (TimerRequest) { DeleteIORequest(TimerRequest); }
|
|
|
if (TimerPort) { DeleteMsgPort(TimerPort); }
|
|
|
#endif
|
|
|
|
|
|
if (SocketBase) {
|
|
|
CloseLibrary(SocketBase);
|
|
|
}
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
#if defined(WIN32)
|
|
|
{
|
|
|
WSACleanup();
|
|
|
}
|
|
|
#endif
|
|
|
}
|
|
|
#else
|