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

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

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

/*  $Id: client.cpp,v 1.9 2022/11/22 01:35:20 sarrazip Exp $
    client.cpp - main() function for BurgerSpace

    burgerspace - A hamburger-smashing video game.
    Copyright (C) 2001-2022 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 "BurgerSpaceClient.h"

#include "LocalServer.h"
#include "RemoteServer.h"
#include "util.h"

#include <errno.h>
#include <time.h>
#include <string.h>

#include <sys/types.h>

#ifdef HAVE_GETOPT_LONG
#include <getopt.h>
#endif

#ifndef DEFAULT_UDP_SERVER_PORT
#error DEFAULT_UDP_SERVER_PORT must be defined to valid UDP port number
#endif

using namespace std;


#ifdef HAVE_GETOPT_LONG
static struct option knownOptions[] =
{
    { "help",          no_argument,       NULL, 'h' },
    { "version",       no_argument,       NULL, 'v' },
    { "initial-level", required_argument, NULL, 'i' },
    { "ms-per-frame",  required_argument, NULL, 'm' },
    { "no-sound",      no_argument,       NULL, 'n' },
    { "full-screen",   no_argument,       NULL, 'f' },
    { "old-motion",    no_argument,       NULL, 'o' },
    { "z-for-pepper",  no_argument,       NULL, 'z' },
    { "hide-landed-enemies", no_argument, NULL, 'l' },
    { "server",        required_argument, NULL, 's' },
    { "port",          required_argument, NULL, 'p' },
    { "chef",          no_argument,       NULL, 'c' },
    { "enemy",         no_argument,       NULL, 'e' },
    { "no-active-event", no_argument,     NULL, 'a' },

    { NULL, 0, NULL, 0 }  // marks the end
};


static
void
displayVersionNo()
{
    cout << PROGRAM << " " << VERSION << endl;
}


static
void
displayHelp()
{
    cout << "\n";

    displayVersionNo();

    cout <<
"\n"
"Copyright (C) 2001-2021 Pierre Sarrazin <http://sarrazip.com/>\n"
"This program is free software; you may redistribute it under the terms of\n"
"the GNU General Public License.  This program has absolutely no warranty.\n"
    ;

    cout <<
"\n"
"Known options:\n"
"--help                 Display this help page and exit\n"
"--version              Display this program's version number and exit\n"
"--initial-level=N      Start game at level N (N >= 1) [default=1]\n"
"--ms-per-frame=N       N milliseconds per animation frame [default=55]\n"
"                       Min. 1, max. 1000.  50 ms means 20 frames per second\n"
"--no-sound             Disable sound effects [default is to enable them]\n"
"--full-screen          Attempt to use full screen mode [default is window mode]\n"
"--old-motion           Use the old player motion system [default is new system]\n"
"--z-for-pepper         Use Z key to shoot pepper [default is Ctrl key]\n"
"--hide-landed-enemies  After an enemy lands in a plate, hide it until it revives\n"
"--server=HOSTNAME      Run as a network client and use specified server\n"
"--port=PORT            Specify UDP port of server (only relevant with --server)\n"
"--chef                 In a network game, play the role of the chef\n"
"--enemy                In a network game, play the role of one of the enemies\n"
"--no-active-event      Do not pause when the window becomes inactive.\n"
"\n"
    ;
}
#endif  /* HAVE_GETOPT_LONG */


static RemoteServer *
createRemoteServer(BurgerSpaceClient &client,
                   const string &serverHostname,
                   unsigned short port,
                   Role role,
                   int &errorCode)
{
    // Try to resolve the server hostname:
    hostent *ent = gethostbyname(serverHostname.c_str());
    if (ent == NULL)
    {
        errorCode = -1;
        return NULL;
    }

    try
    {
        // Create and initialize an object representing the remote server:
        RemoteServer *rs = new RemoteServer(client,
                                            (unsigned char *) ent->h_addr_list[0],
                                            port,
                                            role);
        rs->describeClient();
        rs->requestLevelDescription();
        errorCode = 0;
        return rs;
    }
    catch (int errNo)
    {
        errorCode = errNo;
        return NULL;
    }
}


