//----------------------------------------------------------------------------- // Filename: njamnet.cpp // Created: 05. Jun 2003. by Milan Babuskov // // Purpose: Manage all networking stuff // // This file implements methods of NjamEngine class // which are defined in njam.h // // Tabs should be at 4 spaces. // Each section is separated with line: //-------... /*----------------------------------------------------------------------------- Copyright 2003 Milan Babuskov This file is part of Njam (http://njam.sourceforge.net). Njam 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; either version 2 of the License, or (at your option) any later version. Njam 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 Njam in file COPYING; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -----------------------------------------------------------------------------*/ #include #include #include "SDL.h" #include "SDL_mixer.h" #include "SDL_thread.h" #include "SDL_net.h" #include "njamutils.h" #include "njamfont.h" #include "njam.h" //----------------------------------------------------------------------------- void NjamEngine::Disconnect() { if (m_PeerSocket) SDLNet_TCP_Close(m_PeerSocket); if (m_GameType == gtHostDuel && m_ServerSocket) SDLNet_TCP_Close(m_ServerSocket); m_ServerSocket = NULL; m_PeerSocket = NULL; } //----------------------------------------------------------------------------- // return false is game is canceled or network error occurs bool NjamEngine::SetupNetwork() { if(!m_NetworkAvailable) // init SDL networking if needed { if (SDLNet_Init() == -1) { printf("SDLNet_Init: %s\n", SDLNet_GetError()); return false; } else m_NetworkAvailable = true; } else Disconnect(); // clean up if there is anything left from previous sessions if (m_GameType == gtHostDuel) { IPaddress ip; if(SDLNet_ResolveHost(&ip,NULL,5547) == -1) // create a listening TCP socket (server) { printf("SDLNet_ResolveHost: %s\n", SDLNet_GetError()); return false; } m_ServerSocket = SDLNet_TCP_Open(&ip); if(!m_ServerSocket) { printf("SDLNet_TCP_Open: %s\n", SDLNet_GetError()); return false; } } m_PeerSocket = NULL; if (m_GameType == gtJoinDuel) { bool HasTried = false; while (!m_PeerSocket) { if (!EnterServerIP(HasTried)) // or exit return false; HasTried = true; // connect to localhost using TCP (client) IPaddress ip; LogFile("CLIENT: Resolving server name...\n"); if(SDLNet_ResolveHost(&ip, m_GameOptions.ServerIP, 5547) == -1) { printf("SDLNet_ResolveHost: %s\n", SDLNet_GetError()); continue; } LogFile("CLIENT: Connecting to server...\n"); m_PeerSocket = SDLNet_TCP_Open(&ip); if(!m_PeerSocket) { printf("SDLNet_TCP_Open: %s\n", SDLNet_GetError()); LogFile("CLIENT: Failed.\n"); continue; } } LogFile("CLIENT: Connected.\n"); } while (true) // show network connect screen { if (m_GameType == gtHostDuel && !m_PeerSocket) // incoming connections { m_PeerSocket = SDLNet_TCP_Accept(m_ServerSocket); // accept a new connection coming in on Server Socket if (m_PeerSocket) { IPaddress *ip = SDLNet_TCP_GetPeerAddress(m_PeerSocket); if(!ip) { printf("SDLNet_TCP_GetPeerAddress: %s\n", SDLNet_GetError()); printf("This may be a server socket.\n"); m_PeerSocket = NULL; } else printf("Client connected!\n"); } } RenderNetworkScreen(); int player; // declared outside because of switch glitch ;) bool Ready = false; SDLKey key = NjamGetch(false); switch (key) { case SDLK_r: // select players (each player has to press any key) to toggle case SDLK_d: case SDLK_f: case SDLK_g: player = (m_GameType == gtHostDuel ? 1 : 3); m_Player[player].Playing = !m_Player[player].Playing; break; case SDLK_UP: case SDLK_DOWN: case SDLK_LEFT: case SDLK_RIGHT: player = (m_GameType == gtHostDuel ? 0 : 2); m_Player[player].Playing = !m_Player[player].Playing; break; case SDLK_ESCAPE: Disconnect(); return false; case SDLK_SPACE: // SPACE to start the game, ESC to exit if (m_GameType == gtHostDuel && (m_Player[0].Playing || m_Player[1].Playing) || m_GameType == gtJoinDuel && (m_Player[2].Playing || m_Player[3].Playing)) Ready = true; }; if (Ready) // server starts the game? { if (!m_PeerSocket) printf("Cannot start game without client"); else break; // let's go } } LogFile("Checking if peer is there?\n"); if (!m_PeerSocket) // disconnected (just in case) { Disconnect(); return false; } LogFile("Sending CID\n"); Uint8 data[5]; // send Connection Initialization Data (CID) data[0] = 'N'; data[1] = 'J'; int player = (m_GameType == gtHostDuel ? 0 : 2); data[2] = (Uint8)(m_Player[0+player].Playing ? 1 : 0); data[3] = (Uint8)(m_Player[1+player].Playing ? 1 : 0); if (4 > SDLNet_TCP_Send(m_PeerSocket, data, 4)) { printf("SDLNet_TCP_Send: %s\n", SDLNet_GetError()); Disconnect(); return false; } RenderNetworkScreen(true); // show: waiting... LogFile("Waiting for peer CID\n"); // receive CID int result = SDLNet_TCP_Recv(m_PeerSocket, data, 4); if (result < 4) { printf("SDLNet_TCP_Open: %s\n", SDLNet_GetError()); Disconnect(); return false; } else { LogFile("Received some data, reading\n"); if (data[0] == 'N') { LogFile("Data seems to be ok.\n"); int player = (m_GameType == gtHostDuel ? 2 : 0); m_Player[0+player].Playing = data[2]; m_Player[1+player].Playing = data[3]; } else { LogFile("Bad data received.\n"); Disconnect(); return false; } } LogFile("ALL ok. Showing who is playing...\n"); RenderNetworkScreen(true); SDL_Delay(1000); // just to be visible that other players are playing return true; // all ok, start the game } //----------------------------------------------------------------------------- void NjamEngine::RenderNetworkScreen(bool GameStarting) { SDL_BlitSurface(m_NetLobbyImage, NULL, m_Screen, NULL); m_NetLobbyImage; SDL_Rect src, dest; NjamSetRect(src, 100, 0, 25, 25); if (m_GameType == gtHostDuel && m_ServerSocket) { NjamSetRect(dest, 103, 143); SDL_BlitSurface(m_SpriteImage, &src, m_Screen, &dest); } if (m_GameType == gtJoinDuel) { SDL_Rect r; NjamSetRect(r, 99, 497, 430, 40); NjamSetRect(dest, 99, 137); SDL_BlitSurface(m_NetLobbyImage, &r, m_Screen, &dest); } if (GameStarting) { NjamSetRect(dest, 103, 464); SDL_BlitSurface(m_SpriteImage, &src, m_Screen, &dest); } if (m_PeerSocket) { NjamSetRect(dest, 103, 422); SDL_BlitSurface(m_SpriteImage, &src, m_Screen, &dest); } else { SDL_Rect r; NjamSetRect(r, 99, 497, 430, 40); NjamSetRect(dest, 99, 457); SDL_BlitSurface(m_NetLobbyImage, &r, m_Screen, &dest); } for (int i=0; i<4; i++) { if (m_Player[i].Playing) { NjamSetRect(dest, 103, 215 + i * 30); src.x = 75 + i * 25; SDL_BlitSurface(m_SpriteImage, &src, m_Screen, &dest); } } SDL_Flip(m_Screen); } //----------------------------------------------------------------------------- // show entry dialog, and accept server address in m_GameOptions.ServerIP bool NjamEngine::EnterServerIP(bool HasTried) { if (HasTried) { SDL_Rect dest; NjamSetRect(dest, 249, 199); SDL_BlitSurface(m_NetEnterImage, NULL, m_Screen, &dest); m_FontYellow->WriteTextXY(m_Screen, 273, 295, "CONNECTION FAILED."); SDL_Flip(m_Screen); SDL_Delay(2000); } Uint32 white = SDL_MapRGB(m_Screen->format, 255, 255, 255); Uint32 t = SDL_GetTicks(); bool CursorOn = true; while (true) { SDL_Rect dest; NjamSetRect(dest, 249, 199); SDL_BlitSurface(m_NetEnterImage, NULL, m_Screen, &dest); m_FontYellow->WriteTextXY(m_Screen, 273, 295, m_GameOptions.ServerIP); // get len int l = 0; while (m_GameOptions.ServerIP[l]) l++; if (SDL_GetTicks() > t + 400) // make the cursor blink { t = SDL_GetTicks(); CursorOn = !CursorOn; } if (CursorOn) { NjamSetRect(dest, 275 + 10 * l, 295, 10, 15); // draw cursor SDL_FillRect(m_Screen, &dest, white); } SDL_Flip(m_Screen); SDLKey key = NjamGetch(false); if (key >= '0' && key <= '9' || key == '.') { if (l < 15) { m_GameOptions.ServerIP[l] = key; m_GameOptions.ServerIP[l+1] = '\0'; } } if (key == SDLK_BACKSPACE && l > 0) // backspace m_GameOptions.ServerIP[l-1] = '\0'; if ((key == SDLK_KP_ENTER || key == SDLK_RETURN) && m_GameOptions.ServerIP[0]) // enter { SDL_Rect dest; NjamSetRect(dest, 249, 199); SDL_BlitSurface(m_NetEnterImage, NULL, m_Screen, &dest); m_FontYellow->WriteTextXY(m_Screen, 273, 295, "CONNECTING..."); SDL_Flip(m_Screen); return true; } if (key == SDLK_ESCAPE) return false; } } //----------------------------------------------------------------------------- bool NjamEngine::NetworkMap() { LogFile("Exchanging map data.\n"); if (!m_PeerSocket) { Disconnect(); return false; } // draw previos map stats again for background (if doublebuffer is used) SDL_FillRect(m_Screen, NULL, 0); DrawGameStats(); int size = MAPH*MAPW; Uint8 *buffer = m_Maps.GetTiles(); if (m_GameType == gtHostDuel) // send map data { LogFile("SERVER: sending map data\n"); if (size > SDLNet_TCP_Send(m_PeerSocket, buffer, size)) { printf("SDLNet_TCP_Send: %s\n", SDLNet_GetError()); Disconnect(); return false; } // Render SENDING DATA and wait for ACK (this is because otherwise the server would start READY, SET, GO... earlier) SDL_Rect dest; NjamSetRect(dest, 278, 216); SDL_BlitSurface(m_NetSendImage, NULL, m_Screen, &dest); SDL_Flip(m_Screen); Uint8 c; if (SDLNet_TCP_Recv(m_PeerSocket, &c, 1) < 1 || c != 'A') { printf("SDLNet_TCP_Recv: %s\n", SDLNet_GetError()); Disconnect(); return false; } } else // recv map data { SDL_Rect dest; NjamSetRect(dest, 278, 216); SDL_BlitSurface(m_NetSendImage, NULL, m_Screen, &dest); SDL_Flip(m_Screen); LogFile("CLIENT: receiving map data\n"); int result = SDLNet_TCP_Recv(m_PeerSocket, buffer, size); if (result < size) { printf("SDLNet_TCP_Recv: %s\n", SDLNet_GetError()); Disconnect(); return false; } else { LogFile("Received map data\n"); Uint8 c = 'A'; if (1 > SDLNet_TCP_Send(m_PeerSocket, &c, 1)) // Send ACK { printf("SDLNet_TCP_Send: %s\n", SDLNet_GetError()); Disconnect(); return false; } } } return true; } //----------------------------------------------------------------------------- // Packet: // pos len data // 0 1 char 'N' make sure the packet is ok // 1 1 Type of packet: 0 = game packet, 1 = exit game packet // 2 7 Player1 data (rotate, x, y, xo, yo, vx, vy) // 9 7 Player2 data // 16 GHOSTMAX * 5 Ghost data (x , y, xo, yo, delay) bool NjamEngine::ExchangeNetwork() { m_NetworkEscapedGame = false; if (m_GameType != gtHostDuel && m_GameType != gtJoinDuel) return true; int ghostsize = 5; Uint8 buffer[16 + GHOSTMAX * 5]; int size = 16 + GHOSTMAX * ghostsize; buffer[0] = 'N'; buffer[1] = 0; if (m_GameType == gtHostDuel) { // stream data to buffer for (int i=0; i<2; i++) { buffer[2+i*7] = m_Sprite[i].rotate; buffer[3+i*7] = m_Sprite[i].x; buffer[4+i*7] = m_Sprite[i].y; buffer[5+i*7] = m_Sprite[i].xo; buffer[6+i*7] = m_Sprite[i].yo; buffer[7+i*7] = 2 + m_Sprite[i].vx; // to eliminate negative numbers buffer[8+i*7] = 2 + m_Sprite[i].vy; } for (int i=0; i SDLNet_TCP_Send(m_PeerSocket, buffer, size)) { printf("SDLNet_TCP_Send: %s\n", SDLNet_GetError()); Disconnect(); return false; } // wait for reply (must have the same counter!) if (SDLNet_TCP_Recv(m_PeerSocket, buffer, 16) < 16) { printf("SDLNet_TCP_Recv: %s\n", SDLNet_GetError()); Disconnect(); return false; } if (buffer[1] == 1) // client signaled the end of map { m_NetworkEscapedGame = true; return true; } // process data for (int i=0; i<2; i++) { m_Sprite[i+2].rotate = buffer[2+i*7]; m_Sprite[i+2].x = buffer[3+i*7]; m_Sprite[i+2].y = buffer[4+i*7]; m_Sprite[i+2].xo = buffer[5+i*7]; m_Sprite[i+2].yo = buffer[6+i*7]; m_Sprite[i+2].vx = (int)(buffer[7+i*7]) - 2; m_Sprite[i+2].vy = (int)(buffer[8+i*7]) - 2; } } else { // wait for data if (SDLNet_TCP_Recv(m_PeerSocket, buffer, size) < size) { printf("SDLNet_TCP_Recv: %s\n", SDLNet_GetError()); Disconnect(); return false; } if (buffer[1] == 1) // server signaled the end of map { m_NetworkEscapedGame = true; return true; } // process data for (int i=0; i<2; i++) { m_Sprite[i].rotate = buffer[2+i*7]; m_Sprite[i].x = buffer[3+i*7]; m_Sprite[i].y = buffer[4+i*7]; m_Sprite[i].xo = buffer[5+i*7]; m_Sprite[i].yo = buffer[6+i*7]; m_Sprite[i].vx = (int)(buffer[7+i*7]) - 2; m_Sprite[i].vy = (int)(buffer[8+i*7]) - 2; } for (int i=0; i SDLNet_TCP_Send(m_PeerSocket, buffer, 16)) { printf("SDLNet_TCP_Send: %s\n", SDLNet_GetError()); Disconnect(); return false; } } return true; } //-----------------------------------------------------------------------------