//*****************************************************************************
//
// gameloop.c
//
// contains the entrypoint for the MUD, plus various state-handling functions.
//
//*****************************************************************************
#include <sys/time.h>
#include "mud.h"
#include "utils.h"
#include "save.h"
#include "socket.h"
#include "world.h"
#include "room.h"
#include "room_reset.h"
#include "character.h"
#include "object.h"
#include "exit.h"
#include "log.h"
#include "action.h"
#include "event.h"
#include "auxiliary.h"
#include "storage.h"
#include "races.h"
#include "inform.h"
#include "hooks.h"
//*****************************************************************************
// mandatory modules
//*****************************************************************************
#include "editor/editor.h"
#include "char_vars/char_vars.h"
#include "items/items.h"
#include "olc2/olc.h"
#include "set_val/set_val.h"
#include "scripts/scripts.h"
//*****************************************************************************
// optional modules
//*****************************************************************************
#ifdef MODULE_TIME
#include "time/mudtime.h"
#endif
#ifdef MODULE_SOCIALS
#include "socials/socials.h"
#endif
#ifdef MODULE_HELP
#include "help/help.h"
#endif
#ifdef MODULE_ALIAS
#include "alias/alias.h"
#endif
// local procedures
void game_loop ( int control );
bool gameloop_end = FALSE;
// intialize shutdown state
bool shut_down = FALSE;
int control;
// what port are we running on?
int mudport = -1;
// global variables
WORLD_DATA *gameworld = NULL; // the gameworld, and ll the prototypes
LIST *object_list = NULL; // the list of all existing objects
LIST *socket_list = NULL; // the list of active sockets
LIST *mobile_list = NULL; // the list of existing mobiles
LIST *room_list = NULL; // the list of all existing rooms
LIST *mobs_to_delete = NULL; // mobs pending final extraction
LIST *objs_to_delete = NULL; // objs pending final extraction
LIST *rooms_to_delete = NULL; // rooms pending final extraction
PROPERTY_TABLE *mob_table = NULL; // a table of mobs by UID, for quick lookup
PROPERTY_TABLE *obj_table = NULL; // a table of objs by UID, for quick lookup
PROPERTY_TABLE *room_table = NULL; // a table of rooms by UID, for quick lookup
PROPERTY_TABLE *exit_table = NULL; // a table of exits by UID, for quick lookup
PROPERTY_TABLE *sock_table = NULL; // a table of socks by UID, for quick lookup
BUFFER *greeting = NULL; // message seen when a socket connects
BUFFER *motd = NULL; // what characters see when they log on
//
// This is where it all starts, nothing special.
int main(int argc, char **argv)
{
extern fd_set fSet;
int i;
bool fCopyOver = FALSE;
/************************************************************/
/* PARSE OPTIONS */
/************************************************************/
for(i = 1; i < argc-1; i++) {
if(!strcasecmp(argv[i], "-copyover")) {
fCopyOver = TRUE;
control = atoi(argv[++i]);
}
else {
char buf[SMALL_BUFFER];
sprintf(buf, "Invalid argument, %s (#%d)\r\n", argv[i], i);
perror(buf);
return 1;
}
}
// port number not supplied... just use default
if(i > argc-1)
mudport = DEFAULT_PORT;
else if(i == argc-1)
mudport = atoi(argv[argc-1]);
else {
perror("Arguments were not parsed properly. Could not start MUD.\r\n");
return 1;
}
/* seed the random number generator */
srand(time(0));
/* get the current time */
current_time = time(NULL);
/************************************************************/
/* INITIALIZE ALL OUR LISTS AND TABLES */
/************************************************************/
// lists for storing objects, sockets, and mobiles that are
// currently loaded into the game
object_list = newList();
socket_list = newList();
mobile_list = newList();
room_list = newList();
mobs_to_delete = newList();
objs_to_delete = newList();
rooms_to_delete = newList();
// tables for quick lookup of mobiles and objects by UID.
// For optimal speed, the table sizes should be roughly
// 25% larger than the number of mobs/objs you intend to have
// loaded into the game at any given time.
mob_table = newPropertyTable(charGetUID, 3000);
obj_table = newPropertyTable(objGetUID, 3000);
room_table = newPropertyTable(roomGetUID, 3000);
exit_table = newPropertyTable(exitGetUID, 9000);
sock_table = newPropertyTable(socketGetUID,1000);
// make a new world
gameworld = newWorld();
/************************************************************/
/* INITIALIZE OUR SETTINGS AND BASIC SYSTEMS */
/************************************************************/
// change to the lib directory
log_string("Changing to lib directory.");
chdir("../lib");
log_string("Initializing hooks.");
init_hooks();
log_string("Initializing bitvectors.");
init_bitvectors();
log_string("Initializing races and default bodies.");
init_races();
log_string("Initializing inform system.");
init_inform();
log_string("Initializing room resets.");
init_room_reset();
log_string("Initializing MUD settings.");
init_mud_settings();
log_string("Preparing auxiliary data for usage.");
init_auxiliaries();
log_string("Initializing command table.");
init_commands();
log_string("Initializing action handler.");
init_actions();
log_string("Initializing event handler.");
init_events();
log_string("Initializing logging system.");
init_logs();
log_string("Initializing account and player database.");
init_save();
/**********************************************************************/
/* START MANDATORY MODULE INSTALLATION */
/**********************************************************************/
log_string("Initializing item types.");
init_items();
log_string("Initializing editor.");
init_editor();
init_notepad();
log_string("Initializing character variables.");
init_char_vars();
log_string("Initializing OLC v2.0.");
init_olc2();
log_string("Initializing set utility.");
init_set();
/**********************************************************************/
/* START OPTIONAL MODULE INSTALLATION */
/**********************************************************************/
#ifdef MODULE_ALIAS
log_string("Initializing aliases.");
init_aliases();
#endif
#ifdef MODULE_TIME
log_string("Initializing game time.");
init_time();
#endif
#ifdef MODULE_SOCIALS
log_string("Initializing socials.");
init_socials();
#endif
#ifdef MODULE_HELP
log_string("Initializing helpfiles.");
init_help();
#endif
/**********************************************************************/
/* SET UP ALL OF OUR PYTHON STUFF */
/**********************************************************************/
//
// this HAS to be the last module initialized, to allow all the other
// modules to add get/setters and methods to the Python incarnations of
// our various datatypes.
log_string("Initializing scripts.");
init_scripts();
/**********************************************************************/
/* SPAWN THE WORLD */
/**********************************************************************/
/* load all game data */
log_string("Loading gameworld.");
load_muddata();
// force-pulse everything once
log_string("Force-resetting world");
worldForceReset(gameworld);
/**********************************************************************/
/* HANDLE THE SOCKET STARTUP STUFF */
/**********************************************************************/
/* initialize the socket */
if (!fCopyOver) {
log_string("Initializing sockets.");
control = init_socket();
}
/* clear out the file socket set */
FD_ZERO(&fSet);
/* add control to the set */
FD_SET(control, &fSet);
// attach our old sockets
if(fCopyOver)
copyover_recover();
/**********************************************************************/
/* START THE GAME UP, AND HANDLE ITS SHUTDOWN */
/**********************************************************************/
// main game loop
log_string("Entering game loop");
game_loop(control);
// run our finalize hooks
hookRun("shutdown", "");
// close down the socket
close(control);
// terminated without errors
log_string("Program terminated without errors.");
return 0;
}
//
// let all of our actions and events know that time has gone by.
// Also, give a chance of doing some resetting in the game.
//
void update_handler()
{
static int num_updates = 0;
// increment the number of updates we've done
num_updates++;
// pulse actions and events -> one pulse
pulse_actions(1);
pulse_events(1);
// pulse world
// we don't want to be on the same schedule as
// everything else, that updates every PULSES_PER_SECOND.
// We want to be on a schedule that updates every minute or so.
if((num_updates % (1 MINUTE)) == 0)
worldPulse(gameworld);
// if we have final extractions pending, do them
CHAR_DATA *ch = NULL;
while((ch = listPop(mobs_to_delete)) != NULL)
extract_mobile_final(ch);
OBJ_DATA *obj = NULL;
while((obj = listPop(objs_to_delete)) != NULL)
extract_obj_final(obj);
ROOM_DATA *room = NULL;
while((room = listPop(rooms_to_delete)) != NULL)
extract_room_final(room);
}
void game_loop(int control)
{
static struct timeval tv;
struct timeval last_time, new_time;
extern fd_set fSet;
extern fd_set rFd;
long secs, usecs;
/* set this for the first loop */
gettimeofday(&last_time, NULL);
/* do this untill the program is shutdown */
while (!shut_down) {
/* set current_time */
current_time = time(NULL);
/* copy the socket set */
memcpy(&rFd, &fSet, sizeof(fd_set));
/* wait for something to happen */
if (select(FD_SETSIZE, &rFd, NULL, NULL, &tv) < 0)
continue;
/* check for new connections */
if (FD_ISSET(control, &rFd)) {
struct sockaddr_in sock;
unsigned int socksize;
int newConnection;
socksize = sizeof(sock);
if ((newConnection = accept(control, (struct sockaddr*) &sock, &socksize)) >=0) {
SOCKET_DATA *newsock = new_socket(newConnection);
if(newsock != NULL) {
hookRun("receive_connection", hookBuildInfo("sk", newsock));
socketBustPrompt(newsock);
}
}
}
/* check all of the sockets for input */
socket_handler();
/* call the top-level update handler for events and actions */
update_handler();
/*
* Here we sleep out the rest of the pulse, thus forcing
* SocketMud(tm) (NakedMud) to run at PULSES_PER_SECOND pulses each second.
*/
gettimeofday(&new_time, NULL);
// get the time right now, and calculate how long we should sleep
usecs = (int) (last_time.tv_usec - new_time.tv_usec) + 1000000 / PULSES_PER_SECOND;
secs = (int) (last_time.tv_sec - new_time.tv_sec);
//
// Now we make sure that 0 <= usecs < 1.000.000
//
while (usecs < 0)
{
usecs += 1000000;
secs -= 1;
}
while (usecs >= 1000000)
{
usecs -= 1000000;
secs += 1;
}
// if secs < 0 we don't sleep, since we have encountered a laghole
if (secs > 0 || (secs == 0 && usecs > 0))
{
struct timeval sleep_time;
sleep_time.tv_usec = usecs;
sleep_time.tv_sec = secs;
if (select(0, NULL, NULL, NULL, &sleep_time) < 0)
continue;
}
/* reset the last time we where sleeping */
gettimeofday(&last_time, NULL);
/* recycle sockets */
recycle_sockets();
}
}