/*
* This file is part of Crossfire TRT, the Roguelike Realtime MORPG.
*
* Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Crossfire TRT team
* Copyright (©) 2001,2007 Mark Wedel
* Copyright (©) 1992,2007 Frank Tore Johansen
*
* Crossfire TRT 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 3 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, see .
*
* The authors 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
// use relatively small values, as we do not expect to receive much,
// and we do userspace write buffering
// 8kb limits throughput to roughly 66kb/s
#define SOCKET_RCVBUF 8192
#define SOCKET_SNDBUF 16384
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)
{
refcnt_inc (); // the socket is an external reference
mss = 1500 - 52; // 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 SO_RCVBUF
val = SOCKET_RCVBUF;
if (setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof (val)))
LOG (llevError, "SO_RCVBUF: %s\n", strerror (errno));
#endif
#ifdef SO_SNDBUF
val = SOCKET_SNDBUF;
if (setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &val, sizeof (val)))
LOG (llevError, "SO_SNDBUF: %s\n", strerror (errno));
#endif
#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 = 1;
if (setsockopt (fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof (val)))
LOG (llevError, "TCP_KEEPIDLE: %s\n", strerror (errno));
#endif
#ifdef TCP_KEEPCNT
val = 3;
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;
fx_want [FT_FACE] = true; // all clients must support image faces
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);
mapinfo_queue_clear ();
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;
last_flags = 0;
static living zero_living;
last_stats = zero_living;
}
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;
}