cyphesis/server/server.cpp
Al Riddoch 35d78b27a2 * Implemented beginnings of ranges code:
* modules/Location.h: Added inRange() method which can be used
	  to check whether location, including bounding box, is within
	  given range of a given location. Changed parent member
	  variable to ref to fit with Atlas spec. Changed all code
	  related to using the term ref instead of parent so
	  that there is no confusion between ref for coordinates
          and parent in the Atlas hierarchy.

        * rulesets/Thing.cpp: Added code to move operation to check
          what entities come into and out of range when entity moves.
          Appearance and Disappearance operations are sent to the
          moving entity with a list of the relevant entities as their
          argument.

        * server/WorldRouter.h/cpp: Added overloaded Look operation
          handler which applies range constraints to the contains
          attribute before returning.

        * common/BaseEntity.h, rulesets/Thing.h, rulesets/Character.h:
          Added handling of Appearance and Disappearance operations.

        * server/WorldRouter.cpp: Added primitive check to broadcast
          operations to avoid delivering broadcast operation to
          entities out of range.

        * Removed world.get_object() use from server python code, as
          its use is not required.

        * server/WorldRouter.h: Implemented fetching of floating point time
          from the OS so movement is much smoother.

Al
2001-02-19 06:13:31 +00:00

363 lines
9.5 KiB
C++

