Index: test/Makefile.am
===================================================================
--- test/Makefile.am	(revision 91)
+++ test/Makefile.am	(revision 99)
@@ -1,3 +1,3 @@
-AM_CPPFLAGS=-I $(top_srcdir)/src/client/c $(VSCPL_CFLAGS)
+AM_CPPFLAGS=-I $(top_srcdir)/src/client/c -I$(top_srcdir)/src/plugin/src $(VSCPL_CFLAGS)
 
 LDADD=../src/client/c/hu/varadiistvan/xplra/libxplra.la
@@ -6,5 +6,5 @@
 endif
 
-noinst_PROGRAMS=ctest basictest basicctest multigettest multigetctest multisettest multisetctest hotkeytest hotkeyctest
+noinst_PROGRAMS=ctest basictest basicctest multigettest multigetctest multisettest multisetctest hotkeytest hotkeyctest emulator
 
 ctest_SOURCES=ctest.c
@@ -41,4 +41,6 @@
 endif
 
+emulator_SOURCES=emulator.cc
+
 EXTRA_DIST=\
         runpy.sh                \
Index: test/emulator.cc
===================================================================
--- test/emulator.cc	(revision 99)
+++ test/emulator.cc	(revision 99)
@@ -0,0 +1,215 @@
+// Copyright (c) 2019 by István Váradi
+
+// This file is part of XPLRA, a remote-access plugin for X-Plane
+
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// The views and conclusions contained in the software and documentation are those
+// of the authors and should not be interpreted as representing official policies,
+// either expressed or implied, of the FreeBSD Project.
+//------------------------------------------------------------------------------
+
+#include "xplra/Protocol.h"
+
+#include <hu/varadiistvan/scpl/io/Waiter.h>
+#include <hu/varadiistvan/scpl/io/LocalServerSocket.h>
+#include <hu/varadiistvan/scpl/io/LocalAcceptor.h>
+#include <hu/varadiistvan/scpl/io/BufferedStream.h>
+#include <hu/varadiistvan/scpl/io/DataStream.h>
+
+#include <hu/varadiistvan/scpl/Thread.h>
+
+//------------------------------------------------------------------------------
+
+using xplra::Protocol;
+
+using hu::varadiistvan::scpl::io::Waiter;
+using hu::varadiistvan::scpl::io::LocalServerSocket;
+using hu::varadiistvan::scpl::io::LocalAcceptor;
+
+using std::string;
+
+//------------------------------------------------------------------------------
+
+class ServerThread : public hu::varadiistvan::scpl::Thread
+{
+private:
+    /**
+     * Our waiter.
+     */
+    hu::varadiistvan::scpl::io::Waiter waiter;
+
+    /**
+     * The buffered stream to use for communication at the low-level.
+     */
+    hu::varadiistvan::scpl::io::BufferedStream* bufferedStream;
+
+    /**
+     * The data stream being used for communication.
+     */
+    hu::varadiistvan::scpl::io::DataStream stream;
+
+public:
+    /**
+     * Construct the thread by using a LocalSocket returned by the
+     * given acceptor.
+     */
+    ServerThread(hu::varadiistvan::scpl::io::LocalAcceptor& acceptor);
+
+    /**
+     * Destroy the thread.
+     */
+    virtual ~ServerThread();
+
+    /**
+     * Perform the thread's operation.
+     */
+    virtual void run();
+};
+
+//------------------------------------------------------------------------------
+
+ServerThread::ServerThread(LocalAcceptor& acceptor) :
+    bufferedStream(acceptor.getSocket(&waiter)),
+    stream(*bufferedStream)
+{
+}
+
+//------------------------------------------------------------------------------
+
+ServerThread::~ServerThread()
+{
+    delete bufferedStream;
+}
+
+//------------------------------------------------------------------------------
+
+void ServerThread::run()
+{
+    printf("hu.varadiistvan.xplra.ServerThread[%p]::run\n", this);
+    while(stream) {
+        uint8_t command = stream.readU8();
+        if (!stream) continue;
+
+        printf("hu.varadiistvan.xplra.ServerThread[%p]::run: command=0x%02x\n",
+               this, command);
+
+        if (command==Protocol::COMMAND_GET_VERSIONS) {
+            printf("Get versions\n");
+            int xplaneVersion = 0x110000;
+            int xplmVersion = 0x31000;
+
+
+            stream.writeU8(Protocol::RESULT_OK);
+            stream.writeS32(xplaneVersion);
+            stream.writeS32(xplmVersion);
+            stream.writeS32(Protocol::version);
+        } else if (command==Protocol::COMMAND_GET_MULTI) {
+            printf("Get multi\n");
+            uint32_t numTasks = stream.readU32();
+            if (!stream) break;
+
+            for(size_t i = 0; i<numTasks; ++i) {
+                string name = stream.readString();
+                uint8_t type = stream.readU8();
+                if (!stream) break;
+
+                printf("    name='%s', type=%u\n",
+                       name.c_str(), type);
+
+                if (type==Protocol::TYPE_FLOAT_ARRAY ||
+                    type==Protocol::TYPE_INT_ARRAY ||
+                    type==Protocol::TYPE_BYTE_ARRAY)
+                {
+                    int length = stream.readS32();
+                    int offset = stream.readS32();
+                    if (!stream) break;
+                }
+
+            }
+
+            stream.writeU8(Protocol::RESULT_UNKNOWN_DATAREF);
+            stream.writeU32(0);
+        }
+//         if (command==Protocol::COMMAND_GET_SINGLE) {
+//             if (!handleGetSingle()) break;
+//         } else if (command==Protocol::COMMAND_SET_SINGLE) {
+//             if (!handleSetSingle()) break;
+//         } else if (command==Protocol::COMMAND_GET_MULTI) {
+//             if (!handleGetMulti()) break;
+//         } else if (command==Protocol::COMMAND_SET_MULTI) {
+//             if (!handleSetMulti()) break;
+//         } else if (command==Protocol::COMMAND_REGISTER_GET_MULTI) {
+//             if (!handleRegisterGetMulti()) break;
+//         } else if (command==Protocol::COMMAND_UNREGISTER_GET_MULTI) {
+//             if (!handleUnregisterGetMulti()) break;
+//         } else if (command==Protocol::COMMAND_EXECUTE_GET_MULTI) {
+//             if (!handleExecuteGetMulti()) break;
+//         } else if (command==Protocol::COMMAND_REGISTER_SET_MULTI) {
+//             if (!handleRegisterSetMulti()) break;
+//         } else if (command==Protocol::COMMAND_UNREGISTER_SET_MULTI) {
+//             if (!handleUnregisterSetMulti()) break;
+//         } else if (command==Protocol::COMMAND_EXECUTE_SET_MULTI) {
+//             if (!handleExecuteSetMulti()) break;
+//         } else if (command==Protocol::COMMAND_GET_VERSIONS) {
+//             if (!handleGetVersions()) break;
+//         } else if (command==Protocol::COMMAND_RELOAD_PLUGINS) {
+//             if (!handleReloadPlugins()) break;
+// #ifdef XPLM200
+//         } else if (command==Protocol::COMMAND_SAVE_SITUATION) {
+//             if (!handleSaveSituation()) break;
+// #endif
+//         } else if (command==Protocol::COMMAND_SHOW_MESSAGE) {
+//             if (!handleShowMessage()) break;
+//         } else if (command==Protocol::COMMAND_REGISTER_HOTKEYS) {
+//             if (!handleRegisterHotkeys()) break;
+//         } else if (command==Protocol::COMMAND_QUERY_HOTKEYS) {
+//             if (!handleQueryHotkeys()) break;
+//         } else if (command==Protocol::COMMAND_UNREGISTER_HOTKEYS) {
+//             if (!handleUnregisterHotkeys()) break;
+//         } else {
+//             stream.writeU8(Protocol::RESULT_INVALID_COMMAND);
+//         }
+        stream.flush();
+    }
+
+    // destroyHotkeys();
+
+    printf("hu.varadiistvan.xplra.ServerThread[%p]::run: quitting\n", this);
+}
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+
+int main()
+{
+    Waiter waiter;
+
+    LocalServerSocket socket("xplra", &waiter);
+    LocalAcceptor& acceptor = socket.getAcceptor();
+    while(!acceptor.failed()) {
+        while (acceptor.accept()) {
+            ServerThread* serverThread = new ServerThread(acceptor);
+            serverThread->start();
+        }
+        waiter.wait();
+    }
+}
