//Socket handler for Legend MUD engine written by Barghest (Todd Allen),
//toddmallen@gmail.com.
//#include <sys/types.h>
#include <sys/time.h>
#include <iostream>
//#include <string>
#include "socket.h"
//using namespace std;
//bool shut_down = false;
SOCKET::Socket_Handler sock_handler;
UPDATE::Update_Handler u_handler;
//int init_socket(void);
void game_loop(void);
//void log_console(string logstring);
namespace SOCKET
{
Socket_Handler::Socket_Handler()
{
log_console("Creating socket handler...");
resync = false;
}
Socket_Handler::~Socket_Handler()
{
log_console("Terminating socket handler...");
}
int Socket_Handler::init_socket(void)
{
struct sockaddr_in sockin;
// int sock_fd;
using ERROR::E_Handler;
char *optval;
sockin.sin_family = AF_INET;
sockin.sin_port = htons(MUDPORT);
sockin.sin_addr.s_addr = INADDR_ANY;
memset(&(sockin.sin_zero), '\0', 8);
//Open a socket, bind to the correct port, and accept connections. If any of these
//steps fail a fatal exception will be thrown and the error logged.
try
{
if((sock_main = socket(PF_INET, SOCK_STREAM, 0)) == -1)
{
throw E_Handler("FATAL ERROR: Unable to open socket.");
}
if((setsockopt(sock_main, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))) == -1)
{
throw E_Handler("FATAL ERROR: Unable to set socket options.");
}
if(bind(sock_main, (struct sockaddr *)&sockin, sizeof(struct sockaddr)) == -1)
{
throw E_Handler("FATAL ERROR: Unable to bind to port.");
}
if((listen(sock_main, 10)) == -1)
{
throw E_Handler("FATAL ERROR: Failed to listen on socket.");
}
#ifdef DEBUG
log_console("Listening for incoming connections.");
#endif
}
catch(E_Handler &e)
{
log_console(e.message);
exit(EXIT_FAILURE);
}
return sock_main;
}
void Socket_Handler::link_psocket(Psocket_Data *psock_new)
{
// if(!first_psocket)
// {
// first_psocket = psock_new;
// last_psocket = psock_new;
// psock_new->set_next(NULL);
// psock_new->set_prev(NULL);
// }
// else
// {
// last_psocket->set_next(psock_new);
// psock_new->set_prev(last_psocket);
// psock_new->set_next(NULL);
// last_psocket = psock_new;
// }
psock_vector.push_back(*psock_new);
}
void Socket_Handler::set_max_fd(void)
{
vector<Psocket_Data>::iterator psock_it;
max_fd = sock_main;
if(psock_vector.empty())
return;
for(psock_it = psock_vector.begin(); psock_it != psock_vector.end(); psock_it++)
{
if(psock_it->get_sockfd() > max_fd)
{
max_fd = psock_it->get_sockfd();
}
}
return;
}
void Socket_Handler::unlink_psocket(const unsigned int vectorpos)
{
// using ERROR::E_Handler;
// try
// {
// //These should never happen and can probably be removed after debugging.
// if(!psock)
// {
// throw E_Handler("Socket_Handler::unlink_psocket: null socket descriptor!");
// }
// if(!first_psocket)
// {
// throw E_Handler("Socket_Handler::unlink_psocket: no sockets in use!");
// }
// if(psock == first_psocket)
// {
// if(psock == last_psocket)
// {
// psock->set_next(NULL);
// psock->set_prev(NULL);
// first_psocket = NULL;
// last_psocket = NULL;
// }
// else
// {
// first_psocket = psock->get_next();
// first_psocket->set_prev(NULL);
// psock->set_prev(NULL);
// psock->set_next(NULL);
// }
// }
// else if(psock == last_psocket)
// {
// last_psocket = psock->get_prev();
// last_psocket->set_next(NULL);
// psock->set_prev(NULL);
// psock->set_next(NULL);
// }
// else
// {
// psock->get_next()->set_prev(psock->get_prev());
// psock->get_prev()->set_next(psock->get_next());
// psock->set_prev(NULL);
// psock->set_next(NULL);
// }
// }
// catch(E_Handler e)
// {
// log_console(e.message);
// }
// return;
vector<Psocket_Data>::iterator tempit;
try
{
if(psock_vector.size() < vectorpos)
{
throw ERROR::E_Handler("ERROR: Socket_Handler::unlink_psocket: attempt to erase nonexistent element!");
}
psock_vector.erase((psock_vector.begin() + (vectorpos - 1)));
}
catch(ERROR::E_Handler e)
{
log_console(e.message);
e.clear_exception();
}
}
void Socket_Handler::receive_input(int sockfd)
{
using ERROR::E_Handler;
log_console("Receiving data.");
try
{
char buf[MAX_STRING_INPUT];
string str_in;
int retval;
retval = (recv(sockfd, buf, MAX_STRING_INPUT, 0));
if(retval == -1)
{
throw E_Handler("Socket_Handler::receive_input: bad recv()!");
}
else if(!retval)
{
close_socket(sockfd);
log_console("Closing socket.");
return;
}
else
{
// buf[retval - 1] = '\0';
str_in.clear();
str_in = buf;
str_in = UTILS::strip_whitespace(str_in);
#ifdef DEBUG
log_console("Recv successful:");
log_console(str_in);
// log_console(buf);
log_console("\n");
#endif
}
//if(!strcmp(buf, "quit"))
if(!strcmp(str_in.c_str(), "quit"))
{
close_socket(sockfd);
}
if(str_in == "shutdown")
{
u_handler.set_shutdown();
//u_handler.shutdown_mud();
}
}
catch(E_Handler e)
{
log_console(e.message);
close_socket(sockfd);
}
return;
}
void Socket_Handler::close_socket(int sockfd)
{
using ERROR::E_Handler;
try
{
if(sockfd == sock_main)
{
throw E_Handler("Attempt to close main socket through close_socket!");
}
close(sockfd);
FD_CLR(sockfd, &master_set);
#ifdef DEBUG
log_console("Closed socket.");
#endif
}
catch(E_Handler e)
{
log_console(e.message);
}
catch(...)
{
log_console("Undefined exception in Socket_Handler::close_socket");
}
return;
}
void Socket_Handler::log_newconn(const struct sockaddr_in &sockin) const
{
string logmsg;
logmsg = "New connection from IP ";
logmsg += inet_ntoa(sockin.sin_addr);
log_console(logmsg);
return;
}
Psocket_Data::Psocket_Data(const socklen_t ip, const unsigned int sock, const unsigned int vectorpos)
{
ip_addr = ip;
sock_fd = sock;
}
//This should never happen so an exception is thrown. This exception should lead to the bad socket
//being deleted by the calling function.
Psocket_Data::Psocket_Data()
{
using ERROR::E_Handler;
throw E_Handler("Attempt to create Psocket_Data without initialization.");
}
} //end namespace SOCKET
int main(int argc, char *argv)
{
//TODO: Replace with real logging capacity. This function should not do much.
log_console("Initializing MUD.");
game_loop();
log_console("Normal termination of MUD.");
return 0;
}
void game_loop(void)
{
log_console("Initializing MUD socket.");
int main_sock = sock_handler.init_socket();
int new_sock; //New connecting socket to be handled.
// int sock_num = 1; //Number of sockets currently connected.
// int fdmax = main_sock;
register int counter;
//fd_set master_set, working_set; //Sets for call to select().
struct sockaddr_in remoteaddr;
socklen_t addr_length = sizeof(remoteaddr);
// struct timeval main_tv;
// main_tv.tv_sec = 50;
// main_tv.tv_usec = 0;
using ERROR::E_Handler;
//Zero out socket lists.
// FD_ZERO(&master_set);
// FD_ZERO(&working_set);
sock_handler.zero_sets();
//Add main MUD socket as first socket in the set.
// FD_SET(main_sock, &master_set);
sock_handler.add_socket(main_sock);
sock_handler.set_max_fd(main_sock);
//Main game loop. Performance critical. Do NOT add anything to this function
//or those called by it unless it absolutely must be in here!
while(!u_handler.check_shutdown())
{
//TODO: Set up proper pulse timing.
//Poll.
#ifdef DEBUG
log_console("Begin select block.");
#endif
// working_set = master_set;
sock_handler.sync_sets();
//TODO: Add proper pulse timing.
//TODO: Split this up, too much crap in this function already.
try
{
if((select(sock_handler.get_select_fd(), sock_handler.get_working_set(), NULL, NULL, NULL)) == -1)
{
throw E_Handler("FATAL ERROR: select() failure!");
}
}
catch(E_Handler err)
{
log_console(err.message);
exit(EXIT_FAILURE);
}
//New connection.
//TODO: This is hideous and should be changed, only really here for
//debugging.
for(counter = 0; counter <= sock_handler.get_max_fd(); counter++)
{
if(FD_ISSET(counter, sock_handler.get_working_set()))
{
//New connection.
if(counter == main_sock)
{
if((new_sock = accept(main_sock, (struct sockaddr *)&remoteaddr, &addr_length)) == -1)
{
log_console("ERROR: Failed to accept() new connection.");
continue;
}
if(new_sock > sock_handler.get_max_fd())
{
sock_handler.set_max_fd(new_sock);
}
//Add new socket to the master set.
//FD_SET(new_sock, sock_handler.get_master_set());
sock_handler.add_socket(new_sock);
//Store socket information.
SOCKET::Psocket_Data *psock_new;
//Allocate memory for the new socket and add it to the list.
try
{
//TODO: Make sure these are deleted upon close.
psock_new = new SOCKET::Psocket_Data(ntohl(remoteaddr.sin_addr.s_addr), new_sock, 0);
}
catch(bad_alloc &badal)
{
log_console("FATAL ERROR: Unable to allocate memory for new descriptor!");
throw;
}
sock_handler.link_psocket(psock_new);
#ifdef DEBUG
//TODO: Change this into a real log of the connection including
//IP address.
log_console("Connection accepted successfully.");
string test = "Your connection has been accepted.\n";
// int sent = 0;
send(new_sock, test.c_str(), test.length(), 0);
#endif
//Log the new connection.
sock_handler.log_newconn(remoteaddr);
}
else
{
sock_handler.receive_input(counter);
}
//TODO: Add much other handling stuff here, including checking whether output
//needs to go out to a module.
}
}
//Now just does nothing, though this will change as automatic updates become required.
u_handler.update_mud();
}
log_console("Closing MUD socket.");
close(main_sock);
u_handler.set_shutdown();
u_handler.shutdown_mud();
return;
}
void log_console(string logstring)
{
//Log to the console/shell output. Other functions will provide other logging options.
cout << logstring << endl;
}