int
main(int argc, char *argv[])
{
    /*  Default game parameters:
    */
    int initLevelNo = 1;
    int millisecondsPerFrame = 55;
    bool useSound = true;
    bool fullScreen = false;
    bool useOldMotionMode = false;
    bool hideLandedEnemies = false;
    SDLKey pepperKey = SDLK_LCTRL;
    string serverHostname;  // UDP server hostname if not empty; local server otherwise
    unsigned short port = DEFAULT_UDP_SERVER_PORT;
    Role role = ROLE_SPECTATOR;
    bool processActiveEvent = true;  // if true, SDL_ACTIVEEVENT gets processed (see BurgerSpaceClient::runClientMode())


    #ifdef HAVE_GETOPT_LONG

    /*  Interpret the command-line options:
    */
    int c;
    do
    {
        c = getopt_long(argc, argv, "", knownOptions, NULL);

        switch (c)
        {
            case EOF:
                break;  // nothing to do

            case 'i':
                {
                    errno = 0;
                    long n = strtol(optarg, NULL, 10);
                    if (errno == ERANGE || n < 1 || n > 500)
                    {
                        cout << "Invalid initial level number.\n";
                        displayHelp();
                        return EXIT_FAILURE;
                    }

                    initLevelNo = int(n);
                }
                break;

            case 'm':
                {
                    errno = 0;
                    long n = strtol(optarg, NULL, 10);
                    if (errno == ERANGE || n < 1 || n > 1000)
                    {
                        cout << "Invalid number of ms per frame.\n";
                        displayHelp();
                        return EXIT_FAILURE;
                    }

                    millisecondsPerFrame = int(n);
                }
                break;

            case 'n':
                useSound = false;
                break;

            case 'f':
                fullScreen = true;
                break;

            case 'o':
                useOldMotionMode = true;
                break;

            case 'l':
                hideLandedEnemies = true;  // behavior of versions <= 1.9.4
                break;

            case 'z':
                pepperKey = SDLK_z;
                break;

            case 's':
                serverHostname = optarg;
                break;

            case 'p':
                {
                    char *end = NULL;
                    long n = strtol(optarg, &end, 10);
                    if (n < 0 || n > 65535 || end == optarg || *end != '\0')
                    {
                        cerr << PROGRAM << ": --port: invalid UDP port number " << optarg << endl;
                        return EXIT_FAILURE;
                    }
                    port = static_cast<unsigned short>(n);
                }
                break;

            case 'c':
                role = ROLE_CHEF;
                break;

            case 'e':
                role = ROLE_ENEMY;
                break;
        
            case 'a':  // do not process SDL_ACTIVEEVENT, i.e., do not pause when window loses focus
                processActiveEvent = false;
                break;

            case 'v':
                displayVersionNo();
                return EXIT_SUCCESS;

            case 'h':
                displayHelp();
                return EXIT_SUCCESS;

            default:
                displayHelp();
                return EXIT_FAILURE;
        }
    } while (c != EOF && c != '?');

    #elif defined(_MSC_VER)

    if (argc >= 2)
    {
        if (strcmp(argv[1], "--help") == 0)
        {
            cout << PROGRAM << " " << VERSION << ": copyrighted and distributed under GNU GPL\n"
                 << "\n"
                 << "Stand-alone:    " PACKAGE "\n"
                 << "Network client: " PACKAGE " SERVER [chef|enemy]\n";
            return EXIT_SUCCESS;
        }

        // argv[1] expected to be of form "SERVER KEYWORD" where
        // SERVER is a hostname and KEYWORD is either 'chef' or 'enemy'
        size_t i;
        const char *arg = argv[1];
        for (i = 0; arg[i] != '\0' && !isspace(arg[i]); ++i)
            ;

        serverHostname.assign(arg, i);  // prefix of 'arg' up to space or end

        for ( ; isspace(arg[i]); ++i)
            ;

        if (strcmp(arg + i, "chef") == 0)
            role = ROLE_CHEF;
        else if (strcmp(arg + i, "enemy") == 0)
            role = ROLE_ENEMY;
    }

    #endif  /* defined(_MSC_VER) */


    /*  Initialize the random number generator:
    */
    const char *s = getenv("SEED");
    unsigned int seed = (unsigned int) (s != NULL ? atol(s) : time(NULL));
    #ifndef NDEBUG
    cerr << "seed = " << seed << endl;
    cerr << "init-level-no = " << initLevelNo << endl;
    #endif
    srand(seed);


    try
    {
        bool standAlone = serverHostname.empty();

        BurgerSpaceClient theBurgerSpaceClient(
                                standAlone ? ROLE_CHEF : role,
                                !standAlone /* show role */,
                                useSound, pepperKey, fullScreen,
                                standAlone && processActiveEvent);

        BurgerSpaceServerInterface *serverInterface = NULL;

        if (standAlone)
        {
            // Create a "local server":
            LocalServer *ls = new LocalServer(theBurgerSpaceClient, initLevelNo, useOldMotionMode, hideLandedEnemies);
            ls->finishInit();
            serverInterface = ls;
        }
        else  // remote server version:
        {
            // Upon a successful connection, createRemoteServer() sends
            // a description of this client to the server and requests
            // a level update from the server.
            //
            int errorCode = 0;
            serverInterface = createRemoteServer(theBurgerSpaceClient, serverHostname, port, role, errorCode);
            if (serverInterface == NULL)
            {
                switch (errorCode)
                {
                    case -1:
                        cerr << PROGRAM << ": failed to resolve hostname "
                                        << serverHostname << endl;
                        break;
                    default:
                        assert(errorCode > 0);
                        cerr << PROGRAM << ": failed to initialize UDP socket for server "
                                        << serverHostname << ":" << port << ": "
                                        << strerror(errorCode) << endl;
                }
                return EXIT_FAILURE;
            }

            cout << PROGRAM << ": using server " << serverHostname << " on port " << port << endl;
        }

        // Associate the created server object with the client,
        // and run the game:
        theBurgerSpaceClient.setServer(serverInterface);
        theBurgerSpaceClient.runClientMode(millisecondsPerFrame);
        serverInterface->disconnect();  // in remote mode, this notifies server of client's departure

        delete serverInterface;
    }
    catch (const string &s)
    {
        cerr << PROGRAM << ": server init error: " << s << endl;
        return EXIT_FAILURE;
    }
    catch (int e)
    {
        cerr << PROGRAM << ": init error # " << e << endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}


#ifdef _MSC_VER
int CALLBACK WinMain(__in HINSTANCE hInstance,
                     __in HINSTANCE hPrevInstance,
                     __in LPSTR lpCmdLine,
                     __in int nCmdShow)
{
    char *argv[] = { PROGRAM, NULL };
    return main(1, argv);
}
#endif  // def _MSC_VER