From 0bd907e88df535a11483b5bdcb5763a5fce0cbf5 Mon Sep 17 00:00:00 2001 From: dominik Date: Sun, 22 Aug 2004 10:23:37 +0000 Subject: (svn r106) New network core (by sign_de) Features: * network core is dynamicly loaded when needed (-n isn't needed anymore) for easy switching between single and multiplayer. But commandline shortcuts are still enabled: -n = autodetect network server -n [ip] = connect to the server * udp now uses 2 different ports - you can run 1 server and serveral clients on one pc - the clients udp-socket gets unloaded when the network game starts - the servers udp-sockets remains online to allow the network gui to detect itself * new gameinfo structure this struct is available for every online/lan game * dynamic NetworkGameList --- functions.h | 31 ++- intro_gui.c | 8 +- lang/english.txt | 3 + misc.c | 2 + network.c | 805 +++++++++++++++++++++++++++++++++++++++++-------------- network_gui.c | 46 +--- settings.c | 3 +- ttd.c | 57 ++-- variables.h | 14 +- 9 files changed, 691 insertions(+), 278 deletions(-) diff --git a/functions.h b/functions.h index f684fa055..64be83efb 100644 --- a/functions.h +++ b/functions.h @@ -121,19 +121,36 @@ int CalcBridgeLenCostFactor(int x); typedef void CommandCallback(bool success, uint tile, uint32 p1, uint32 p2); bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback, uint32 cmd); -void NetworkConnect(const char *hostname, int port); void NetworkReceive(); void NetworkSend(); void NetworkProcessCommands(); -void NetworkListen(int port); -void NetworkInitialize(const char *hostname); +void NetworkListen(); +void NetworkInitialize(); void NetworkShutdown(); void NetworkSendCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback); -void NetworkStartSync(); -void NetworkUDPListen(int port); -void NetworkUDPReceive(); +void NetworkStartSync(bool fcreset); +void NetworkClose(bool client); + void NetworkIPListInit(); -bool NetworkUDPSearchServer(); + +void NetworkCoreInit(); +void NetworkCoreShutdown(); +void NetworkCoreDisconnect(); +void NetworkCoreLoop(bool incomming); +bool NetworkCoreConnectGame(byte* b, unsigned short port); +bool NetworkCoreStartGame(); + +void NetworkLobbyShutdown(); +void NetworkLobbyInit(); + +void NetworkGameListClear(); +char * NetworkGameListAdd(); +void NetworkGameListFromLAN(); +void NetworkGameListFromInternet(); +char * NetworkGameListItem(uint16 index); + +void NetworkGameFillDefaults(); +void NetworkGameChangeDate(uint16 newdate); /* misc_cmd.c */ void PlaceTreesRandomly(); diff --git a/intro_gui.c b/intro_gui.c index b1095691e..83cb5a043 100644 --- a/intro_gui.c +++ b/intro_gui.c @@ -58,7 +58,9 @@ static void SelectGameWndProc(Window *w, WindowEvent *e) { DoCommandP(0, 0, 0, NULL, CMD_SET_SINGLE_PLAYER); break; case 7: - ShowNetworkGameWindow(); + if (!_network_available) { + ShowErrorMessage(-1,STR_NETWORK_ERR_NOTAVAILABLE, 0, 0); + } else ShowNetworkGameWindow(); break; case 8: ShowGameOptions(); break; case 9: ShowGameDifficulty(); break; @@ -118,7 +120,7 @@ int32 CmdGenRandomNewGame(int x, int y, uint32 flags, uint32 p1, uint32 p2) _random_seed_1 = p1; _random_seed_2 = p2; - if (_networking) { NetworkStartSync(); } + if (_networking) { NetworkStartSync(true); } MakeNewGame(); return 0; @@ -159,7 +161,7 @@ int32 CmdStartScenario(int x, int y, uint32 flags, uint32 p1, uint32 p2) _random_seed_1 = p1; _random_seed_2 = p2; - if (_networking) { NetworkStartSync(); } + if (_networking) { NetworkStartSync(true); } StartScenario(); return 0; diff --git a/lang/english.txt b/lang/english.txt index a79d6b8fd..bd3c5c018 100644 --- a/lang/english.txt +++ b/lang/english.txt @@ -1213,6 +1213,9 @@ STR_NETWORK_NEW_COMPANY :{BLACK}New company STR_NETWORK_NEW_COMPANY_TIP :{BLACK}Open a new company STR_NETWORK_READY :{BLACK}Ready +STR_NETWORK_ERR_NOTAVAILABLE :{WHITE} No network devices found or compiled without ENABLE_NETWORK +STR_NETWORK_ERR_NOSERVER :{WHITE} Could not find any network game +STR_NETWORK_ERR_NOCONNECTION :{WHITE} The server didn't answer the request ############ end network gui strings diff --git a/misc.c b/misc.c index 2a57f73ba..06e790019 100644 --- a/misc.c +++ b/misc.c @@ -542,6 +542,8 @@ void IncreaseDate() /* yeah, increse day counter and call various daily loops */ _date++; + NetworkGameChangeDate(_date); + _vehicle_id_ctr_day = 0; DisasterDailyLoop(); diff --git a/network.c b/network.c index e7a9b69cf..028099782 100644 --- a/network.c +++ b/network.c @@ -1,5 +1,6 @@ #include "stdafx.h" #include "ttd.h" +#include "gui.h" #include "command.h" #include "player.h" @@ -180,9 +181,10 @@ typedef struct FutureSeeds { static FutureSeeds _future_seed[8]; static int _num_future_seed; -static SOCKET _listensocket; -static SOCKET _udpsocket; +static SOCKET _listensocket; // tcp socket +static SOCKET _udp_client_socket; // udp server socket +static SOCKET _udp_server_socket; // udp client socket typedef struct UDPPacket { byte command_code; @@ -194,15 +196,13 @@ typedef struct UDPPacket { enum { NET_UDPCMD_SERVERSEARCH = 1, NET_UDPCMD_SERVERACTIVE, + NET_UDPCMD_GETSERVERINFO, + NET_UDPCMD_SERVERINFO, }; -uint32 _network_ip_list[10]; // Network ips -char * _network_detected_serverip = "255.255.255.255"; // UDP Broadcast detected server-ip -uint32 _network_detected_serverport = 0; // UDP Broadcast detected server-port +void NetworkUDPSend(bool client, struct sockaddr_in recv,struct UDPPacket packet); -void NetworkUDPSend(struct sockaddr_in recv,struct UDPPacket packet); - -static bool _network_synced; +uint32 _network_ip_list[10]; // network ip list // this is set to point to the savegame static byte *_transmit_file; @@ -210,8 +210,51 @@ static size_t _transmit_file_size; static FILE *_recv_file; +typedef struct NetworkGameInfo { + char server_name[40]; // name of the game + char server_revision[8]; // server game version + byte server_lang; // langid + byte players_max; // max players allowed on server + byte players_on; // current count of players on server + uint16 game_date; // current date + char game_password[10]; // should fit ... 14 chars + char map_name[40]; // map which is played ["random" for a randomized map] + uint map_width; // map width / 8 + uint map_height; // map height / 8 + byte map_set; // graphical set +} NetworkGameInfo; + +typedef struct NetworkGameList { + NetworkGameInfo item; + uint32 ip; + uint16 port; + char * _next; +} NetworkGameList; + +static NetworkGameInfo _network_game; +static NetworkGameList * _network_game_list = NULL; + +/* multi os compatible sleep function */ +void CSleep(int milliseconds) { +#if defined(WIN32) +Sleep(milliseconds); +#endif +#if defined(UNIX) +#ifndef __BEOS__ +usleep(milliseconds*1000); +#endif +#ifdef __BEOS__ +snooze(milliseconds*1000); +#endif +#endif +} + ////////////////////////////////////////////////////////////////////// +// ****************************** // +// * TCP Packets and Handlers * // +// ****************************** // + static QueuedCommand *AllocQueuedCommand(CommandQueue *nq) { QueuedCommand *qp = (QueuedCommand*)calloc(sizeof(QueuedCommand), 1); @@ -471,12 +514,13 @@ static void HandleFilePacket(FilePacketHdr *fp) // sync to server. _networking_queuing = false; - + NetworkStartSync(false); +/* _networking_sync = true; _frame_counter = 0; // start executing at frame 0. _sync_seed_1 = _sync_seed_2 = 0; _num_future_seed = 0; - memset(_my_seed_list, 0, sizeof(_my_seed_list)); + memset(_my_seed_list, 0, sizeof(_my_seed_list)); */ if (_network_playas == 0) { // send a command to make a new player @@ -504,7 +548,7 @@ static void CloseClient(ClientState *cs) { Packet *p, *next; - printf("CloseClient\n"); + DEBUG(misc,1) ("[NET][TCP] closed client connection"); assert(cs->socket != INVALID_SOCKET); @@ -523,6 +567,8 @@ static void CloseClient(ClientState *cs) } cs->socket = INVALID_SOCKET; + if (_networking_server) _network_game.players_on--; + _num_clients--; } @@ -683,6 +729,8 @@ static ClientState *AllocClient(SOCKET s) if (_num_clients == MAX_CLIENTS) return NULL; + if (_networking_server) _network_game.players_on++; + cs = &_clients[_num_clients++]; memset(cs, 0, sizeof(*cs)); cs->last = &cs->head; @@ -779,14 +827,89 @@ static void SendQueuedCommandsToNewClient(ClientState *cs) } +// ************************** // +// * TCP Networking * // +// ************************** // + +bool NetworkConnect(const char *hostname, int port) +{ + SOCKET s; + struct sockaddr_in sin; + int b; + + DEBUG(misc, 1) ("[NET][TCP] Connecting to %s %d", hostname, port); + + s = socket(AF_INET, SOCK_STREAM, 0); + if (s == INVALID_SOCKET) error("socket() failed"); + + b = 1; + setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const char*)&b, sizeof(b)); + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = inet_addr(hostname); + sin.sin_port = htons(port); + + if (connect(s, (struct sockaddr*) &sin, sizeof(sin)) != 0) { + NetworkClose(true); + return false; + } + + // set nonblocking mode for socket.. + { unsigned long blocking = 1; ioctlsocket(s, FIONBIO, &blocking); } + + // in client mode, only the first client field is used. it's pointing to the server. + AllocClient(s); + + // queue packets.. because we're waiting for the savegame. + _networking_queuing = true; + _frame_counter_max = 0; + + return true; +} + +void NetworkListen() +{ + + SOCKET ls; + struct sockaddr_in sin; + int port; + + port = _network_server_port; + + DEBUG(misc, 1) ("[NET][TCP] listening on port %d", port); + + ls = socket(AF_INET, SOCK_STREAM, 0); + if (ls == INVALID_SOCKET) + error("socket() on listen socket failed"); + + // reuse the socket + { + int reuse = 1; if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) == -1) + error("setsockopt() on listen socket failed"); + } + + // set nonblocking mode for socket + { unsigned long blocking = 1; ioctlsocket(ls, FIONBIO, &blocking); } + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = 0; + sin.sin_port = htons(port); + + if (bind(ls, (struct sockaddr*)&sin, sizeof(sin)) != 0) + error("bind() failed"); + + if (listen(ls, 1) != 0) + error("listen() failed"); + + _listensocket = ls; +} + void NetworkReceive() { ClientState *cs; int n; fd_set read_fd, write_fd; struct timeval tv; - - NetworkUDPReceive(); // udp handling FD_ZERO(&read_fd); FD_ZERO(&write_fd); @@ -897,165 +1020,76 @@ void NetworkSend() } } -void NetworkConnect(const char *hostname, int port) -{ - SOCKET s; - struct sockaddr_in sin; - int b; - - - s = socket(AF_INET, SOCK_STREAM, 0); - if (s == INVALID_SOCKET) error("socket() failed"); - - b = 1; - setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const char*)&b, sizeof(b)); - if (strcmp(hostname,"auto")==0) { - // autodetect server over udp broadcast [works 4 lan] - if (NetworkUDPSearchServer()) { - hostname=_network_detected_serverip; - port=_network_detected_serverport; - } else { - error("udp: server not found"); - } - } - - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = inet_addr(hostname); - sin.sin_port = htons(port); - - if (connect(s, (struct sockaddr*) &sin, sizeof(sin)) != 0) - error("connect() failed"); - - // set nonblocking mode for socket.. - { unsigned long blocking = 1; ioctlsocket(s, FIONBIO, &blocking); } - - // in client mode, only the first client field is used. it's pointing to the server. - AllocClient(s); - - // queue packets.. because we're waiting for the savegame. - _networking_queuing = true; - _frame_counter_max = 0; - -} - -void NetworkListen(int port) -{ - - SOCKET ls; - struct sockaddr_in sin; - - ls = socket(AF_INET, SOCK_STREAM, 0); - if (ls == INVALID_SOCKET) - error("socket() on listen socket failed"); - - // reuse the socket - { - int reuse = 1; if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) == -1) - error("setsockopt() on listen socket failed"); - } - - // set nonblocking mode for socket - { unsigned long blocking = 1; ioctlsocket(ls, FIONBIO, &blocking); } - - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = 0; - sin.sin_port = htons(port); - - if (bind(ls, (struct sockaddr*)&sin, sizeof(sin)) != 0) - error("bind() failed"); - - if (listen(ls, 1) != 0) - error("listen() failed"); - - _listensocket = ls; -} - -void NetworkInitialize(const char *hostname) +void NetworkInitialize() { ClientState *cs; -#if defined(WIN32) - WSADATA wsa; - if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) - error("WSAStartup failed"); -#endif - -#if defined(__MORPHOS__) || defined(__AMIGA__) - if (!(SocketBase = OpenLibrary("bsdsocket.library", 4))) { - error("Couldn't open bsdsocket.library version 4."); - } -#endif - _command_queue.last = &_command_queue.head; _ack_queue.last = &_ack_queue.head; // invalidate all clients for(cs=_clients; cs != &_clients[MAX_CLIENTS]; cs++) cs->socket = INVALID_SOCKET; - - /* startup udp listener - * - only if this instance is a server, so clients can find it - * OR - * - a client, wanting to find a server to connect to - */ - if (hostname == NULL || strcmp(hostname,"auto") == 0) { - printf("Trying to open UDP port...\n"); - NetworkUDPListen(_network_port); - } + +} + +void NetworkClose(bool client) { + + ClientState *cs; + // invalidate all clients + + for(cs=_clients; cs != &_clients[MAX_CLIENTS]; cs++) if (cs->socket != INVALID_SOCKET) { + CloseClient(cs); + } + + if (!client) { + // if in servermode --> close listener + closesocket(_listensocket); + _listensocket= INVALID_SOCKET; + DEBUG(misc,1) ("[NET][TCP] closed listener on port %i", _network_server_port); + } } void NetworkShutdown() { -#if defined(__MORPHOS__) || defined(__AMIGA__) - if (SocketBase) { - CloseLibrary(SocketBase); - } -#endif + } // switch to synced mode. -void NetworkStartSync() +void NetworkStartSync(bool fcreset) { + DEBUG(misc,3) ("[NET][SYNC] switching to synced game mode"); _networking_sync = true; _frame_counter = 0; - _frame_counter_max = 0; - _frame_counter_srv = 0; + if (fcreset) { + _frame_counter_max = 0; + _frame_counter_srv = 0; + } _num_future_seed = 0; _sync_seed_1 = _sync_seed_2 = 0; memset(_my_seed_list, 0, sizeof(_my_seed_list)); + } // ************************** // // * UDP Network Extensions * // // ************************** // -/* multi os compatible sleep function */ -void CSleep(int milliseconds) { -#if defined(WIN32) -Sleep(milliseconds); -#endif -#if defined(UNIX) -#ifndef __BEOS__ -usleep(milliseconds*1000); -#endif -#ifdef __BEOS__ -snooze(milliseconds*1000); -#endif -#endif -} - -void NetworkUDPListen(int port) +void NetworkUDPListen(bool client) { SOCKET udp; struct sockaddr_in sin; + int port; - DEBUG(misc,0) ("udp: listener initiated on port %i", port); - NetworkIPListInit(); + if (client) { port = _network_client_port; } else { port = _network_server_port; }; + + DEBUG(misc,1) ("[NET][UDP] listening on port %i", port); udp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (udp == INVALID_SOCKET) - error("udp: socket() on listen socket failed"); + + // this disables network + _network_available = !(udp == INVALID_SOCKET); // set nonblocking mode for socket { unsigned long blocking = 1; ioctlsocket(udp, FIONBIO, &blocking); } @@ -1065,137 +1099,502 @@ void NetworkUDPListen(int port) sin.sin_port = htons(port); if (bind(udp, (struct sockaddr*)&sin, sizeof(sin)) != 0) - error("udp: bind() failed"); + DEBUG(misc,1) ("[NET][UDP] error: bind failed on port %i", port); + // enable broadcasting { unsigned long val=1; setsockopt(udp, SOL_SOCKET, SO_BROADCAST, (char *) &val , sizeof(val)); } - - _udpsocket = udp; + // allow reusing + { unsigned long val=1; setsockopt(udp, SOL_SOCKET, SO_REUSEADDR, (char *) &val , sizeof(val)); } + + if (client) { _udp_client_socket = udp; } else { _udp_server_socket = udp; } ; } -void NetworkUDPReceive() { +void NetworkUDPClose(bool client) { + if (client) { + DEBUG(misc,1) ("[NET][UDP] closed listener on port %i", _network_client_port); + closesocket(_udp_client_socket); + _udp_client_socket = INVALID_SOCKET; + } else { + DEBUG(misc,1) ("[NET][UDP] closed listener on port %i", _network_server_port); + closesocket(_udp_server_socket); + _udp_server_socket = INVALID_SOCKET; + }; + } + +void NetworkUDPReceive(bool client) { struct sockaddr_in client_addr; int client_len; int nbytes; struct UDPPacket packet; int packet_len; + SOCKET udp; + if (client) udp=_udp_client_socket; else udp=_udp_server_socket; + packet_len = sizeof(packet); client_len = sizeof(client_addr); - nbytes = recvfrom(_udpsocket, (char *) &packet, packet_len , 0, (struct sockaddr *) &client_addr, &client_len); + nbytes = recvfrom(udp, (char *) &packet, packet_len , 0, (struct sockaddr *) &client_addr, &client_len); if (nbytes>0) { if (packet.command_code==packet.command_check) switch (packet.command_code) { - case NET_UDPCMD_SERVERSEARCH: - if (_networking_server) { - packet.command_check=packet.command_code=NET_UDPCMD_SERVERACTIVE; - NetworkUDPSend(client_addr, packet); - } - break; - case NET_UDPCMD_SERVERACTIVE: - if (!_networking_server) { - _network_detected_serverip=inet_ntoa(*(struct in_addr *) &client_addr.sin_addr); - _network_detected_serverport=ntohs(client_addr.sin_port); + case NET_UDPCMD_SERVERSEARCH: + if (!client) { + packet.command_check=packet.command_code=NET_UDPCMD_SERVERINFO; + memcpy(&packet.data,&_network_game,sizeof(_network_game)); + packet.data_len=sizeof(_network_game); + NetworkUDPSend(client,client_addr, packet); + } + break; + case NET_UDPCMD_GETSERVERINFO: + if (!client) { + packet.command_check=packet.command_code=NET_UDPCMD_SERVERINFO; + memcpy(&packet.data,&_network_game,sizeof(_network_game)); + packet.data_len=sizeof(_network_game); + NetworkUDPSend(client,client_addr, packet); } break; + case NET_UDPCMD_SERVERINFO: + if (client) { + NetworkGameList * item; + + item = (NetworkGameList *) NetworkGameListAdd(); + item -> ip = inet_addr(inet_ntoa(client_addr.sin_addr)); + item -> port = ntohs(client_addr.sin_port); + + memcpy(item,&packet.data,packet.data_len); + } + break; } } } -void NetworkIPListInit() { - struct hostent* he; - char hostname[250]; - uint32 bcaddr; - int i=0; - - _network_detected_serverip=""; - - gethostname(hostname,250); - DEBUG(misc,0) ("iplist: init for host %s", hostname); - he=gethostbyname((char *) hostname); - - if (he == NULL) { - DEBUG(misc, 0) ("iplist: gethostbyname failed for host %s...trying with IP address", hostname); - bcaddr = inet_addr(hostname); - he = gethostbyaddr(inet_ntoa(*(struct in_addr *)bcaddr), sizeof(bcaddr), AF_INET); - } - if (he == NULL) { - DEBUG(misc, 0) ("iplist: cannot resolve %s", hostname); - } else { - while(he->h_addr_list[i]) { - bcaddr = inet_addr(inet_ntoa(*(struct in_addr *) he->h_addr_list[i])); - _network_ip_list[i]=bcaddr; - DEBUG(misc,0) ("iplist: add %s",inet_ntoa(*(struct in_addr *) he->h_addr_list[i])); - i++; - } - } - _network_ip_list[i]=0; - -} -void NetworkUDPBroadCast(struct UDPPacket packet) { +void NetworkUDPBroadCast(bool client, struct UDPPacket packet) { int i=0, res; struct sockaddr_in out_addr; uint32 bcaddr; byte * bcptr; + + SOCKET udp; + if (client) udp=_udp_client_socket; else udp=_udp_server_socket; + while (_network_ip_list[i]!=0) { bcaddr=_network_ip_list[i]; out_addr.sin_family = AF_INET; - out_addr.sin_port = htons(_network_port); + if (client) { out_addr.sin_port = htons(_network_server_port); } else { out_addr.sin_port = htons(_network_client_port); }; bcptr = (byte *) &bcaddr; bcptr[3]=255; out_addr.sin_addr.s_addr = bcaddr; - res=sendto(_udpsocket,(char *) &packet,sizeof(packet),0,(struct sockaddr *) &out_addr,sizeof(out_addr)); + res=sendto(udp,(char *) &packet,sizeof(packet),0,(struct sockaddr *) &out_addr,sizeof(out_addr)); if (res==-1) error("udp: broadcast error: %i",GET_LAST_ERROR()); i++; } } -void NetworkUDPSend(struct sockaddr_in recv,struct UDPPacket packet) { - sendto(_udpsocket,(char *) &packet,sizeof(packet),0,(struct sockaddr *) &recv,sizeof(recv)); +void NetworkUDPSend(bool client, struct sockaddr_in recv,struct UDPPacket packet) { + + SOCKET udp; + if (client) udp=_udp_client_socket; else udp=_udp_server_socket; + + sendto(udp,(char *) &packet,sizeof(packet),0,(struct sockaddr *) &recv,sizeof(recv)); } -bool NetworkUDPSearchServer() { + +bool NetworkUDPSearchGame(byte ** _network_detected_serverip, unsigned short * _network_detected_serverport) { struct UDPPacket packet; int timeout=3000; - DEBUG(misc,0) ("udp: searching server"); - _network_detected_serverip = "255.255.255.255"; - _network_detected_serverport = 0; + + NetworkGameListClear(); + + DEBUG(misc,0) ("[NET][UDP] searching server"); + *_network_detected_serverip = "255.255.255.255"; + *_network_detected_serverport = 0; packet.command_check=packet.command_code=NET_UDPCMD_SERVERSEARCH; packet.data_len=0; - NetworkUDPBroadCast(packet); + NetworkUDPBroadCast(true, packet); while (timeout>=0) { CSleep(100); timeout-=100; - NetworkUDPReceive(); - if (_network_detected_serverport>0) { - timeout=-1; - DEBUG(misc,0) ("udp: server found on %s", _network_detected_serverip); - } + NetworkUDPReceive(true); + + if (_network_game_count>0) { + NetworkGameList * item; + item = (NetworkGameList *) NetworkGameListItem(0); + *_network_detected_serverip=inet_ntoa(*(struct in_addr *) &item->ip); + *_network_detected_serverport=item->port; + timeout=-1; + DEBUG(misc,0) ("[NET][UDP] server found on %s", *_network_detected_serverip); + } + } + return (_network_detected_serverport>0); } +// *************************** // +// * New Network Core System * // +// *************************** // + +void NetworkIPListInit() { + struct hostent* he = NULL; + char hostname[250]; + uint32 bcaddr; + int i=0; + + gethostname(hostname,250); + DEBUG(misc,2) ("[NET][IP] init for host %s", hostname); + he=gethostbyname((char *) hostname); + + if (he == NULL) { + he = gethostbyname("localhost"); + } + + if (he == NULL) { + bcaddr = inet_addr("127.0.0.1"); + he = gethostbyaddr(inet_ntoa(*(struct in_addr *) &bcaddr), sizeof(bcaddr), AF_INET); + } + + if (he == NULL) { + DEBUG(misc, 2) ("[NET][IP] cannot resolve %s", hostname); + } else { + while(he->h_addr_list[i]) { + bcaddr = inet_addr(inet_ntoa(*(struct in_addr *) he->h_addr_list[i])); + _network_ip_list[i]=bcaddr; + DEBUG(misc,2) ("[NET][IP] add %s",inet_ntoa(*(struct in_addr *) he->h_addr_list[i])); + i++; + } + + } + _network_ip_list[i]=0; + +} + +/* *************************************************** */ + +void NetworkCoreInit() { + +DEBUG(misc,3) ("[NET][Core] init()"); +_network_available=true; + +// [win32] winsock startup + +#if defined(WIN32) +{ + WSADATA wsa; + DEBUG(misc,3) ("[NET][Core] using windows socket library"); + if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) { + DEBUG(misc,3) ("[NET][Core] error: WSAStartup failed"); + _network_available=false; + } +} +#else + +// [morphos/amigaos] bsd-socket startup + +#if defined(__MORPHOS__) || defined(__AMIGA__) +{ + DEBUG(misc,3) ("[NET][Core] using bsd socket library"); + if (!(SocketBase = OpenLibrary("bsdsocket.library", 4))) { + DEBUG(misc,3) ("[NET][Core] Couldn't open bsdsocket.library version 4."); + _network_available=false; + } +} +#else + +// [linux/macos] unix-socket startup + + DEBUG(misc,3) ("[NET][Core] using unix socket library"); + +#endif + +#endif + + +if (_network_available) { + DEBUG(misc,3) ("[NET][Core] OK: multiplayer available"); + // initiate network ip list + NetworkIPListInit(); + } else { + DEBUG(misc,3) ("[NET][Core] FAILED: multiplayer not available"); + } +} + +/* *************************************************** */ + +void NetworkCoreShutdown() { + +DEBUG(misc,3) ("[NET][Core] shutdown()"); + +#if defined(__MORPHOS__) || defined(__AMIGA__) +{ + if (SocketBase) { + CloseLibrary(SocketBase); + } +} +#endif + + +#if defined(WIN32) +{ + WSACleanup(); +} +#endif + +} + +/* *************************************************** */ + +bool NetworkCoreConnectGame(byte* b, unsigned short port) +{ + if (!_network_available) return false; + + if (strcmp((char *) b,"auto")==0) { + // do autodetect + NetworkUDPSearchGame(&b, &port); + } + + if (port==0) { + // autodetection failed + if (_networking_override) NetworkLobbyShutdown(); + ShowErrorMessage(-1, STR_NETWORK_ERR_NOSERVER, 0, 0); + return false; + } + NetworkInitialize(); + _networking = NetworkConnect(b, port); + if (_networking) { + NetworkLobbyShutdown(); + } else { + if (_networking_override) NetworkLobbyShutdown(); + ShowErrorMessage(-1, STR_NETWORK_ERR_NOCONNECTION,0,0); + } + return _networking; +} + +/* *************************************************** */ + +bool NetworkCoreStartGame() +{ + if (!_network_available) return false; + NetworkLobbyShutdown(); + NetworkInitialize(); + NetworkListen(); + NetworkUDPListen(false); + _networking_server = true; + _networking = true; + NetworkGameFillDefaults(); // clears the network game info + _network_game.players_on++; // the serverplayer is online + return true; +} + +/* *************************************************** */ + +void NetworkCoreDisconnect() +{ + /* terminate server */ + if (_networking_server) { + NetworkUDPClose(false); + NetworkClose(false); + } + + /* terminate client connection */ + else if (_networking) { + NetworkClose(true); + } + + _networking_server = false; + _networking = false; + NetworkShutdown(); +} + +/* *************************************************** */ + +void NetworkCoreLoop(bool incomming) { + + +if (incomming) { + + // incomming + + if ( _udp_client_socket != INVALID_SOCKET ) NetworkUDPReceive(true); + if ( _udp_server_socket != INVALID_SOCKET ) NetworkUDPReceive(false); + + if (_networking) { + NetworkReceive(); + NetworkProcessCommands(); // to check if we got any new commands belonging to the current frame before we increase it. + } + + } else { + + // outgoing + + if (_networking) { + NetworkSend(); + } + + } + +} + +void NetworkLobbyInit() { + DEBUG(misc,3) ("[NET][Lobby] init()"); + NetworkUDPListen(true); +} + +void NetworkLobbyShutdown() { + DEBUG(misc,3) ("[NET][Lobby] shutdown()"); + NetworkUDPClose(true); +} + + +// ******************************** // +// * Network Game List Extensions * // +// ******************************** // + +void NetworkGameListClear() { +NetworkGameList * item; +NetworkGameList * next; + +DEBUG(misc,4) ("[NET][G-List] cleared server list"); + +item = _network_game_list; +while (item != NULL) { + next = (NetworkGameList *) item -> _next; + free (item); + item = next; + } +_network_game_list=NULL; +_network_game_count=0; +} + +char * NetworkGameListAdd() { +NetworkGameList * item; +NetworkGameList * before; + +DEBUG(misc,4) ("[NET][G-List] added server to list"); + +item = _network_game_list; +before = item; +while (item != NULL) { + before = item; + item = (NetworkGameList *) item -> _next; + } +item = malloc(sizeof(NetworkGameList)); +item -> _next = NULL; +if (before == NULL) { + _network_game_list = item; + } else { + before -> _next = (char *) item; + } +_network_game_count++; +return (char *) item; +} + +void NetworkGameListFromLAN() { + struct UDPPacket packet; + DEBUG(misc,2) ("[NET][G-List] searching server over lan"); + NetworkGameListClear(); + packet.command_check=packet.command_code=NET_UDPCMD_SERVERSEARCH; + packet.data_len=0; + NetworkUDPBroadCast(true,packet); +} + +void NetworkGameListFromInternet() { + DEBUG(misc,2) ("[NET][G-List] searching servers over internet"); + NetworkGameListClear(); + + // **TODO** masterserver communication [internet protocol list] + +} + +char * NetworkGameListItem(uint16 index) { +NetworkGameList * item; +NetworkGameList * next; +uint16 cnt = 0; + +item = _network_game_list; + +while ((item != NULL) && (cnt != index)) { + next = (NetworkGameList *) item -> _next; + item = next; + cnt++; + } + +return (char *) item; +} + +// *************************** // +// * Network Game Extensions * // +// *************************** // + +void NetworkGameFillDefaults() { + NetworkGameInfo * game = &_network_game; +#if defined(WITH_REV) + extern char _openttd_revision[]; +#endif + + DEBUG(misc,4) ("[NET][G-Info] setting defaults"); + + ttd_strlcpy(game->server_name,"OpenTTD Game",13); + game->game_password[0]='\0'; + game->map_name[0]='\0'; +#if defined(WITH_REV) + ttd_strlcpy(game->server_revision,_openttd_revision,strlen(_openttd_revision)); +#else + ttd_strlcpy(game->server_revision,"norev000",strlen("norev000")); +#endif + game->game_date=0; + + game->map_height=0; + game->map_width=0; + game->map_set=0; + + game->players_max=8; + game->players_on=0; + + game->server_lang=_dynlang.curr; +} + +void NetworkGameChangeDate(uint16 newdate) { + if (_networking_server) { + _network_game.game_date = newdate; + } +} + #else // not ENABLE_NETWORK // stubs -void NetworkInitialize(const char *hostname) {} +void NetworkInitialize() {} void NetworkShutdown() {} -void NetworkListen(int port) {} +void NetworkListen() {} void NetworkConnect(const char *hostname, int port) {} void NetworkReceive() {} void NetworkSend() {} void NetworkSendCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback) {} void NetworkProcessCommands() {} void NetworkStartSync() {} -void NetworkUDPListen(int port) {} -void NetworkUDPReceive() {} -bool NetworkUDPSearchServer() { return false; } -#endif // ENABLE_NETWORK +void NetworkCoreInit() { _network_available=false; }; +void NetworkCoreShutdown() {}; +void NetworkCoreDisconnect() {}; +void NetworkCoreLoop(bool incomming) {}; +bool NetworkCoreConnectGame(byte* b, unsigned short port) {}; +bool NetworkCoreStartGame() {}; +void NetworkLobbyShutdown() {}; +void NetworkLobbyInit() {}; +void NetworkGameListClear() {}; +char * NetworkGameListAdd() {return NULL;}; +void NetworkGameListFromLAN() {}; +void NetworkGameListFromInternet() {}; +void NetworkGameFillDefaults() {}; +char * NetworkGameListItem(uint16 index) {return NULL;}; +void NetworkGameChangeDate(uint16 newdate) {}; +} + +#endif diff --git a/network_gui.c b/network_gui.c index 008067e4d..75f65d89b 100644 --- a/network_gui.c +++ b/network_gui.c @@ -21,16 +21,6 @@ void ShowQueryString(StringID str, StringID caption, int maxlen, int maxwidth, b static byte _selected_field; char *direct_ip = NULL; - -void ConnectToServer(byte* b) -{ - _networking = true; - - NetworkInitialize(b); - DEBUG(misc, 1) ("Connecting to %s %d\n", b, _network_port); - NetworkConnect(b, _network_port); -} - static const StringID _connection_types_dropdown[] = { STR_NETWORK_LAN, STR_NETWORK_INTERNET, @@ -65,7 +55,15 @@ static void NetworkGameWindowWndProc(Window *w, WindowEvent *e) case 0: // close X case 15: // cancel button DeleteWindowById(WC_NETWORK_WINDOW, 0); + NetworkLobbyShutdown(); break; + case 3: // find server automaticaly + { + byte *b = "auto"; + NetworkCoreConnectGame(b,_network_server_port); + } + break; + case 4: // connect via direct ip { StringID str; @@ -111,7 +109,7 @@ static void NetworkGameWindowWndProc(Window *w, WindowEvent *e) byte *b = e->edittext.str; if (*b == 0) return; - ConnectToServer(b); + NetworkCoreConnectGame(b,_network_server_port); } break; } @@ -159,6 +157,8 @@ void ShowNetworkGameWindow() { Window *w; DeleteWindowById(WC_NETWORK_WINDOW, 0); + + NetworkLobbyInit(); w = AllocateWindowDesc(&_network_game_window_desc); strcpy(_edit_str_buf, "Your name"); @@ -168,24 +168,9 @@ void ShowNetworkGameWindow() WP(w,querystr_d).maxlen = MAX_QUERYSTR_LEN; WP(w,querystr_d).maxwidth = 240; WP(w,querystr_d).buf = _edit_str_buf; - - - ShowErrorMessage(-1, TEMP_STRING_NO_NETWORK, 0, 0); -} - - -void StartServer() -{ - _networking = true; - NetworkInitialize(NULL); - DEBUG(misc, 1) ("Listening on port %d\n", _network_port); - NetworkListen(_network_port); - _networking_server = true; - DoCommandP(0, 0, 0, NULL, CMD_START_NEW_GAME); + // ShowErrorMessage(-1, TEMP_STRING_NO_NETWORK, 0, 0); } - - static const StringID _players_dropdown[] = { STR_NETWORK_2_PLAYERS, STR_NETWORK_3_PLAYERS, @@ -232,8 +217,9 @@ static void NetworkStartServerWindowWndProc(Window *w, WindowEvent *e) ShowDropDownMenu(w, _players_dropdown, _opt_mod_ptr->currency, e->click.widget, 0); return; case 9: // start game - StartServer(); + NetworkCoreStartGame(); ShowNetworkLobbyWindow(); + DoCommandP(0, 0, 0, NULL, CMD_START_NEW_GAME); break; } @@ -308,10 +294,6 @@ static void ShowNetworkStartServerWindow() WP(w,querystr_d).buf = _edit_str_buf; } - - - - static void NetworkLobbyWindowWndProc(Window *w, WindowEvent *e) { switch(e->event) { diff --git a/settings.c b/settings.c index 134153672..56fee932c 100644 --- a/settings.c +++ b/settings.c @@ -760,7 +760,8 @@ static const SettingDesc misc_settings[] = { }; static const SettingDesc network_settings[] = { - {"port", SDT_UINT | SDT_NOSAVE, (void*)3979, &_network_port}, + {"port", SDT_UINT | SDT_NOSAVE, (void*)3978, &_network_client_port}, + {"server_port", SDT_UINT | SDT_NOSAVE, (void*)3979, &_network_server_port}, {"sync_freq", SDT_UINT | SDT_NOSAVE, (void*)4, &_network_sync_freq}, {"ahead_frames", SDT_UINT | SDT_NOSAVE, (void*)5, &_network_ahead_frames}, {NULL} diff --git a/ttd.c b/ttd.c index 1ea0ef015..531fe40bf 100644 --- a/ttd.c +++ b/ttd.c @@ -484,6 +484,7 @@ int ttd_main(int argc, char* argv[]) uint startdate = -1; _ignore_wrong_grf = false; musicdriver[0] = sounddriver[0] = videodriver[0] = 0; + _networking_override=false; _game_mode = GM_MENU; _switch_mode = SM_MENU; @@ -496,6 +497,7 @@ int ttd_main(int argc, char* argv[]) case 'v': ttd_strlcpy(videodriver, mgo.opt, sizeof(videodriver)); break; case 'n': { network = 1; + _networking_override=true; if (mgo.opt) { network_conn = mgo.opt; network++; @@ -551,14 +553,17 @@ int ttd_main(int argc, char* argv[]) if (resolution[0]) { _cur_resolution[0] = resolution[0]; _cur_resolution[1] = resolution[1]; } if (startdate != -1) _patches.starting_date = startdate; + // initialize network-core + NetworkCoreInit(); + // enumerate language files InitializeLanguagePacks(); // initialize screenshot formats InitializeScreenshotFormats(); - // initialize airport state machines - InitializeAirports(); + // initialize airport state machines + InitializeAirports(); // Sample catalogue DEBUG(misc, 1) ("Loading sound effects..."); @@ -583,25 +588,21 @@ int ttd_main(int argc, char* argv[]) if (_opt_mod_ptr->diff_level == 9) SetDifficultyLevel(0, _opt_mod_ptr); - if (network) { - _networking = true; - - NetworkInitialize(network_conn); - if (network==1) { - DEBUG(misc, 1) ("Listening on port %d\n", _network_port); - NetworkListen(_network_port); - _networking_server = true; - } else { - DEBUG(misc, 1) ("Connecting to %s %d\n", network_conn, _network_port); - NetworkConnect(network_conn, _network_port); + if ((network) && (_network_available)) { + NetworkLobbyInit(); + if (network_conn!=NULL) { + NetworkCoreConnectGame(network_conn,_network_server_port); + } else { + NetworkCoreConnectGame("auto",_network_server_port); + } } - } while (_video_driver->main_loop() == ML_SWITCHDRIVER) {} - if (network) { - NetworkShutdown(); - } + if (_network_available) { + // shutdown network-core + NetworkCoreShutdown(); + } _video_driver->stop(); _music_driver->stop(); @@ -609,8 +610,8 @@ int ttd_main(int argc, char* argv[]) SaveToConfig(); - // uninitialize airport state machines - UnInitializeAirports(); + // uninitialize airport state machines + UnInitializeAirports(); return 0; } @@ -774,14 +775,14 @@ static void SwitchMode(int new_mode) break; case SM_NEWGAME: - if (_networking) { NetworkStartSync(); } // UGLY HACK HACK HACK + if (_networking) { NetworkStartSync(true); } // UGLY HACK HACK HACK MakeNewGame(); break; normal_load: case SM_LOAD: { // Load game - if (_networking) { NetworkStartSync(); } // UGLY HACK HACK HACK + if (_networking) { NetworkStartSync(true); } // UGLY HACK HACK HACK _error_message = INVALID_STRING_ID; if (!SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_NORMAL)) { @@ -820,6 +821,10 @@ normal_load: case SM_MENU: // Switch to game menu + + if ((_networking) && (!_networking_override)) NetworkCoreDisconnect(); + _networking_override=false; + LoadIntroGame(); break; @@ -951,10 +956,8 @@ void GameLoop() _timer_counter+=8; CursorTick(); - if (_networking) { - NetworkReceive(); - NetworkProcessCommands(); // to check if we got any new commands belonging to the current frame before we increase it. - } + // incomming packets + NetworkCoreLoop(true); if (_networking_sync) { // make sure client's time is synched to the server by running frames quickly up to where the server is. @@ -983,8 +986,8 @@ void GameLoop() MouseLoop(); // send outgoing packets. - if (_networking) - NetworkSend(); + NetworkCoreLoop(false); + if (_game_mode != GM_MENU) MusicLoop(); diff --git a/variables.h b/variables.h index e2a353ac4..21e60e71d 100644 --- a/variables.h +++ b/variables.h @@ -218,14 +218,16 @@ VARDEF int32 _frame_counter_max; // for networking, this is the frame that we ar VARDEF int32 _frame_counter_srv; // for networking, this is the last known framecounter of the server. it is always less than frame_counter_max. // networking settings -VARDEF uint _network_port; +VARDEF bool _network_available; // is network mode available? +VARDEF uint32 _network_ip_list[10]; // Network IPs +VARDEF uint16 _network_game_count; + +VARDEF uint _network_client_port; +VARDEF uint _network_server_port; + VARDEF uint _network_sync_freq; VARDEF uint _network_ahead_frames; -VARDEF uint32 _network_ip_list[10]; // Network IPs -VARDEF char * _network_detected_serverip; // UDP Broadcast detected Server -VARDEF uint32 _network_detected_serverport; // UDP Broadcast detected server-port - VARDEF uint32 _sync_seed_1, _sync_seed_2; VARDEF bool _is_ai_player; // current player is an AI player? - Can be removed if new AI is done @@ -283,6 +285,8 @@ VARDEF SmallFiosItem _file_to_saveload; VARDEF byte _make_screenshot; VARDEF bool _networking; +VARDEF bool _networking_override; // dont shutdown network core when the GameMenu appears. + VARDEF bool _networking_sync; // if we use network mode and the games must stay in sync. VARDEF bool _networking_server; VARDEF bool _networking_queuing; // queueing only? -- cgit v1.2.3-70-g09d2