Codebase list burgerspace / lintian-fixes/main src / RemoteServer.cpp
lintian-fixes/main

Tree @lintian-fixes/main (Download .tar.gz)

RemoteServer.cpp @lintian-fixes/mainraw · history · blame

/*  $Id: RemoteServer.cpp,v 1.5 2012/10/07 21:29:24 sarrazip Exp $
    RemoteServer.cpp - UDP server

    burgerspace - A hamburger-smashing video game.
    Copyright (C) 2010 Pierre Sarrazin <http://sarrazip.com/>

    This program 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.

    This program 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 this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "RemoteServer.h"

#include "BurgerSpaceClient.h"

#include <flatzebra/Couple.h>

#include <stdio.h>
#include <errno.h>
#include <limits.h>

#ifdef _MSC_VER
#include <io.h>  // for close()
#else
#include <unistd.h>  // for close()
#endif


using namespace std;
using namespace flatzebra;


RemoteServer::RemoteServer(BurgerSpaceClient &_client,
                                                unsigned char _serverIPAddr[4],
                                                unsigned short _serverUDPPort,
                                                Role _role)
  : client(_client),
    role(_role),
    sock(socket(AF_INET, SOCK_DGRAM, 0)),
    serverUDPPort(_serverUDPPort),
    buffer(NULL),
    acc(),
    lastPlayerCommandSent()
{
    if (serverIPAddr == NULL)
        throw EINVAL;
    if (sock < 0)
        throw errno;
    memcpy(serverIPAddr, _serverIPAddr, 4);
    buffer = new char[MAX_LEN_PACKET];

#ifdef _MSC_VER
    u_long iMode = 1;
    ioctlsocket(sock, FIONBIO, &iMode);  // non blocking mode
#endif
}


//virtual
RemoteServer::~RemoteServer()
{
    if (sock >= 0)
    {
        #ifdef _MSC_VER
        closesocket(sock);
        #else
        close(sock);
        #endif
    }

    delete [] buffer;
}


void
RemoteServer::describeClient()
{
    char s[] =
    {
        char(CLIENT_DESC_PACKET),
        char(role)
    };
    const size_t len = sizeof(s) / sizeof(s[0]);
    ssize_t numBytesSent = sendToServer(s, len);
    if (numBytesSent != ssize_t(len))
        cout << PROGRAM << ": tried to send client descriptor of " << len
                << " bytes but only " << numBytesSent << " bytes sent" << endl;
}


void
RemoteServer::requestLevelDescription()
{
    char packet = char(LEVEL_DESC_REQUEST_PACKET);
    ssize_t numBytesSent = sendToServer(&packet, 1);
    if (numBytesSent != 1)
        cout << PROGRAM << ": tried to send level description request but only " << numBytesSent << " bytes sent" << endl;
}


//virtual
void
RemoteServer::startNewGame()
{
    char s[] =
    {
        char(START_NEW_GAME_PACKET)
    };
    const size_t len = sizeof(s) / sizeof(s[0]);
    ssize_t numBytesSent = sendToServer(s, len);
    if (numBytesSent != ssize_t(len))
        cout << PROGRAM << ": tried to send start request of " << len
                << " bytes but only " << numBytesSent << " bytes sent" << endl;
}


//virtual
bool
RemoteServer::isPaused() const
{
    return false;  // No player can pause the game in client-server mode.
}


//virtual
void
RemoteServer::pauseGame()
{
    // No player can pause the game in client-server mode.
}


//virtual
void
RemoteServer::resumeGame()
{
    // No player can pause the game in client-server mode.
}


//virtual
void
RemoteServer::setChefRequest(const bool desiredDirections[4], bool shootPepper)
{
    char s[] =
    {
        char(PLAYER_COMMAND_PACKET),
        char(desiredDirections[0]),
        char(desiredDirections[1]),
        char(desiredDirections[2]),
        char(desiredDirections[3]),
        char(shootPepper)
    };
    const size_t len = sizeof(s) / sizeof(s[0]);
    if (lastPlayerCommandSent.empty() || lastPlayerCommandSent.compare(0, len, s, len) != 0)
    {
        ssize_t numBytesSent = sendToServer(s, len);
        if (numBytesSent != ssize_t(len))
            cout << PROGRAM << ": tried to send player command but only " << numBytesSent << " bytes sent" << endl;

        lastPlayerCommandSent.assign(s, len);
    }
}


//virtual
void
RemoteServer::setEnemyRequest(const bool desiredDirections[4])
{
    setChefRequest(desiredDirections, false);
}


ssize_t
RemoteServer::sendToServer(const void *s, size_t len)
{
    if (s == NULL || len == 0)
    {
        assert(!"RemoteServer::sendToServer: empty packet");
        return 0;
    }

    assert(serverIPAddr != NULL);

    sockaddr_in to;
    memset(&to, '\0', sizeof(to));
    to.sin_family = AF_INET;
    memcpy(&to.sin_addr, serverIPAddr, 4);
    to.sin_port = htons(serverUDPPort);
    return sendto(sock, reinterpret_cast<const char *>(s), len, 0, (sockaddr *) &to, sizeof(to));
}


//virtual
bool
RemoteServer::update()
{
    assert(buffer != NULL);

    // Receive and interpret messages from the server, if any.

    if (sock < 0)
    {
        cout << PROGRAM << ": invalid socket" << endl;
        return false;
    }

    // Process all immediately available UDP packets:
    for (;;)
    {
        sockaddr_in from;
        memset(&from, '\0', sizeof(from));
        socklen_t length = sizeof(from);
        int flags = 0;
        #ifndef _MSC_VER
        flags = MSG_DONTWAIT;
        #endif
        ssize_t numBytesReceived = recvfrom(sock, buffer, MAX_LEN_PACKET,
                                            flags, (sockaddr *) &from, &length);
        int e = errno;
        //cerr << "RemoteServer::update: " << numBytesReceived << " " << e << endl;
        if (numBytesReceived < 0)  // if error or no packet available
        {
            #ifdef _MSC_VER

            int winsockError = WSAGetLastError();
            if (winsockError == WSAEWOULDBLOCK)  // if no packet available
                return true;
            cout << PROGRAM << ": read error on socket: " << winsockError << endl;  // see WinError.h

            #else

            if (e == EAGAIN)  // if no packet available
                return true;
            cout << PROGRAM << ": read error on socket: " << strerror(e) << endl;

            #endif

            return false;
        }
        if (numBytesReceived == 0)
            continue;
        //cerr << "RemoteServer::update: received " << numBytesReceived << " byte(s)" << endl;

        processReceivedBytes(buffer, numBytesReceived);
    }

    return true;
}


//virtual
void
RemoteServer::disconnect()
{
    char s[] =
    {
        char(DISCONNECT_PACKET)
    };
    const size_t len = sizeof(s) / sizeof(s[0]);
    ssize_t numBytesSent = sendToServer(s, len);
    if (numBytesSent != ssize_t(len))
        cout << PROGRAM << ": tried to disconnect packet but only " << numBytesSent << " bytes sent" << endl;
}


//virtual
bool
RemoteServer::saveGame(std::ostream &/*out*/)
{
    return false;  // save not supported in client-server mode
}


