Killed clientconsole, mwcompiler, mwinterpreter and command server

actorid
Nicolay Korslund 15 years ago
parent f693a2942e
commit 532ccbd1f8

@ -50,20 +50,13 @@ source_group(components\\esm_store FILES ${ESM_STORE} ${ESM_STORE_HEADER})
file(GLOB ESM_HEADER ${COMP_DIR}/esm/*.hpp)
source_group(components\\esm FILES ${ESM_HEADER})
set(COMMANDSERVER
${COMP_DIR}/commandserver/command.hpp
${COMP_DIR}/commandserver/server.hpp
${COMP_DIR}/commandserver/server.cpp)
source_group(components\\commandserver FILES ${COMMANDSERVER})
set(MISC
${COMP_DIR}/misc/stringops.cpp
${COMP_DIR}/misc/fileops.cpp)
set(MISC_HEADER
${COMP_DIR}/misc/fileops.hpp
${COMP_DIR}/misc/slice_array.hpp
${COMP_DIR}/misc/stringops.hpp
${COMP_DIR}/misc/tsdeque.hpp)
${COMP_DIR}/misc/stringops.hpp)
source_group(components\\misc FILES ${MISC} ${MISC_HEADER})
file(GLOB COMPILER ${COMP_DIR}/compiler/*.cpp)
@ -75,7 +68,7 @@ file(GLOB INTERPRETER_HEADER ${COMP_DIR}/interpreter/*.hpp)
source_group(components\\interpreter FILES ${INTERPRETER} ${INTERPRETER_HEADER})
set(COMPONENTS ${BSA} ${NIF} ${NIFOGRE} ${ESM_STORE} ${MISC}
${COMMANDSERVER} ${COMPILER} ${INTERPRETER})
${COMPILER} ${INTERPRETER})
set(COMPONENTS_HEADER ${BSA_HEADER} ${NIF_HEADER} ${NIFOGRE_HEADER} ${ESM_STORE_HEADER}
${ESM_HEADER} ${MISC_HEADER} ${COMPILER_HEADER}
${INTERPRETER_HEADER})
@ -195,18 +188,3 @@ option(BUILD_ESMTOOL "build ESM inspector" ON)
if (BUILD_ESMTOOL)
add_subdirectory( apps/esmtool )
endif()
option(BUILD_CLIENTCONSOLE "build external console for script interpreter" ON)
if (BUILD_CLIENTCONSOLE)
add_subdirectory( apps/clientconsole )
endif()
option(BUILD_MWCOMPILER "build standalone Morrowind script compiler" ON)
if (BUILD_MWCOMPILER)
add_subdirectory( apps/mwcompiler )
endif()
option(BUILD_MWINTERPRETER "build standalone Morrowind script code interpreter" ON)
if (BUILD_MWINTERPRETER)
add_subdirectory( apps/mwinterpreter )
endif()

@ -1,4 +0,0 @@
find_package(Threads REQUIRED)
add_executable(clientconsole client.cpp)
target_link_libraries(clientconsole ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})

@ -1,142 +0,0 @@
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <libs/mangle/tools/str_exception.hpp>
using boost::asio::ip::tcp;
#ifdef WIN32
#pragma warning( disable : 4966 )
#endif
class Client
{
protected:
struct Header
{
char magic[4];
boost::uint32_t dataLength;
};
boost::asio::io_service mIOService;
tcp::socket* mpSocket;
public:
bool connect(const char* port)
{
tcp::resolver resolver(mIOService);
tcp::resolver::query query("localhost", port);
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
mpSocket = new tcp::socket(mIOService);
boost::system::error_code error = boost::asio::error::host_not_found;
while (error && endpoint_iterator != end)
{
mpSocket->close();
mpSocket->connect(*endpoint_iterator++, error);
}
return (error) ? false : true;
}
void disconnect()
{
mpSocket->close();
mIOService.stop();
}
bool send (const char* msg)
{
const size_t slen = strlen(msg);
const size_t plen = sizeof(Header) + slen + 1;
std::vector<char> packet(plen);
Header* pHeader = reinterpret_cast<Header*>(&packet[0]);
strncpy(pHeader->magic, "OMW0", 4);
pHeader->dataLength = slen + 1; // Include the null terminator
strncpy(&packet[8], msg, pHeader->dataLength);
boost::system::error_code ec;
boost::asio::write(*mpSocket, boost::asio::buffer(packet),
boost::asio::transfer_all(), ec);
if (ec)
std::cout << "Error: " << ec.message() << std::endl;
return !ec;
}
bool receive (std::string& reply)
{
Header header;
boost::system::error_code error;
mpSocket->read_some(boost::asio::buffer(&header, sizeof(Header)), error);
if (error != boost::asio::error::eof)
{
if (strncmp(header.magic, "OMW0", 4) == 0)
{
std::vector<char> msg;
msg.resize(header.dataLength);
boost::system::error_code error;
mpSocket->read_some(boost::asio::buffer(&msg[0], header.dataLength), error);
if (!error)
{
reply = &msg[0];
return true;
}
}
else
throw str_exception("Unexpected header!");
}
return false;
}
};
int main(int argc, char* argv[])
{
std::cout << "OpenMW client console" << std::endl;
std::cout << "=====================" << std::endl;
std::cout << "Type 'quit' to exit." << std::endl;
std::cout << "Connecting...";
Client client;
if (client.connect("27917"))
{
std::cout << "success." << std::endl;
bool bDone = false;
do
{
std::cout << "Client> ";
std::string buffer;
std::getline(std::cin, buffer);
if (buffer == "quit")
bDone = true;
else
{
if (client.send(buffer.c_str()))
{
std::string reply;
if (client.receive(reply))
std::cout << "Server: " << reply << std::endl;
else
bDone = true;
}
else
bDone = true;
}
} while (!bDone);
client.disconnect();
}
else
std::cout << "failed." << std::endl;
return 0;
}

@ -56,7 +56,7 @@ int main(int argc, char**argv)
cout << "Special flag: " << esm.getSpecial() << endl;
cout << "Masters:\n";
ESMReader::MasterList m = esm.getMasters();
for(int i=0;i<m.size();i++)
for(unsigned int i=0;i<m.size();i++)
cout << " " << m[i].name << ", " << m[i].size << " bytes\n";
// Loop through all records

@ -1,9 +0,0 @@
project(MWCompiler)
set(TOOLS_MWCOMPILER ${COMPILER}
main.cpp
context.cpp
context.hpp)
add_executable(mwcompiler ${TOOLS_MWCOMPILER})

@ -1,21 +0,0 @@
#include "context.hpp"
namespace SACompiler
{
bool Context::canDeclareLocals() const
{
return true;
}
char Context::getGlobalType (const std::string& name) const
{
return ' ';
}
bool Context::isId (const std::string& name) const
{
return false;
}
}

@ -1,24 +0,0 @@
#ifndef MWCOMPILER_CONTEXT_H_INCLUDED
#define MWCOMPILER_CONTEXT_H_INCLUDED
#include <components/compiler/context.hpp>
namespace SACompiler
{
class Context : public Compiler::Context
{
public:
virtual bool canDeclareLocals() const;
///< Is the compiler allowed to declare local variables?
virtual char getGlobalType (const std::string& name) const;
///< 'l: long, 's': short, 'f': float, ' ': does not exist.
virtual bool isId (const std::string& name) const;
///< Does \a name match an ID, that can be referenced?
};
}
#endif

@ -1,78 +0,0 @@
// Stand-alone MW-script compiler
#include <exception>
#include <iostream>
#include <fstream>
#include <vector>
#include <components/interpreter/types.hpp>
#include <components/compiler/streamerrorhandler.hpp>
#include <components/compiler/scanner.hpp>
#include <components/compiler/fileparser.hpp>
#include <components/compiler/exception.hpp>
#include "context.hpp"
int main (int argc, char **argv)
{
try
{
SACompiler::Context context;
Compiler::StreamErrorHandler errorHandler (std::cout);
Compiler::FileParser parser (errorHandler, context);
std::string filename = argc>1 ? argv[1] : "test.mwscript";
try
{
std::ifstream file (filename.c_str());
if (!file.is_open())
{
std::cout << "can't open script file: " << filename << std::endl;
return 1;
}
Compiler::Scanner scanner (errorHandler, file);
scanner.scan (parser);
}
catch (const Compiler::SourceException&)
{
// ignore exception (problem has already been reported to the user)
}
if (errorHandler.countErrors() || errorHandler.countWarnings())
{
std::cout
<< errorHandler.countErrors() << " error(s), "
<< errorHandler.countWarnings() << " warning(s)" << std::endl
<< std::endl;
}
if (errorHandler.isGood())
{
std::vector<Interpreter::Type_Code> code;
parser.getCode (code);
std::ofstream codeFile ((filename + ".code").c_str());
codeFile.write (reinterpret_cast<const char *> (&code[0]),
code.size()*sizeof (Interpreter::Type_Code));
std::ofstream localFile ((filename + ".locals").c_str());
parser.getLocals().write (localFile);
return 0;
}
return 1;
}
catch (const std::exception &e)
{
std::cout << "\nERROR: " << e.what() << std::endl;
return 1;
}
}

@ -1,9 +0,0 @@
project(MWInterpreter)
set(TOOLS_MWINTERPRETER
${INTERPRETER}
main.cpp
context.cpp
context.hpp)
add_executable(mwinterpreter ${TOOLS_MWINTERPRETER})

@ -1,156 +0,0 @@
#include "context.hpp"
#include <cassert>
#include <fstream>
#include <stdexcept>
#include <iostream>
namespace SAInterpreter
{
Context::Context (const std::string& filename)
{
std::ifstream file (filename.c_str());
if (!file.is_open())
throw std::runtime_error ("can't open locals file: " + filename);
std::size_t shortSize, longSize, floatSize;
file >> shortSize >> longSize >> floatSize;
mShorts.resize (shortSize, 0);
mLongs.resize (longSize, 0);
mFloats.resize (floatSize, 0.0);
std::size_t size = shortSize + longSize + floatSize;
mNames.resize (size);
for (std::size_t i=0; i<size; ++i)
file >> mNames[i];
}
int Context::getLocalShort (int index) const
{
assert (index>=0);
return mShorts.at (index);
}
int Context::getLocalLong (int index) const
{
assert (index>=0);
return mLongs.at (index);
}
float Context::getLocalFloat (int index) const
{
assert (index>=0);
return mFloats.at (index);
}
void Context::setLocalShort (int index, int value)
{
assert (index>=0);
mShorts.at (index) = value;
}
void Context::setLocalLong (int index, int value)
{
assert (index>=0);
mLongs.at (index) = value;
}
void Context::setLocalFloat (int index, float value)
{
assert (index>=0);
mFloats.at (index) = value;
}
void Context::messageBox (const std::string& message,
const std::vector<std::string>& buttons)
{
std::cout << "message box: " << message << std::endl;
for (std::size_t i=0; i<buttons.size(); ++i)
std::cout << " button " << i << ": " << buttons[i] << std::endl;
}
bool Context::menuMode()
{
return false;
}
int Context::getGlobalShort (const std::string& name) const
{
return 0;
}
int Context::getGlobalLong (const std::string& name) const
{
return 0;
}
float Context::getGlobalFloat (const std::string& name) const
{
return 0;
}
void Context::setGlobalShort (const std::string& name, int value) {}
void Context::setGlobalLong (const std::string& name, int value) {}
void Context::setGlobalFloat (const std::string& name, float value) {}
bool Context::isScriptRunning (const std::string& name) const
{
return false;
}
void Context::startScript (const std::string& name) {}
void Context::stopScript (const std::string& name) {}
float Context::getDistance (const std::string& name, const std::string& id) const
{
return 0;
}
float Context::getSecondsPassed() const
{
return 0;
}
bool Context::isDisabled (const std::string& id) const
{
return false;
}
void Context::enable (const std::string& id) {}
void Context::disable (const std::string& id) {}
void Context::report()
{
std::size_t i = 0;
std::cout << "local shorts:" << std::endl;
for (std::vector<Interpreter::Type_Short>::const_iterator iter (mShorts.begin());
iter!=mShorts.end(); ++iter)
std::cout << mNames[i++] << ": " << *iter << std::endl;
std::cout << "local longs:" << std::endl;
for (std::vector<Interpreter::Type_Integer>::const_iterator iter (mLongs.begin());
iter!=mLongs.end(); ++iter)
std::cout << mNames[i++] << ": " << *iter << std::endl;
std::cout << "local floats:" << std::endl;
for (std::vector<Interpreter::Type_Float>::const_iterator iter (mFloats.begin());
iter!=mFloats.end(); ++iter)
std::cout << mNames[i++] << ": " << *iter << std::endl;
}
}

@ -1,76 +0,0 @@
#ifndef SAINTERPRETER_CONTEXT_H_INCLUDED
#define SAINTERPRETER_CONTEXT_H_INCLUDED
#include <string>
#include <vector>
#include <components/interpreter/context.hpp>
#include <components/interpreter/types.hpp>
namespace SAInterpreter
{
class Context : public Interpreter::Context
{
std::vector<Interpreter::Type_Short> mShorts;
std::vector<Interpreter::Type_Integer> mLongs;
std::vector<Interpreter::Type_Float> mFloats;
std::vector<std::string> mNames;
public:
Context (const std::string& filename);
///< Create context from file
/// \note A context for an integreted interpreter will typically not
/// configure at construction, but will offer a separate function.
virtual int getLocalShort (int index) const;
virtual int getLocalLong (int index) const;
virtual float getLocalFloat (int index) const;
virtual void setLocalShort (int index, int value);
virtual void setLocalLong (int index, int value);
virtual void setLocalFloat (int index, float value);
virtual void messageBox (const std::string& message,
const std::vector<std::string>& buttons);
virtual bool menuMode();
virtual int getGlobalShort (const std::string& name) const;
virtual int getGlobalLong (const std::string& name) const;
virtual float getGlobalFloat (const std::string& name) const;
virtual void setGlobalShort (const std::string& name, int value);
virtual void setGlobalLong (const std::string& name, int value);
virtual void setGlobalFloat (const std::string& name, float value);
virtual bool isScriptRunning (const std::string& name) const;
virtual void startScript (const std::string& name);
virtual void stopScript (const std::string& name);
virtual float getDistance (const std::string& name, const std::string& id = "") const;
virtual float getSecondsPassed() const;
virtual bool isDisabled (const std::string& id = "") const;
virtual void enable (const std::string& id = "");
virtual void disable (const std::string& id = "");
void report();
///< Write state to std::cout
};
}
#endif

@ -1,57 +0,0 @@
// Stand-alone MW-script code interpreter
#include <exception>
#include <fstream>
#include <iostream>
#include <vector>
#include <components/interpreter/interpreter.hpp>
#include <components/interpreter/context.hpp>
#include <components/interpreter/types.hpp>
#include <components/interpreter/installopcodes.hpp>
#include "context.hpp"
int main (int argc, char **argv)
{
try
{
std::string filename = argc>1 ? argv[1] : "test.mwscript";
std::string localfilename = filename + ".locals";
SAInterpreter::Context context (localfilename);
Interpreter::Interpreter interpreter (context);
Interpreter::installOpcodes (interpreter);
std::string codefilename = filename + ".code";
std::ifstream codefile (codefilename.c_str());
if (!codefile.is_open())
{
std::cout << "can't open code file: " << codefilename << std::endl;
return 1;
}
std::vector<Interpreter::Type_Code> code (4);
codefile.read (reinterpret_cast<char *> (&code[0]), 4 * sizeof (Interpreter::Type_Code));
unsigned int size = code[0] + code[1] + code[2] + code[3];
code.resize (4+size);
codefile.read (reinterpret_cast<char *> (&code[4]), size * sizeof (Interpreter::Type_Code));
interpreter.run (&code[0], size+4);
context.report();
}
catch (const std::exception &e)
{
std::cout << "\nERROR: " << e.what() << std::endl;
return 1;
}
}

@ -45,9 +45,6 @@ bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt)
{
mEnvironment.mFrameDuration = evt.timeSinceLastFrame;
// console
processCommands();
// global scripts
mEnvironment.mGlobalScripts->run (mEnvironment);
@ -68,27 +65,11 @@ bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt)
return true;
}
void OMW::Engine::processCommands()
{
Command cmd;
while (mCommandQueue.try_pop_front(cmd))
{
///\todo Add actual processing of the received command strings
std::cout << "Command: '" << cmd.mCommand << "'" << std::endl;
///\todo Replace with real output. For now, echo back the string in uppercase
std::string reply(cmd.mCommand);
std::transform(reply.begin(), reply.end(), reply.begin(), toupper);
cmd.mReplyFunction(reply);
}
}
OMW::Engine::Engine()
: mDebug (false)
, mVerboseScripts (false)
, mNewGame (false)
, mEnableCommandServer (false)
, mScriptManager (0)
, mScriptContext (0)
{
@ -96,9 +77,6 @@ OMW::Engine::Engine()
OMW::Engine::~Engine()
{
if (mspCommandServer.get())
mspCommandServer->stop();
delete mGuiManager;
delete mEnvironment.mWorld;
delete mEnvironment.mSoundManager;
@ -167,11 +145,6 @@ void OMW::Engine::enableDebugMode()
{
mDebug = true;
}
void OMW::Engine::enableCommandServer()
{
mEnableCommandServer = true;
}
void OMW::Engine::enableVerboseScripts()
{
@ -239,16 +212,6 @@ void OMW::Engine::go()
MWInput::MWInputManager input(mOgre, mEnvironment.mWorld->getPlayerPos(),
*mEnvironment.mWindowManager, mDebug);
// Launch the console server
if (mEnableCommandServer)
{
std::cout << "Starting command server on port " << kCommandServerPort << std::endl;
mspCommandServer.reset(new OMW::CommandServer::Server(&mCommandQueue, kCommandServerPort));
mspCommandServer->start();
}
else
std::cout << "Command server disabled" << std::endl;
std::cout << "\nPress Q/ESC or close window to exit.\n";
mOgre.getRoot()->addFrameListener (this);

@ -8,9 +8,6 @@
#include <OgreFrameListener.h>
#include <openengine/ogre/renderer.hpp>
#include <components/misc/tsdeque.hpp>
#include <components/commandserver/server.hpp>
#include <components/commandserver/command.hpp>
#include <components/compiler/extensions.hpp>
#include "mwworld/environment.hpp"
@ -54,8 +51,6 @@ namespace OMW
class Engine : private Ogre::FrameListener
{
enum { kCommandServerPort = 27917 };
boost::filesystem::path mDataDir;
OEngine::Render::OgreRenderer mOgre;
std::string mCellName;
@ -64,10 +59,6 @@ namespace OMW
bool mVerboseScripts;
bool mNewGame;
TsDeque<OMW::Command> mCommandQueue;
bool mEnableCommandServer;
std::auto_ptr<OMW::CommandServer::Server> mspCommandServer;
MWWorld::Environment mEnvironment;
MWScript::ScriptManager *mScriptManager;
Compiler::Extensions mExtensions;
@ -90,7 +81,6 @@ namespace OMW
virtual bool frameStarted(const Ogre::FrameEvent& evt);
/// Process pending commands
void processCommands();
public:
@ -115,7 +105,6 @@ namespace OMW
/// Enable the command server so external apps can send commands to the console.
/// Must be set before go().
void enableCommandServer();
/// Enable verbose script output
void enableVerboseScripts();

@ -33,7 +33,6 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
( "debug", "debug mode" )
( "script-verbose", "verbose script output" )
( "new-game", "activate char gen/new game mechanics" )
( "disable-command-server", "turn off the command server" )
;
bpo::variables_map variables;
@ -72,9 +71,6 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
if (variables.count ("new-game"))
engine.setNewGame();
if (variables.count("disable-command-server") == 0)
engine.enableCommandServer();
return true;
}

@ -1,21 +0,0 @@
#ifndef COMMANDSERVER_COMMAND_HPP
#define COMMANDSERVER_COMMAND_HPP
#include <boost/function.hpp>
namespace OMW
{
///
/// A Command is currently defined as a string input that, when processed,
/// will generate a string output. The string output is passed to the
/// mReplyFunction as soon as the command has been processed.
///
class Command
{
public:
std::string mCommand;
boost::function1<void, std::string> mReplyFunction;
};
}
#endif //COMMANDSERVER_COMMAND_HPP

@ -1,232 +0,0 @@
#include "server.hpp"
#include "libs/platform/strings.h"
using boost::asio::ip::tcp;
//
// Namespace for containing implementation details that the
// rest of OpenMW doesn't need to worry about
//
namespace OMW { namespace CommandServer { namespace Detail {
struct Header
{
char magic[4];
size_t dataLength;
} header;
///
/// Tracks an active connection to the CommandServer
///
class Connection
{
public:
Connection (boost::asio::io_service& io_service, Server* pServer);
void start();
void stop();
tcp::socket& socket();
void reply (std::string s);
protected:
void handle ();
tcp::socket mSocket;
Server* mpServer;
boost::thread* mpThread;
};
Connection::Connection (boost::asio::io_service& io_service, Server* pServer)
: mSocket (io_service)
, mpServer (pServer)
{
}
void Connection::start()
{
mpThread = new boost::thread(boost::bind(&Connection::handle, this));
}
///
/// Stops and disconnects the connection
///
void Connection::stop()
{
mSocket.shutdown(boost::asio::socket_base::shutdown_both);
mSocket.close();
mpThread->join();
}
tcp::socket& Connection::socket()
{
return mSocket;
}
void Connection::reply (std::string reply)
{
const size_t plen = sizeof(Header) + reply.length() + 1;
std::vector<char> packet(plen);
Header* pHeader = reinterpret_cast<Header*>(&packet[0]);
strncpy(pHeader->magic, "OMW0", 4);
pHeader->dataLength = reply.length() + 1; // Include the null terminator
strncpy(&packet[8], reply.c_str(), pHeader->dataLength);
boost::system::error_code ec;
boost::asio::write(mSocket, boost::asio::buffer(packet),
boost::asio::transfer_all(), ec);
if (ec)
std::cout << "Error: " << ec.message() << std::endl;
}
void Connection::handle ()
{
bool bDone = false;
while (!bDone)
{
// Read the header
boost::system::error_code error;
mSocket.read_some(boost::asio::buffer(&header, sizeof(Header)), error);
if (error != boost::asio::error::eof)
{
if (strncmp(header.magic, "OMW0", 4) == 0)
{
std::vector<char> msg;
msg.resize(header.dataLength);
boost::system::error_code error;
mSocket.read_some(boost::asio::buffer(&msg[0], header.dataLength), error);
if (!error)
mpServer->postCommand(this, &msg[0]);
else
bDone = true;
}
else
throw std::runtime_error("Unexpected header!");
}
else
bDone = true;
}
mpServer->removeConnection(this);
}
}}}
namespace OMW { namespace CommandServer {
using namespace Detail;
Server::Server (Deque* pCommandQueue, const int port)
: mPort (port)
, mAcceptor (mIOService, tcp::endpoint(tcp::v4(), mPort))
, mbStopping (false)
, mpCommandQueue (pCommandQueue)
{
}
void Server::start()
{
mIOService.run();
mpThread = new boost::thread(boost::bind(&Server::threadMain, this));
}
//
// Helper function - see Server::stop()
//
static void connectAndDisconnect (int port)
{
char portString[64];
snprintf(portString, 64, "%d", port);
boost::asio::io_service ioService;
tcp::resolver resolver(ioService);
tcp::resolver::query query("localhost", portString);
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
tcp::socket socket(ioService);
boost::system::error_code error = boost::asio::error::host_not_found;
while (error && endpoint_iterator != end)
{
socket.close();
socket.connect(*endpoint_iterator++, error);
}
socket.close();
ioService.stop();
}
void Server::stop()
{
// Boost.Asio doesn't have a way to cancel the blocking accept() call
// in the listening thread. Therefore, set an internal flag to let
// the server know it's stopping and then unblock it via a do-nothing
// connect/disconnect.
mbStopping = true;
connectAndDisconnect(mPort);
// (1) Stop accepting new connections
// (2) Wait for the listener thread to finish
mAcceptor.close();
mpThread->join();
// Now that no new connections are possible, close any existing
// open connections
{
boost::mutex::scoped_lock lock(mConnectionsMutex);
for (ConnectionSet::iterator it = mConnections.begin();
it != mConnections.end();
++it)
{
(*it)->stop();
}
}
}
void Server::removeConnection (Connection* ptr)
{
// If the server is shutting down (rather the client closing the
// connection), don't remove the connection from the list: that
// would corrupt the iterator the server is using to shutdown all
// clients.
if (!mbStopping)
{
boost::mutex::scoped_lock lock(mConnectionsMutex);
std::set<Connection*>::iterator it = mConnections.find(ptr);
if (it != mConnections.end())
mConnections.erase(it);
}
delete ptr;
}
void Server::postCommand (Connection* pConnection, const char* s)
{
Command cmd;
cmd.mCommand = s;
cmd.mReplyFunction = std::bind1st(std::mem_fun(&Connection::reply), pConnection);
mpCommandQueue->push_back(cmd);
}
void Server::threadMain()
{
// Loop until accept() fails, which will cause the break statement to be hit
while (true)
{
std::auto_ptr<Connection> spConnection(new Connection(mAcceptor.io_service(), this));
boost::system::error_code ec;
mAcceptor.accept(spConnection->socket(), ec);
if (!ec && !mbStopping)
{
boost::mutex::scoped_lock lock(mConnectionsMutex);
mConnections.insert(spConnection.get());
spConnection->start();
spConnection.release();
}
else
break;
}
}
}}

@ -1,64 +0,0 @@
#ifndef CONSOLESERVER_H
#define CONSOLESERVER_H
#include <iostream>
#include <string>
#include <deque>
#include <set>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include "components/misc/tsdeque.hpp"
#include "components/commandserver/command.hpp"
namespace OMW { namespace CommandServer
{
//
// Forward Declarations
//
namespace Detail
{
class Connection;
}
//
// Server that opens a port to listen for string commands which will be
// put into the deque provided in the Server constructor.
//
class Server
{
public:
typedef TsDeque<Command> Deque;
Server (Deque* pCommandQueue, const int port);
void start();
void stop();
protected:
friend class Detail::Connection;
typedef std::set<Detail::Connection*> ConnectionSet;
void removeConnection (Detail::Connection* ptr);
void postCommand (Detail::Connection*, const char* s);
void threadMain();
// Objects used to set up the listening server
int mPort;
boost::asio::io_service mIOService;
boost::asio::ip::tcp::acceptor mAcceptor;
boost::thread* mpThread;
bool mbStopping;
// Track active connections
ConnectionSet mConnections;
mutable boost::mutex mConnectionsMutex;
// Pointer to command queue
Deque* mpCommandQueue;
};
}}
#endif // CONSOLESERVER_H

@ -1,56 +0,0 @@
#ifndef TSDEQUE_H
#define TSDEQUE_H
#include <boost/thread.hpp>
//
// Adapted from http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html
//
template<typename Data>
class TsDeque
{
private:
std::deque<Data> the_queue;
mutable boost::mutex the_mutex;
boost::condition_variable the_condition_variable;
public:
void push_back(Data const& data)
{
boost::mutex::scoped_lock lock(the_mutex);
the_queue.push_back(data);
lock.unlock();
the_condition_variable.notify_one();
}
bool empty() const
{
boost::mutex::scoped_lock lock(the_mutex);
return the_queue.empty();
}
bool try_pop_front(Data& popped_value)
{
boost::mutex::scoped_lock lock(the_mutex);
if(the_queue.empty())
return false;
popped_value=the_queue.front();
the_queue.pop_front();
return true;
}
void wait_and_pop_front(Data& popped_value)
{
boost::mutex::scoped_lock lock(the_mutex);
while(the_queue.empty())
{
the_condition_variable.wait(lock);
}
popped_value=the_queue.front();
the_queue.pop_front();
}
};
#endif // TSDEQUE_H
Loading…
Cancel
Save