// This file may be redistributed and modified only under the terms of
// the GNU Lesser General Public License (See COPYING for details).
// Copyright (C) 2000 Alistair Riddoch
#include <Atlas/Message/Object.h>
#include <Atlas/Net/Stream.h>
#include <Atlas/Objects/Root.h>
#include <Atlas/Objects/Encoder.h>
#include <Atlas/Objects/Decoder.h>
#include <varconf/Config.h>
#include <iostream.h>
#include <fstream.h>
#include <Python.h>
extern "C" {
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
}
#include <common/config.h>
#include <common/const.h>
#include <common/log.h>
#include <common/debug.h>
#include <fstream>
#include "ServerRouting.h"
#include "Connection.h"
#include "server.h"
#include "CommClient.h"
#include "CommServer.h"
int profile_flag=0;
static const bool debug_flag = false;
string install_directory = string(INSTALLDIR);
using namespace Atlas;
void init_python_api();
CommClient::~CommClient() {
if (connection != NULL) {
connection->destroy();
delete connection;
}
if (encoder != NULL) {
delete encoder;
}
}
int CommClient::setup()
{
Atlas::Net::StreamAccept accept("cyphesis", client_ios, this);
debug(cout << "Negotiating... " << flush;);
while (accept.GetState() == Negotiate<iostream>::IN_PROGRESS) {
accept.Poll();
}
debug(cout << "done" << endl;);
if (accept.GetState() == Negotiate<iostream>::FAILED) {
cerr << "Failed to negotiate" << endl;
return(0);
}
// Negotiation was successful
// Get the codec that negotiation established
codec = accept.GetCodec();
// This should always be sent at the beginning of a session
encoder = new Objects::Encoder(codec);
codec->StreamBegin();
connection=new Connection(this);
return(1);
}
void CommClient::message(const Objects::Operation::RootOperation & op)
{
oplist reply = connection->message(op);
while (reply.size() != 0) {
Objects::Operation::RootOperation * rep_op = reply.front();
debug(cout << "sending reply" << endl << flush;);
send(rep_op);
delete rep_op;
reply.pop_front();
}
}
void CommClient::UnknownObjectArrived(const Atlas::Message::Object& o)
{
#if 0
debug(cout << "An unknown has arrived." << endl << flush;);
if (o.IsMap()) {
for(Message::Object::MapType::const_iterator I = o.AsMap().begin();
I != o.AsMap().end();
I++) {
debug(cout << I->first << endl << flush;);
if (I->second.IsString()) {
debug(cout << I->second.AsString() << endl << flush;);
}
}
} else {
debug(cout << "Its not a map." << endl << flush;);
}
#endif
}
void CommClient::ObjectArrived(const Objects::Operation::Login & op)
{
debug(cout << "A login operation thingy here!" << endl << flush;);
message(op);
}
void CommClient::ObjectArrived(const Objects::Operation::Create & op)
{
debug(cout << "A create operation thingy here!" << endl << flush;);
message(op);
}
void CommClient::ObjectArrived(const Objects::Operation::Move & op)
{
debug(cout << "A move operation thingy here!" << endl << flush;);
message(op);
}
void CommClient::ObjectArrived(const Objects::Operation::Set & op)
{
debug(cout << "A set operation thingy here!" << endl << flush;);
message(op);
}
void CommClient::ObjectArrived(const Objects::Operation::Touch & op)
{
debug(cout << "A touch operation thingy here!" << endl << flush;);
message(op);
}
void CommClient::ObjectArrived(const Objects::Operation::Look & op)
{
debug(cout << "A look operation thingy here!" << endl << flush;);
message(op);
}
void CommClient::ObjectArrived(const Objects::Operation::Talk & op)
{
debug(cout << "A talk operation thingy here!" << endl << flush;);
message(op);
}
int CommServer::setup(int port)
{
struct sockaddr_in sin;
server_port = port;
server_fd = socket(PF_INET, SOCK_STREAM, 0);
if (server_fd < 0) {
return(-1);
}
int flag=1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = 0L;
if (bind(server_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
close(server_fd);
return(-1);
}
listen(server_fd, 5);
server=new ServerRouting(this,"server");
return(0);
}
int CommServer::accept() {
struct sockaddr_in sin;
unsigned int addr_len = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_port = htons(server_port);
sin.sin_addr.s_addr = 0L;
debug(cout << "Accepting.." << endl << flush;);
int asockfd = ::accept(server_fd, (struct sockaddr *)&sin, &addr_len);
if (asockfd < 0) {
return(-1);
}
debug(cout << "Accepted" << endl << flush;);
CommClient * newcli = new CommClient(this, asockfd, sin.sin_port);
if (newcli->setup()) {
clients.insert(std::pair<int, CommClient *>(asockfd, newcli));
}
return(0);
}
inline void CommServer::idle() {
server->idle();
}
void CommServer::loop() {
fd_set sock_fds;
int highest = server_fd;
int client_fd;
CommClient * client;
struct timeval tv;
tv.tv_sec=0;
tv.tv_usec=100000;
FD_ZERO(&sock_fds);
FD_SET(server_fd, &sock_fds);
client_map_t::const_iterator I;
for(I = clients.begin(); I != clients.end(); I++) {
client_fd = I->first;
FD_SET(client_fd, &sock_fds);
if (client_fd > highest) {
highest=client_fd;
}
}
highest++;
int rval = select(highest, &sock_fds, NULL, NULL, &tv);
if (rval < 0) {
return;
}
for(I = clients.begin(); I != clients.end(); I++) {
client_fd = I->first;
if (FD_ISSET(client_fd, &sock_fds)) {
client = I->second;
if (client->peek() != -1) {
client->read();
} else if (client->eof()) {
remove_client(client);
break;
} else {
cerr << "FATAL THIS SHOULD NEVER HAPPEN" << endl << flush;
remove_client(client);
break;
}
}
}
if (FD_ISSET(server_fd, &sock_fds)) {
debug(cout << "selected on server" << endl << flush;);
accept();
}
idle();
}
inline void CommServer::remove_client(CommClient * client, char * error_msg)
{
Message::Object::MapType err;
err["message"] = Message::Object(error_msg);
Message::Object::ListType eargs(1,Message::Object(err));
Error * e = new Error();
*e = Error::Instantiate();
e->SetArgs(eargs);
// Need to deal with cleanly sending the error op, without hanging
// if the client has already gone. FIXME
if (client) {
client->send(e);
clients.erase(client->get_fd());
}
delete e;
delete client;
}
void CommServer::remove_client(CommClient * client)
{
remove_client(client,"You caused exception. Connection closed");
}
varconf::Config * global_conf = varconf::Config::inst();
list<string> rulesets;
#include <rulesets/ThingFactory.h>
int main(int argc, char ** argv)
{
if (install_directory=="NONE") {
install_directory = "/usr/local";
}
// See if the user has set the install directory on the command line
char * home;
if ((home = getenv("HOME")) != NULL) {
global_conf->readFromFile(string(home) + "/.cyphesis.vconf");
}
global_conf->getCmdline(argc, argv);
if (global_conf->findItem("cyphesis", "directory")) {
install_directory = global_conf->getItem("cyphesis", "directory");
if (home != NULL) {
global_conf->writeToFile(string(home) + "/.cyphesis.vconf");
}
}
global_conf->readFromFile(install_directory + "/share/cyphesis/cyphesis.vconf");
if ((home = getenv("HOME")) != NULL) {
global_conf->readFromFile(string(home) + "/.cyphesis.vconf");
}
global_conf->getCmdline(argc, argv);
string ruleset;
while (global_conf->findItem("cyphesis", "ruleset")) {
ruleset = global_conf->getItem("cyphesis", "ruleset");
global_conf->erase("cyphesis", "ruleset");
cout << "Reading in " << ruleset << endl << flush;
thing_factory.readRuleset(install_directory + "/share/cyphesis/" + ruleset);
rulesets.push_back(ruleset);
};
init_python_api();
cout << Py_GetPath() << endl << flush;
if (consts::debug_level>=1) {
cout << "consts::debug_level>=1, logging to cyphesis_server*.log files" << endl << flush;
//ofstream log_stream("cyphesis_server.log",ios::out);
common::log::inform_fp.open("cyphesis_server.log",ios::out);
common::log::debug_fp.open("cyphesis_server_debug.log",ios::out);
}
if (consts::debug_thinking>=1) {
char * log_name="thinking.log";
cout << "consts::debug_thinking>=1:, logging to" << log_name << endl;
common::log::thinking_fp.open(log_name,ios::out);
}
CommServer s;
if (s.setup(6767)) {
cerr << "Could not create listen socket." << endl << flush;
exit(1);
}
if (consts::debug_level>=1) {
char * log_name="cyphesis_world.log";
cout << "consts::debug_level>=1:, logging to" << log_name << endl;
//s.server.world.queue_fp.open(log_name,ios::out);
}
int exit_flag=0;
while (!exit_flag) {
try {
s.loop();
}
catch (...) {
cerr << "*********** EMERGENCY ***********" << endl;
cerr << "EXCEPTION: Caught in main()" << endl;
cerr << " : Continuing..." << endl;
}
}
}