/* * CrossFire, A Multiplayer game for X-windows * * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team * Copyright (C) 2001 Mark Wedel * Copyright (C) 1992 Frank Tore Johansen * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * The author can be reached via e-mail to */ /** * \file * Socket general functions * * \date 2003-12-02 * * Mainly deals with initialization and higher level socket * maintenance (checking for lost connections and if data has arrived.) * The reading of data is handled in ericserver.c */ #include #include #include #include #include #include #include #include #include #include #include #include sockvec clients; /** * Initializes a connection. Really, it just sets up the data structure, * socket setup is handled elsewhere. We do send a version to the * client. */ client::client (int fd, const char *peername) : fd (fd), host (strdup (peername)), socket_ev (this, &client::socket_cb), cmd_ev (this, &client::cmd_cb), cc_inv (this), cc_other (this) { refcnt_inc (); // the socket is an external reference mss = 1500 - 66; // 1500 typical ethernet frame, 66 typical tcp header overhead { struct linger linger_opt; linger_opt.l_onoff = 0; linger_opt.l_linger = 0; if (setsockopt (fd, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof (struct linger))) LOG (llevError, "SO_LINGER: %s\n", strerror (errno)); } { int val; #ifdef IP_TOS val = IPTOS_LOWDELAY; if (setsockopt (fd, IPPROTO_IP, IP_TOS, &val, sizeof (val))) LOG (llevError, "IP_TOS: %s\n", strerror (errno)); #endif #ifdef TCP_NODELAY val = 1; if (setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof (val))) LOG (llevError, "TCP_NODELAY: %s\n", strerror (errno)); #endif // set some very aggressive keepalive parameters #ifdef TCP_KEEPIDLE val = 10; if (setsockopt (fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof (val))) LOG (llevError, "TCP_KEEPIDLE: %s\n", strerror (errno)); #endif #ifdef TCP_KEEPCNT val = 5; if (setsockopt (fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof (val))) LOG (llevError, "TCP_KEEPCNT: %s\n", strerror (errno)); #endif #ifdef TCP_KEEPINTVL val = 1; if (setsockopt (fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof (val))) LOG (llevError, "TCP_KEEPINTVL: %s\n", strerror (errno)); #endif // try to find the mss value in use #ifdef TCP_MAXSEG socklen_t sl = sizeof (val); if (!getsockopt (fd, IPPROTO_TCP, TCP_MAXSEG, &val, &sl) && sl == sizeof (val)) mss = val; #endif } { int bufsize = 65535; /*Supposed absolute upper limit */ int oldbufsize; int buflen = sizeof (int); if (fcntl (fd, F_SETFL, O_NONBLOCK) == -1) LOG (llevError, "InitConnection: Error on fcntl.\n"); if (getsockopt (fd, SOL_SOCKET, SO_SNDBUF, (char *) &oldbufsize, (socklen_t *) & buflen) == -1) oldbufsize = 0; if (oldbufsize < bufsize) if (setsockopt (fd, SOL_SOCKET, SO_SNDBUF, (char *) &bufsize, sizeof (&bufsize))) LOG (llevError, "InitConnection: setsockopt unable to set output buf size to %d\n", bufsize); buflen = sizeof (oldbufsize); getsockopt (fd, SOL_SOCKET, SO_SNDBUF, (char *) &oldbufsize, (socklen_t *) & buflen); } state = ST_SETUP; mapmode = Map0Cmd; darkness = 1; mapx = 11; mapy = 11; itemcmd = 1; /* Default is version item1 command */ max_rate = 100000 / (1000000 / MAX_TIME); // ~1mbit is assumed per default /* Do this so we don't send a face command for the client for * this face. Face 0 is sent to the client to say clear * face information. */ faces_sent[0] = true; socket_ev.prio (PE_PRIO_NORMAL); socket_ev.fd (fd); socket_ev.poll (PE_R); cmd_ev.prio (PE_PRIO_NORMAL); // initialisation done, kick it! send_packet_printf ("version %d %d %s\n", VERSION_CS, VERSION_SC, VERSION_INFO); flush (); reset_stats (); clients.insert (this); } client::~client () { clients.erase (this); free (stats.range); free (stats.title); free (host); } void client::do_destroy () { attachable::do_destroy (); if (pl) pl->disconnect (); if (fd >= 0) { send_packet ("goodbye"); flush (); close (fd); } state = ST_DEAD; socket_ev.suspend (); cmd_ev.suspend (); refcnt_dec (); // socket no longer open } void client::reset_stats () { /* we need to clear these to -1 and not zero - otherwise, * if a player quits and starts a new character, we wont * send new values to the client, as things like exp start * at zero. */ for (int i = 0; i < NUM_SKILLS; i++) last_skill_exp[i] = -1; for (int i = 0; i < NROFATTACKS; i++) last_resist[i] = -1; last_weapon_sp = -1; last_level = -1; last_stats.exp = -1; last_weight = (uint32) - 1; last_flags = 0; last_weight = 0; last_weight_limit = 0; last_path_attuned = 0; last_path_repelled = 0; last_path_denied = 0; last_speed = 0; flags = 0; static living zero_living; last_stats = zero_living; } /** This sets up the socket and reads all the image information into memory. */ void init_ericserver (void) { cst_tot.time_start = time (NULL); cst_lst.time_start = time (NULL); LOG (llevDebug, "Initialize new client/server data\n"); } /******************************************************************************* * * Start of functions dealing with freeing of the data. * ******************************************************************************/ /** Free's all the memory that ericserver allocates. */ void free_all_newserver (void) { LOG (llevDebug, "Freeing all new client/server information.\n"); free_socket_images (); } client * client::create (int fd, const char *peername) { client *ns = new client (dup (fd), peername); ns->instantiate (); // effectively a nop right now INVOKE_CLIENT (CONNECT, ns); return ns; }