Killed clientconsole, mwcompiler, mwinterpreter and command server
parent
f693a2942e
commit
532ccbd1f8
@ -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;
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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…
Reference in New Issue