#include <iostream.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <string> #include <time.h> #include <sys/time.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include "defines.h" // // Function Delcarations // int initSocket(int port); // Initializes on port 'port', then returns the socket number it initialized. int getMaxSocket(); // Gets the top socket, used with select(2) void parseCommandLine(int, char **); // Parses the command line arugments void gameLoop(); // Calls the main game loop void gameCleanup(); // After the mud has shut down, memory cleanup, etc. goes here. void loadInterpret(); // Where the interpret class loading/linking goes (for actions). // // Command line functions // void commandVersion(void); void commandHelp(void); // // Global Variables // int listensocket; // Socket to listen on int portnumber; // Port number struct Character *c_first; // First descriptor in the list bool running = true; // Is the mud still running? // // Externed Variables // List < Character > c_list; // Character List List < BaseInterpret * > i_list; // Interpret List // // Start here (main) // TODO: add command line options and server shutdown // int main(int argc, char **argv) { parseCommandLine(argc, argv); listensocket = initSocket(portnumber); loadInterpret(); gameLoop(); gameCleanup(); return 0; } // // Initializes and returns listening socket // int initSocket(int port) { struct sockaddr_in sin; int sock; if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { cerr << strerror(errno) << " "; cerr << "Error opening Socket" << endl; exit(1); } memset(&sin, 0, sizeof sin); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_ANY); sin.sin_port = htons(port); /* int yes = 1; if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (void *) yes, sizeof(int)) < 0) { cerr << strerror(errno) << " "; cerr << "Error setting the socket options" << endl; close(sock); exit(1); } */ if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) { cerr << strerror(errno) << " "; cerr << "Error binding socket" << endl; close(sock); exit(1); } if (listen(sock, 5) < 0) { cerr << strerror(errno) << " "; cerr << "Error opening the socket for listening" << endl; close(sock); exit(1); } return sock; } // // Main game loop, "Keep it clean!" // void gameLoop() { int client; // client socket int max; // max socket int err; // error checking fd_set exceptionfds, readfds, writefds; // file decriptor listeners struct sockaddr_in addr_client; // client address struct timeval mudtimer; // MUD Timer Character ch; // Multi-use desecriptor class while (running) { FD_ZERO(&readfds); // init readfds FD_ZERO(&writefds); // init writefds FD_ZERO(&exceptionfds); // init exceptionfds FD_SET(listensocket, &readfds); // set readfds to listen for new clients // // Set up to listen on the descriptors // CLOOP_START { if (ch.getStatus() == STATUS_QUIT) continue; FD_SET(ch.getSocket(), &readfds); FD_SET(ch.getSocket(), &writefds); FD_SET(ch.getSocket(), &exceptionfds); CLOOP_END} max = getMaxSocket(); // Finds the largest socket number // // "If you just listen to the people, they will talk" // err = select(max + 1, &readfds, &writefds, &exceptionfds, NULL); if (err < 0) { cerr << strerror(errno) << " - "; cerr << "Error in polling descriptors."; exit(1); } // // Mud Timer // mudtimer.tv_sec = 0; mudtimer.tv_usec = 1; select(0, NULL, NULL, NULL, &mudtimer); // // Check the socket for new descriptors // if (FD_ISSET(listensocket, &readfds)) { int len_client = sizeof addr_client; client = accept(listensocket, (struct sockaddr *) &addr_client, (socklen_t *) & len_client); // Get the new client cout << "New connection on socket " << client << endl; if (client == -1) { cerr << strerror(errno); cerr << "Error getting new connection" << endl; exit(1); } Character ch2(client); c_list.listInsert(1, ch2); } // // Read from socket // CLOOP_START { if (FD_ISSET(ch.getSocket(), &readfds)) { string buf; char buf2[4000]; err = read(ch.getSocket(), buf2, sizeof(buf2)); if (err <= 0) { FD_CLR(ch.getSocket(), &readfds); FD_CLR(ch.getSocket(), &writefds); ch.closeD(); c_list.listDelete(count); if (count <= c_list.listLength()) { count--; } continue; } else { buf2[err] = '\0'; buf = buf2; ch.addReadBuf(buf); c_list.listReplace(count, ch); } } CLOOP_END} // // Kick out all the exceptions // CLOOP_START { if (FD_ISSET(ch.getSocket(), &exceptionfds)) { FD_CLR(ch.getSocket(), &readfds); FD_CLR(ch.getSocket(), &writefds); ch.closeD(); c_list.listDelete(count); if (count <= c_list.listLength()) { count--; } } CLOOP_END} // // Has the socket finished inputing a line? // CLOOP_START { if (ch.bufEnd()) { ch.setReadyBuf(true); c_list.listReplace(count, ch); } CLOOP_END} // // Handle the actions // handleActions(); // // Write writebuf to every socket // CLOOP_START { ch.writeBuf(); CLOOP_END} // // If they selected quit, kick them out // CLOOP_START { if (ch.getStatus() == STATUS_QUIT) { FD_CLR(ch.getSocket(), &readfds); FD_CLR(ch.getSocket(), &writefds); ch.closeD(); c_list.listDelete(count); if (count <= c_list.listLength()) { count--; } } CLOOP_END} } } int getMaxSocket() { int currmax = 0; Character ch; // Only sockets should be descriptors or listen socket CLOOP_START { if (ch.getSocket() > currmax) currmax = ch.getSocket(); CLOOP_END} if (listensocket > currmax) currmax = listensocket; if (currmax == 0) { cerr << "Error: no sockets open" << endl; exit(1); } return currmax; } // // Load Interpret Classes here after you define them in interpret.h and interpret.cpp // Try to keep them in alphabetical order // void loadInterpret() { // The pointer used in the macros BaseInterpret *iClassBasePtr; LOAD_INTERPRET(InterpretHelp); LOAD_INTERPRET(InterpretPassword); LOAD_INTERPRET(InterpretQuit); LOAD_INTERPRET(InterpretSave); LOAD_INTERPRET(InterpretSay); LOAD_INTERPRET(InterpretShutdown); LOAD_INTERPRET(InterpretWho); } // // Can't think of much that would go here, but in case it needs it, it gets it's own function // void gameCleanup() { Character ch; // // Cleanup lists // // Character List while (c_list.listLength() > 0) { c_list.listRetrieve(1, ch); ch.closeD(); c_list.listDelete(1); } // Interpret List while (i_list.listLength() > 0) { i_list.listDelete(1); } } // Parse command line options void parseCommandLine(int argc, char **argv) { int argCount = 1; // Temp variable for parsing arguments // Parse command line arguments while (argCount < argc) { if (argv[argCount][0] == '-') { switch (argv[argCount][1]) { case 'p': // Set port number if ((argCount + 1 < argc) && (argv[argCount + 1][1] != '-') && (atoi(argv[argCount + 1]) > 1024) && (atoi(argv[argCount + 1]) < 65535)) { portnumber = atoi(argv[argCount + 1]); } else { commandHelp(); exit(1); } break; case 'v': // Show version information commandVersion(); exit(0); break; case 'h': // Show help case '?': // Show help default: // Show help by default commandHelp(); exit(0); break; } } else { // Wrong parameter format commandHelp(); exit(0); } argCount += 2; } if (argCount == 1) { portnumber = 4500; } // Print this to everyone regarless of argument when the program starts commandVersion(); cout << "Starting COWMud on port " << portnumber << endl; } // Prints out usage instructions void commandHelp(void) { cout << "cowmud [OPTIONS]" << endl << "Options:" << endl << " -p NUM Use port NUM, 1024 < NUM < 65535" << endl << " Default: 4500" << endl << " -h This help screen" << endl << " -v Display version information" << endl; } // Prints version information void commandVersion(void) { cout << "CowMUD is a simple MUD, using object oriented programming." << endl; cout << "CowMUD version " << VERSION << endl; }