//virtual
int
RemoteServer::loadGame(std::istream &/*in*/)
{
    return -1;  // load not supported in client-server mode
}


void
RemoteServer::processReceivedBytes(const char *s, ssize_t len)
{
    assert(s != NULL);
    assert(len > 0);

    acc.append(s, len);
    if (acc.length() < 2)
        return;  // nothing to do

    size_t origAccLen = acc.length();

    ServerPacketType type = static_cast<ServerPacketType>(* (uint16_t *) acc.data());
    //cerr << "len=" << len << ", type=" << type << ", acc=" << acc.length() << endl;

    if (type == UPDATE_LEVEL_PACKET && acc.length() >= 2 + 12)
    {
        uint16_t levelNo = * (uint16_t *) &acc[2];
        uint16_t numRows = * (uint16_t *) &acc[4];
        uint16_t numColumns = * (uint16_t *) &acc[6];
        uint16_t levelPosX = * (uint16_t *) &acc[8];
        uint16_t levelPosY = * (uint16_t *) &acc[10];
        uint16_t descLen = * (uint16_t *) &acc[12];
        string desc(acc, 14, descLen);

        if (false) cout << "processReceivedBytes/0: "
                << levelNo << " "
                << numRows << " "
                << numColumns << " "
                << int16_t(levelPosX) << " "
                << int16_t(levelPosY) << " "
                << descLen << " '"
                << desc << "'" << endl;
        assert(uint16_t(desc.length()) == numRows * numColumns);

        client.handleLevelUpdate(int(levelNo), size_t(numRows), size_t(numColumns),
                                    Couple(levelPosX, levelPosY), desc);

        acc.erase(0, 14 + descLen);
    }
    else if (type == UPDATE_SPRITE_PACKET && acc.length() >= 4)
    {
        // Expected at offset 2:
        // number of sprite updates, then array of updates.

        size_t numUpdates = * (uint16_t *) &acc[2];
        size_t reqLen = 2 + 2 + numUpdates * UPDATE_SPRITE_PACKET_LEN;
        assert(acc.length() >= reqLen);

        for (size_t i = 0; i < numUpdates; ++i)
        {
            size_t j = 2 + 2 + i * UPDATE_SPRITE_PACKET_LEN;
            uint32_t id = * (uint32_t *) &acc[j];
            uint16_t stype = * (uint16_t *) &acc[j + 4];
            uint16_t posX = * (uint16_t *) &acc[j + 6];
            uint16_t posY = * (uint16_t *) &acc[j + 8];
            uint16_t pixmapIndex = * (uint16_t *) &acc[j + 10];

            BurgerSpaceServer::SpriteType spriteType = BurgerSpaceServer::SpriteType(stype);
            if (spriteType == BurgerSpaceServer::NO_SPRITE)
                client.handleSpriteDeletion(id);
            else
                client.handleSpriteUpdate(id,
                                        spriteType,
                                        Couple(posX, posY),
                                        pixmapIndex == USHRT_MAX ? size_t(-1) : size_t(pixmapIndex));
        }

        acc.erase(0, reqLen);
    }
    else if (type == UPDATE_SCORE_PACKET && acc.length() >= 2 + 10)
    {
        uint32_t score = * (uint32_t *) &acc[2];
        uint16_t lives = * (uint16_t *) &acc[6];
        uint16_t peppers = * (uint16_t *) &acc[8];
        uint16_t level = * (uint16_t *) &acc[10];

        client.handleScoreUpdate(long(score), int(lives), int(peppers), int(level));

        acc.erase(0, 2 + 10);
    }
    else if (type == PLAY_SOUND_EFFECT_PACKET && acc.length() >= 2 + 2)
    {
        uint16_t se = * (uint16_t *) &acc[2];

        client.handleSoundEffect(BurgerSpaceServer::SoundEffect(se));

        acc.erase(0, 2 + 2);
    }
    else if (type == ROLE_ASSIGNMENT_PACKET && acc.length() >= 2 + 1)
    {
        char r = * (char *) &acc[2];
        client.handleRoleAssignment(static_cast<Role>(r));
        acc.erase(0, 2 + 1);
    }
    else
        cout << PROGRAM << ": invalid server packet of type " << type << endl;

    if (acc.length() == origAccLen)
        cout << PROGRAM << ": packet accumulator not consumed" << endl;
}