// Cyphesis Online RPG Server and AI Engine
// Copyright (C) 2001 Alistair Riddoch
//
// This program 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 2 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, write to the Free Software Foundation,
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
// $Id: ClientConnection.cpp,v 1.44 2007-09-04 10:51:27 alriddoch Exp $
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif // HAVE_CONFIG_H
#include "ClientConnection.h"
#include "common/log.h"
#include "common/debug.h"
#include "common/types.h"
#include "common/globals.h"
#include "common/compose.hpp"
#include <Atlas/Codecs/XML.h>
#include <Atlas/Net/Stream.h>
#include <Atlas/Objects/Encoder.h>
#include <Atlas/Objects/Operation.h>
#include <Atlas/Objects/Anonymous.h>
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif // HAVE_SYS_UN_H
using Atlas::Objects::Root;
using Atlas::Objects::Entity::Anonymous;
static bool debug_flag = false;
ClientConnection::ClientConnection() :
client_fd(-1), encoder(NULL), serialNo(512)
{
}
ClientConnection::~ClientConnection()
{
if (encoder != NULL) {
delete encoder;
}
}
void ClientConnection::operation(const Operation & op)
{
#if 0
const std::string & from = op->getFrom();
if (from.empty()) {
std::cerr << "ERROR: Operation with no destination" << std::endl << std::flush;
return;
}
dict_t::const_iterator I = objects.find(from);
if (I == objects.end()) {
std::cerr << "ERROR: Operation with invalid destination" << std::endl << std::flush;
return;
}
OpVector res = I->second->message(op);
OpVector::const_iterator Jend = res.end();
fora (OpVector::const_iterator J = res.begin(); J != Jend; ++J) {
(*J)->setFrom(I->first);
send(*(*J));
}
#endif
}
void ClientConnection::objectArrived(const Atlas::Objects::Root & obj)
{
Operation op = Atlas::Objects::smart_dynamic_cast<Operation>(obj);
if (!op.isValid()) {
const std::list<std::string> & parents = obj->getParents();
if (parents.empty()) {
log(ERROR, String::compose("Object of type \"%1\" with no parent arrived from server", obj->getObjtype()));
} else {
log(ERROR, String::compose("Object of type \"%1\" with parent \"%2\" arrived from server", obj->getObjtype(), obj->getParents().front()));
}
return;
}
debug(std::cout << "A " << op->getParents().front() << " op from server!" << std::endl << std::flush;);
reply_flag = true;
operationQueue.push_back(op);
if (op->getClassNo() == Atlas::Objects::Operation::ERROR_NO) {
errorArrived(op);
} else if (op->getClassNo() == Atlas::Objects::Operation::INFO_NO) {
infoArrived(op);
}
}
void ClientConnection::errorArrived(const Operation & op)
{
debug(std::cout << "ERROR" << std::endl << std::flush;);
error_flag = true;
}
void ClientConnection::infoArrived(const Operation & op)
{
debug(std::cout << "INFO" << std::endl << std::flush;);
const std::string & from = op->getFrom();
if (from.empty()) {
try {
const Root & ac = op->getArgs().front();
reply = ac;
// const std::string & acid = reply["id"].asString();
// objects[acid] = new ClientAccount(acid, *this);
}
catch (...) {
std::cerr << "WARNING: Malformed account from server" << std::endl << std::flush;
}
} else {
operation(op);
}
}
int ClientConnection::read() {
if (ios.is_open()) {
codec->poll();
return 0;
} else {
return -1;
}
}
int ClientConnection::connectLocal(const std::string & sockname)
{
#ifdef HAVE_SYS_UN_H
debug(std::cout << "Attempting local connect." << std::endl << std::flush;);
std::string socket;
if (sockname == "") {
socket = var_directory + "/tmp/" + client_socket_name;
} else if (sockname[0] != '/') {
socket = var_directory + "/tmp/" + sockname;
} else {
socket = sockname;
}
struct sockaddr_un sun;
sun.sun_family = AF_UNIX;
strncpy(sun.sun_path, socket.c_str(), sizeof(sun.sun_path));
int fd = ::socket(PF_UNIX, SOCK_STREAM, 0);
if (0 != ::connect(fd, (struct sockaddr *)&sun, sizeof(sun))) {
debug(std::cout << "Local connect refused" << std::endl << std::flush;);
return -1;
}
ios.setSocket(fd);
if (!ios.is_open()) {
std::cerr << "ERROR: For some reason " << sockname << " not open."
<< std::endl << std::flush;
return -1;
}
client_fd = ios.getSocket();
linger();
int ret = negotiate();
if (ret == -1) {
ios.close();
}
return ret;
#else // HAVE_SYS_UN_H
return -1;
#endif // HAVE_SYS_UN_H
}
int ClientConnection::connect(const std::string & server)
{
debug(std::cout << "Connecting to " << server << std::endl << std::flush;);
ios.open(server, client_port_num);
if (!ios.is_open()) {
std::cerr << "ERROR: Could not connect to " << server << "."
<< std::endl << std::flush;
return -1;
}
client_fd = ios.getSocket();
linger();
return negotiate();
}
int ClientConnection::linger()
{
struct linger {
int l_onoff;
int l_linger;
} listenLinger = { 1, 10 };
::setsockopt(client_fd, SOL_SOCKET, SO_LINGER, (char *)&listenLinger,
sizeof(listenLinger));
// Ensure the address can be reused once we are done with it.
return 0;
}
int ClientConnection::negotiate()
{
Atlas::Net::StreamConnect conn("cyphesis_aiclient", ios);
debug(std::cout << "Negotiating... " << std::flush;);
while (conn.getState() == Atlas::Net::StreamConnect::IN_PROGRESS) {
conn.poll();
}
debug(std::cout << "done" << std::endl;);
if (conn.getState() == Atlas::Net::StreamConnect::FAILED) {
std::cerr << "Failed to negotiate" << std::endl;
return -1;
}
codec = conn.getCodec(*this);
encoder = new Atlas::Objects::ObjectsEncoder(*codec);
codec->streamBegin();
return 0;
}
void ClientConnection::login(const std::string & account,
const std::string & password)
{
Atlas::Objects::Operation::Login l;
Anonymous login_arg;
login_arg->setAttr("username", account);
login_arg->setAttr("password", password);
l->setArgs1(login_arg);
reply_flag = false;
error_flag = false;
send(l);
}
void ClientConnection::create(const std::string & account,
const std::string & password)
{
Atlas::Objects::Operation::Create c;
Anonymous create_arg;
create_arg->setAttr("username", account);
create_arg->setAttr("password", password);
c->setArgs1(create_arg);
reply_flag = false;
error_flag = false;
send(c);
}
int ClientConnection::wait()
// Waits for response from server. Used when we are expecting a login response
// Return whether or not an error occured
{
error_flag = false;
reply_flag = false;
while (!reply_flag) {
poll(1);
}
return error_flag ? -1 : 0;
}
void ClientConnection::send(const Operation & op)
{
/* debug(Atlas::Codecs::XML c((std::iostream&)std::cout, (Atlas::Bridge*)this);
Atlas::Objects::Encoder enc(&c);
enc.streamMessage(&op);
std::cout << std::endl << std::flush;); */
op->setSerialno(++serialNo);
encoder->streamObjectsMessage(op);
ios << std::flush;
}
void ClientConnection::poll(int timeOut)
{
fd_set infds;
struct timeval tv;
FD_ZERO(&infds);
FD_SET(client_fd, &infds);
tv.tv_sec = timeOut;
tv.tv_usec = 0;
int retval = select(client_fd+1, &infds, NULL, NULL, &tv);
if (retval < 1) {
return;
}
if (FD_ISSET(client_fd, &infds)) {
if (ios.peek() == -1) {
std::cerr << "Server disconnected" << std::endl << std::flush;
error_flag = true;
reply_flag = true;
} else {
codec->poll();
}
}
}
Operation ClientConnection::pop()
{
poll();
if (operationQueue.empty()) {
return Operation(0);
}
Operation op = operationQueue.front();
operationQueue.pop_front();
return op;
}
bool ClientConnection::pending()
{
return !operationQueue.empty();
}
syntax highlighted by Code2HTML, v. 0.9.1