diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index d2916b0a71..a39fefada2 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -8,6 +8,7 @@ #include +#include #include #include "MetavoxelServer.h" @@ -88,7 +89,9 @@ void MetavoxelSession::sendData(const QByteArray& data) { } void MetavoxelSession::readPacket(Bitstream& in) { - qDebug("got packet from client!\n"); + QVariant msg; + in >> msg; + qDebug() << msg.value().test << "\n"; Bitstream& out = _sequencer.startPacket(); _sequencer.endPacket(); diff --git a/cmake/macros/AutoMTC.cmake b/cmake/macros/AutoMTC.cmake new file mode 100644 index 0000000000..f22430a8d0 --- /dev/null +++ b/cmake/macros/AutoMTC.cmake @@ -0,0 +1,20 @@ +macro(AUTO_MTC TARGET ROOT_DIR) + if (NOT TARGET mtc) + add_subdirectory(${ROOT_DIR}/tools/mtc ${ROOT_DIR}/tools/mtc) + endif (NOT TARGET mtc) + + file(GLOB INCLUDE_FILES src/*.h) + + add_custom_command(OUTPUT ${TARGET}_automtc.cpp COMMAND mtc -o ${TARGET}_automtc.cpp ${INCLUDE_FILES} DEPENDS mtc) + + find_package(Qt5Core REQUIRED) + + add_library(${TARGET}_automtc STATIC ${TARGET}_automtc.cpp) + + qt5_use_modules(${TARGET}_automtc Core) + + target_link_libraries(${TARGET} ${TARGET}_automtc) + +endmacro() + + diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 56d2b39056..018c7005cf 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -12,6 +12,7 @@ #include +#include #include #include "Application.h" @@ -193,6 +194,8 @@ MetavoxelClient::MetavoxelClient(const HifiSockAddr& address) : void MetavoxelClient::simulate(float deltaTime) { Bitstream& out = _sequencer.startPacket(); + ClientPositionMessage msg = { 55 }; + out << QVariant::fromValue(msg); _sequencer.endPacket(); } diff --git a/libraries/metavoxels/CMakeLists.txt b/libraries/metavoxels/CMakeLists.txt index 0f9c1c695c..98b2baf7ac 100644 --- a/libraries/metavoxels/CMakeLists.txt +++ b/libraries/metavoxels/CMakeLists.txt @@ -13,6 +13,9 @@ find_package(Qt5Widgets REQUIRED) include(${MACRO_DIR}/SetupHifiLibrary.cmake) setup_hifi_library(${TARGET_NAME}) +include(${MACRO_DIR}/AutoMTC.cmake) +auto_mtc(${TARGET_NAME} ${ROOT_DIR}) + qt5_use_modules(${TARGET_NAME} Widgets Script) include(${MACRO_DIR}/IncludeGLM.cmake) diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 8fdeb1c1fc..bd991829a9 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -40,6 +40,7 @@ IDStreamer& IDStreamer::operator<<(int value) { } IDStreamer& IDStreamer::operator>>(int& value) { + value = 0; _stream.read(&value, _bits); if (value == (1 << _bits) - 1) { _bits++; @@ -93,7 +94,8 @@ Bitstream& Bitstream::read(void* data, int bits, int offset) { _underlying >> _byte; } int bitsToRead = qMin(BITS_IN_BYTE - _position, qMin(BITS_IN_BYTE - offset, bits)); - *dest |= ((_byte >> _position) & ((1 << bitsToRead) - 1)) << offset; + int mask = ((1 << bitsToRead) - 1) << offset; + *dest = (*dest & ~mask) | (((_byte >> _position) << offset) & mask); _position = (_position + bitsToRead) & LAST_BIT_POSITION; if ((offset += bitsToRead) == BITS_IN_BYTE) { dest++; @@ -166,7 +168,7 @@ Bitstream& Bitstream::operator<<(int value) { } Bitstream& Bitstream::operator>>(int& value) { - qint32 sizedValue; + qint32 sizedValue = 0; read(&sizedValue, 32); value = sizedValue; return *this; @@ -180,6 +182,7 @@ Bitstream& Bitstream::operator<<(const QByteArray& string) { Bitstream& Bitstream::operator>>(QByteArray& string) { int size; *this >> size; + string.resize(size); return read(string.data(), size * BITS_IN_BYTE); } @@ -191,6 +194,7 @@ Bitstream& Bitstream::operator<<(const QString& string) { Bitstream& Bitstream::operator>>(QString& string) { int size; *this >> size; + string.resize(size); return read(string.data(), size * sizeof(QChar) * BITS_IN_BYTE); } diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index d86bad67cc..2e75c85a0a 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -13,6 +13,7 @@ #include #include #include +#include class QByteArray; class QDataStream; @@ -278,10 +279,12 @@ public: #define REGISTER_SIMPLE_TYPE_STREAMER(x) static int x##Streamer = \ Bitstream::registerTypeStreamer(QMetaType::type(#x), new SimpleTypeStreamer()); -/// Declares the metatype and the streaming operators. +/// Declares the metatype and the streaming operators. The last line +/// ensures that the generated file will be included in the link phase. #define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \ Bitstream& operator<<(Bitstream& out, const X& obj); \ - Bitstream& operator>>(Bitstream& in, X& obj); + Bitstream& operator>>(Bitstream& in, X& obj); \ + static const int* _TypePtr##X = &X::Type; /// Registers a streamable type and its streamer. template int registerStreamableMetaType() { @@ -290,4 +293,10 @@ template int registerStreamableMetaType() { return type; } +/// Flags a class as streamable (use as you would Q_OBJECT). +#define STREAMABLE public: static const int Type; private: + +/// Flags a field or base class as streaming. +#define STREAM + #endif /* defined(__interface__Bitstream__) */ diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 389d8b68bf..6a199fe0f7 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -13,9 +13,11 @@ /// A message containing the position of a client. class ClientPositionMessage { + STREAMABLE + public: - int test; + STREAM int test; }; DECLARE_STREAMABLE_METATYPE(ClientPositionMessage) diff --git a/tools/mtc/CMakeLists.txt b/tools/mtc/CMakeLists.txt new file mode 100644 index 0000000000..95cb95d573 --- /dev/null +++ b/tools/mtc/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.8) + +set(TARGET_NAME mtc) + +set(ROOT_DIR ../..) +set(MACRO_DIR ${ROOT_DIR}/cmake/macros) + +include(${MACRO_DIR}/SetupHifiProject.cmake) +setup_hifi_project(${TARGET_NAME} TRUE) + + diff --git a/tools/mtc/src/main.cpp b/tools/mtc/src/main.cpp new file mode 100644 index 0000000000..77f0a069b5 --- /dev/null +++ b/tools/mtc/src/main.cpp @@ -0,0 +1,180 @@ +// +// main.cpp +// mtc +// +// Created by Andrzej Kapolka on 12/31/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +class Class { +public: + QString name; + QStringList bases; +}; + +class Streamable { +public: + Class clazz; + QStringList fields; +}; + +void processInput(QTextStream& in, QList* streamables) { + Class clazz; + Streamable currentStreamable; + + QRegExp exp( + "(/\\*.*\\*/)|" // multi-line comments + "(//.*\n)|" // single-line comments + "(\\s*#.*\n)|" // preprocessor definitions + "(\\s*STREAMABLE\\s+)|" // STREAMABLE tag for classes + "(\\s*STREAM\\s+.*;)|" // STREAM tag for fields + "(\\s*class\\s+[^;]+\\{)" // class definition + ); + exp.setMinimal(true); + + QRegExp classExp("class (\\w+) ?:?([^:]*)\\{"); + + // read in the entire input and look for matches with our expression + QString all = in.readAll(); + for (int off = 0; (off = exp.indexIn(all, off)) != -1; off += exp.matchedLength()) { + QString match = exp.cap().simplified(); + if (match.startsWith("/*") || match.startsWith("//") || match.startsWith('#')) { + continue; // comment, preprocessor definition + } + if (match.startsWith("STREAMABLE")) { + if (clazz.name.isEmpty()) { + cerr << "Found STREAMABLE marker before class definition." << endl; + continue; + } + if (!currentStreamable.clazz.name.isEmpty()) { + streamables->append(currentStreamable); + } + currentStreamable.clazz = clazz; + currentStreamable.fields.clear(); + + } else if (match.startsWith("STREAM")) { + match.chop(1); // get rid of the semicolon + match = match.trimmed(); // and any space before it + currentStreamable.fields.append(match.mid(match.lastIndexOf(' ') + 1)); + + } else { // match.startsWith("class") + classExp.exactMatch(match); + clazz.name = classExp.cap(1); + clazz.bases.clear(); + foreach (const QString& bstr, classExp.cap(2).split(',')) { + QString base = bstr.trimmed(); + if (!base.isEmpty() && base.startsWith("STREAM")) { + clazz.bases.append(base.mid(base.lastIndexOf(' ') + 1)); + } + } + } + } + if (!currentStreamable.clazz.name.isEmpty()) { + streamables->append(currentStreamable); + } +} + +void generateOutput (QTextStream& out, const QList& streamables) { + foreach (const Streamable& str, streamables) { + const QString& name = str.clazz.name; + + out << "Bitstream& operator<< (Bitstream& out, const " << name << "& obj) {\n"; + foreach (const QString& base, str.clazz.bases) { + out << " out << static_cast(obj);\n"; + } + foreach (const QString& field, str.fields) { + out << " out << obj." << field << ";\n"; + } + out << " return out;\n"; + out << "}\n"; + + out << "Bitstream& operator>> (Bitstream& in, " << name << "& obj) {\n"; + foreach (const QString& base, str.clazz.bases) { + out << " in >> static_cast<" << base << "&>(obj);\n"; + } + foreach (const QString& field, str.fields) { + out << " in >> obj." << field << ";\n"; + } + out << " return in;\n"; + out << "}\n"; + + out << "const int " << name << "::Type = registerStreamableMetaType<" << name << ">();\n\n"; + } +} + +int main (int argc, char** argv) { + // process the command line arguments + QStringList inputs; + QString output; + for (int ii = 1; ii < argc; ii++) { + QString arg(argv[ii]); + if (!arg.startsWith('-')) { + inputs.append(arg); + continue; + } + QStringRef name = arg.midRef(1); + if (name == "o") { + if (++ii == argc) { + cerr << "Missing file name argument for -o" << endl; + return 1; + } + output = argv[ii]; + + } else { + cerr << "Unknown option " << arg.toStdString() << endl; + return 1; + } + } + if (inputs.isEmpty()) { + cerr << "Usage: mtc [OPTION]... input files" << endl; + cerr << "Where options include:" << endl; + cerr << " -o filename: Send output to filename rather than standard output." << endl; + return 0; + } + + QList streamables; + foreach (const QString& input, inputs) { + QFile ifile(input); + if (!ifile.open(QIODevice::ReadOnly | QIODevice::Text)) { + cerr << ("Couldn't open " + input + ": " + ifile.errorString()).toStdString() << endl; + continue; + } + QTextStream istream(&ifile); + int oldSize = streamables.size(); + processInput(istream, &streamables); + if (streamables.size() == oldSize) { + // no streamables; remove from list + inputs.removeOne(input); + } + } + + QFile ofile(output); + if (output.isNull()) { + ofile.open(stdout, QIODevice::WriteOnly | QIODevice::Text); + + } else if (!ofile.open(QIODevice::WriteOnly | QIODevice::Text)) { + cerr << ("Couldn't open " + output + ": " + ofile.errorString()).toStdString() << endl; + return 1; + } + + QTextStream ostream(&ofile); + ostream << "// generated by mtc\n"; + foreach (const QString& input, inputs) { + ostream << "#include \"" << input << "\"\n"; + } + generateOutput(ostream, streamables); + + return 0; +}