<commit_before>#pragma once

#define DATA_FILE_EXTENSION ".mwm"
#define DATA_FILE_EXTENSION_TMP ".mwm.tmp"
#define DATA_FILE_TAG "dat"
#define GEOMETRY_FILE_TAG "geom"
#define TRIANGLE_FILE_TAG "trg"
#define INDEX_FILE_TAG "idx"
#define SEARCH_INDEX_FILE_TAG "sdx"
#define HEADER_FILE_TAG "header"
#define VERSION_FILE_TAG "version"

#define COUNTRIES_FILE  "countries.txt"

#define WORLD_FILE_NAME "World"
#define WORLD_COASTS_FILE_NAME "WorldCoasts"

#define SETTINGS_FILE_NAME "settings.ini"

#define SEARCH_CATEGORIES_FILE_NAME "categories.txt"

#define PACKED_POLYGONS_FILE "packed_polygons.bin"
#define PACKED_POLYGONS_INFO_TAG "info"

#define DRAWING_RULES_TXT_FILE "drules_proto.txt"
#define DRAWING_RULES_BIN_FILE "drules_proto.bin"

/// How many langs we're supporting on indexing stage
#define MAX_SUPPORTED_LANGUAGES 64

#ifdef OMIM_PRODUCTION
  #define URL_SERVERS_LIST "http://active.servers.url"
  #define DEFAULT_SERVERS_JSON "[\"http://1st.default.server/\",\"http://2nd.default.server/\"]"
#else
  #define URL_SERVERS_LIST "http://metaserver-dev.mapswithme.com/server_data/active_servers"
  #define DEFAULT_SERVERS_JSON "[\"http://svobodu404popugajam.mapswithme.com:34568/maps/\",\"http://svobodu404popugajam.mapswithme.com:34568/maps/\"]"
#endif

#define REDBUTTON_SERVER_URL "http://redbutton.mapswithme.com/enable_search_banner"
<commit_msg>Added 3rd server to default list<commit_after>#pragma once

#define DATA_FILE_EXTENSION ".mwm"
#define DATA_FILE_EXTENSION_TMP ".mwm.tmp"
#define DATA_FILE_TAG "dat"
#define GEOMETRY_FILE_TAG "geom"
#define TRIANGLE_FILE_TAG "trg"
#define INDEX_FILE_TAG "idx"
#define SEARCH_INDEX_FILE_TAG "sdx"
#define HEADER_FILE_TAG "header"
#define VERSION_FILE_TAG "version"

#define COUNTRIES_FILE  "countries.txt"

#define WORLD_FILE_NAME "World"
#define WORLD_COASTS_FILE_NAME "WorldCoasts"

#define SETTINGS_FILE_NAME "settings.ini"

#define SEARCH_CATEGORIES_FILE_NAME "categories.txt"

#define PACKED_POLYGONS_FILE "packed_polygons.bin"
#define PACKED_POLYGONS_INFO_TAG "info"

#define DRAWING_RULES_TXT_FILE "drules_proto.txt"
#define DRAWING_RULES_BIN_FILE "drules_proto.bin"

/// How many langs we're supporting on indexing stage
#define MAX_SUPPORTED_LANGUAGES 64

#ifdef OMIM_PRODUCTION
  #define URL_SERVERS_LIST "http://active.servers.url"
  #define DEFAULT_SERVERS_JSON "[\"http://1st.default.server/\",\"http://2nd.default.server/\",\"http://3rd.default.server/\"]"
#else
  #define URL_SERVERS_LIST "http://metaserver-dev.mapswithme.com/server_data/active_servers"
  #define DEFAULT_SERVERS_JSON "[\"http://svobodu404popugajam.mapswithme.com:34568/maps/\",\"http://svobodu404popugajam.mapswithme.com:34568/maps/\"]"
#endif

#define REDBUTTON_SERVER_URL "http://redbutton.mapswithme.com/enable_search_banner"
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Pavlo Lavrenenko
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <Rubik.h>
#include <ObjectManager.h>
#include <RenderManager.h>
#include <Window.h>
#include <OpenGL.h>
#include <Shader.h>
#include <Entity.h>
#include <Font.h>
#include <Layout.h>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdlib>

namespace Rubik {

Rubik::Rubik() {
    std::srand(static_cast<int>(std::time(nullptr)));
}

int Rubik::getShuffles() const {
    return this->shuffles;
}

void Rubik::setShuffles(int shuffles) {
    this->shuffles = shuffles;
}

void Rubik::onMouseMotion(int x, int y) {
    static Graphene::MousePosition mousePosition(this->getWindow()->getMousePosition());
    const Graphene::MouseState& mouseState = this->getWindow()->getMouseState();

    Math::Vec3 motionDirection(static_cast<float>(x - mousePosition.first), static_cast<float>(y - mousePosition.second), 0.0f);
    mousePosition = this->getWindow()->getMousePosition();

    int objectId = -1;
    bool isCubeSelected = false;
    bool isFrontCubeSelected = false;

    switch (this->state) {
        case GameState::RUNNING:
            if (mouseState[Graphene::MouseButton::BUTTON_LEFT] || mouseState[Graphene::MouseButton::BUTTON_RIGHT]) {
                int pickupX = x * this->pickupBuffer->getWidth() / this->getWindow()->getWidth();
                int pickupY = (this->getWindow()->getHeight() - y) * this->pickupBuffer->getHeight() / this->getWindow()->getHeight();
                this->pickupBuffer->getPixel(pickupX, pickupY, GL_RED_INTEGER, GL_INT, &objectId);

                isCubeSelected = (std::find(this->puzzleObjects.begin(), this->puzzleObjects.end(), objectId) != this->puzzleObjects.end());
                isFrontCubeSelected = (isCubeSelected && std::get<2>(this->puzzle->getCubePosition(objectId)) == 0);
            }

            if (mouseState[Graphene::MouseButton::BUTTON_LEFT] && !mouseState[Graphene::MouseButton::BUTTON_RIGHT]) {
                if (isFrontCubeSelected) {
                    this->rotateCube(objectId, motionDirection);
                }
            } else if (mouseState[Graphene::MouseButton::BUTTON_RIGHT] && !mouseState[Graphene::MouseButton::BUTTON_LEFT]) {
                if (isCubeSelected) {
                    this->rotateCube(-1, motionDirection);
                }
            }
            break;

        default:
            break;
    }
}

void Rubik::onKeyboardKey(Graphene::KeyboardKey key, bool state) {
    static bool pausePressed = false;
    static float rotationSpeed = 0.0f;

    switch (this->state) {
        case GameState::RUNNING:
            if (key == Graphene::KeyboardKey::KEY_S) {
                if (state) {
                    if (rotationSpeed == 0.0f) {
                        rotationSpeed = this->puzzle->getRotationSpeed();
                        this->puzzle->setRotationSpeed(rotationSpeed * 2.0f);
                    }
                } else {
                    this->puzzle->setRotationSpeed(rotationSpeed);
                    rotationSpeed = 0.0f;
                }
            }
            // Fall through

        case GameState::PAUSED:
            if (key == Graphene::KeyboardKey::KEY_P) {
                if (state) {
                    if (!pausePressed) {
                        this->state = (this->state == GameState::RUNNING) ? GameState::PAUSED : GameState::RUNNING;
                        pausePressed = true;
                    }
                } else {
                    pausePressed = false;
                }
            }
            break;

        default:
            break;
    }
}

void Rubik::onSetup() {
    auto& renderManager = Graphene::GetRenderManager();
    auto& objectManager = Graphene::GetObjectManager();

    renderManager.setShader(Graphene::RenderStep::BUFFER, objectManager.createShader("shaders/object_pickup.shader"));
    renderManager.setRenderCallback(Graphene::RenderStep::BUFFER, [](const std::shared_ptr<Graphene::Object> object) {
        auto& renderManager = Graphene::GetRenderManager();

        auto shader = renderManager.getShader(renderManager.getRenderStep());
        shader->setUniform("objectId", object->getId());
    });

    this->setupScene();
    this->setupUI();
}

void Rubik::onTeardown() {
    Graphene::GetRenderManager().setShader(Graphene::RenderStep::BUFFER, nullptr);
}

void Rubik::onIdle() {
    this->updateScene();
    this->updateUI();

    const Graphene::KeyboardState& keyboardState = this->getWindow()->getKeyboardState();
    if (this->state == GameState::RUNNING && !keyboardState[Graphene::KeyboardKey::KEY_S]) {
        this->gameTime += this->getFrameTime();
    }
}

void Rubik::setupScene() {
    /* Setup scene */

    auto scene = this->createScene();
    scene->setAmbientEnergy(0.2f);

    auto player = scene->createNode();
    auto cube = scene->createNode();
    this->puzzle = std::make_shared<Puzzle>();

    auto sceneRoot = scene->getRootNode();
    sceneRoot->attachNode(player);
    sceneRoot->attachNode(cube);

    /* Populate scene with objects */

    auto& objectManager = Graphene::GetObjectManager();
    auto& renderManager = Graphene::GetRenderManager();
    renderManager.setLightPass(true);

    auto background = objectManager.createEntity("assets/background.entity");
    background->translate(0.0f, 0.0f, 5.0f);
    background->scale(25.0f, 25.0f, 1.0f);
    sceneRoot->attachObject(background);

    auto camera = objectManager.createCamera(Graphene::ProjectionType::PERSPECTIVE);
    auto pickupCamera = objectManager.createCamera(Graphene::ProjectionType::PERSPECTIVE);
    auto light = objectManager.createLight(Graphene::LightType::DIRECTED);

    player->attachObject(camera);
    player->attachObject(pickupCamera);
    player->attachObject(light);
    player->translate(0.25f, -0.25f, -4.5f);

    for (int i = -1; i <= 1; i++) {
        for (int j = -1; j <= 1; j++) {
            for (int k = -1; k <= 1; k++) {
                auto cubepart = scene->createNode();
                cube->attachNode(cubepart);

                auto entity = objectManager.createEntity("assets/cubepart.entity");
                entity->translate(static_cast<float>(i), static_cast<float>(j), static_cast<float>(k));
                cubepart->attachObject(entity);

                this->puzzle->attachCube(entity);
                this->puzzleObjects.push_back(entity->getId());
            }
        }
    }

    cube->roll(-30.0f);
    cube->yaw(-30.0f);
    this->puzzle->shuffle(this->shuffles);

    /* Update default viewport with camera */

    auto window = this->getWindow();
    auto viewport = window->createViewport(0, 0, window->getWidth(), window->getHeight());
    viewport->setCamera(camera);

    /* Create framebuffer for object picking */

    int bufferWidth = 256;
    int bufferHeight = static_cast<int>(bufferWidth / camera->getAspectRatio());
    this->pickupBuffer = this->createFrameBuffer(bufferWidth, bufferHeight, GL_R32I);  // Render object IDs into texture
    this->pickupBuffer->setAlphaBlending(false);  // Not supported for integer framebuffers

    auto pickupViewport = pickupBuffer->createViewport(0, 0, this->pickupBuffer->getWidth(), this->pickupBuffer->getHeight());
    pickupViewport->setCamera(pickupCamera);
}

void Rubik::setupUI() {
    /* Setup scene */

    auto uiScene = this->createScene();
    auto uiRootNode = uiScene->getRootNode();

    /* Populate scene with objects */

    auto& objectManager = Graphene::GetObjectManager();
    auto camera = objectManager.createCamera(Graphene::ProjectionType::ORTHOGRAPHIC);
    camera->setNearPlane(-1.0f);
    camera->setFarPlane(1.0f);

    auto font = std::make_shared<Graphene::Font>("fonts/dejavu-sans.ttf", 14);
    this->timeLabel = std::make_shared<Graphene::Label>(200, 20, font);
    this->movesLabel = std::make_shared<Graphene::Label>(200, 20, font);
    this->promptLabel = std::make_shared<Graphene::Label>(200, 20, font);

    uiRootNode->attachObject(camera);
    uiRootNode->attachObject(this->timeLabel);
    uiRootNode->attachObject(this->movesLabel);
    uiRootNode->attachObject(this->promptLabel);

    /* Arrange UI elements */

    auto window = this->getWindow();

    auto uiLayout = std::make_shared<Graphene::Layout>();
    uiLayout->addComponent(this->timeLabel, 10, window->getHeight() - 20);
    uiLayout->addComponent(this->movesLabel, 10, window->getHeight() - 40);
    uiLayout->addComponent(this->promptLabel, 10, 10);

    /* Update viewport with camera */

    auto overlay = window->createOverlay(0, 0, window->getWidth(), window->getHeight());
    overlay->setCamera(camera);
    overlay->setLayout(uiLayout);
}

void Rubik::updateScene() {
    const Graphene::KeyboardState& keyboardState = this->getWindow()->getKeyboardState();

    switch (this->state) {
        case GameState::RUNNING:
            if (this->puzzle->isSolved()) {
                this->state = GameState::FINISHED;
            } else if (keyboardState[Graphene::KeyboardKey::KEY_ESCAPE]) {
                this->state = GameState::QUIT;
            } else if (keyboardState[Graphene::KeyboardKey::KEY_S]) {
                this->puzzle->selectCube(this->puzzleObjects[std::rand() % this->puzzleObjects.size()]);
                this->puzzle->setAnimationState(static_cast<AnimationState>(std::rand() % 4 + 1));
            }

            this->puzzle->update(this->getFrameTime());
            break;

        case GameState::QUIT:
            if (keyboardState[Graphene::KeyboardKey::KEY_Y]) {
                this->exit(0);
            } else if (keyboardState[Graphene::KeyboardKey::KEY_N]) {
                this->state = GameState::RUNNING;
            }
            break;

        case GameState::FINISHED:
            if (keyboardState[Graphene::KeyboardKey::KEY_N]) {
                this->exit(0);
            } else if (keyboardState[Graphene::KeyboardKey::KEY_Y]) {
                this->moves = 0;
                this->gameTime = 0.0f;
                this->state = GameState::RUNNING;
                this->puzzle->shuffle(this->shuffles);
            }
            break;

        default:
            break;
    }
}

void Rubik::updateUI() {
    int gameTime = static_cast<int>(this->gameTime);
    int seconds = gameTime % 60;
    int minutes = gameTime / 60;
    int hours = gameTime / 3600;

    std::wstringstream time;
    time << "Time: " << std::setw(2) << std::setfill(L'0') << hours << ":"
                     << std::setw(2) << std::setfill(L'0') << minutes << ":"
                     << std::setw(2) << std::setfill(L'0') << seconds;
    this->timeLabel->setText(time.str());

    std::wstringstream moves;
    moves << "Moves: " << this->moves;
    this->movesLabel->setText(moves.str());

    switch (this->state) {
        case GameState::FINISHED:
            this->promptLabel->setText(L"Done! New game? Y/N");
            this->promptLabel->setVisible(true);
            break;

        case GameState::PAUSED:
            this->promptLabel->setText(L"Paused");
            this->promptLabel->setVisible(true);
            break;

        case GameState::QUIT:
            this->promptLabel->setText(L"Quit? Y/N");
            this->promptLabel->setVisible(true);
            break;

        default:
            this->promptLabel->setVisible(false);
            break;
    }
}

void Rubik::rotateCube(int objectId, const Math::Vec3& direction) {
    if (this->state == GameState::PAUSED) {
        return;  // No action on pause
    }

    if (direction.length() < 1.5f) {
        return;  // Movement is too short
    }

    if (objectId != -1 && this->puzzle->getAnimationState() == AnimationState::IDLE) {
        this->moves++;
    }

    float xDirection = direction.get(Math::Vec3::X);
    float yDirection = direction.get(Math::Vec3::Y);
    AnimationState puzzleState = AnimationState::IDLE;

    if (fabsf(xDirection) > fabsf(yDirection)) {
        puzzleState = (xDirection > 0) ? AnimationState::RIGHT_ROTATION : AnimationState::LEFT_ROTATION;
    } else {
        puzzleState = (yDirection > 0) ? AnimationState::DOWN_ROTATION : AnimationState::UP_ROTATION;
    }

    this->puzzle->selectCube(objectId);
    this->puzzle->setAnimationState(puzzleState);
}

}  // namespace Rubik
<commit_msg>Fix object ID framebuffer size to half the window size<commit_after>/*
 * Copyright (c) 2013 Pavlo Lavrenenko
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <Rubik.h>
#include <ObjectManager.h>
#include <RenderManager.h>
#include <Window.h>
#include <OpenGL.h>
#include <Shader.h>
#include <Entity.h>
#include <Font.h>
#include <Layout.h>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdlib>

namespace Rubik {

Rubik::Rubik() {
    std::srand(static_cast<int>(std::time(nullptr)));
}

int Rubik::getShuffles() const {
    return this->shuffles;
}

void Rubik::setShuffles(int shuffles) {
    this->shuffles = shuffles;
}

void Rubik::onMouseMotion(int x, int y) {
    static Graphene::MousePosition mousePosition(this->getWindow()->getMousePosition());
    const Graphene::MouseState& mouseState = this->getWindow()->getMouseState();

    Math::Vec3 motionDirection(static_cast<float>(x - mousePosition.first), static_cast<float>(y - mousePosition.second), 0.0f);
    mousePosition = this->getWindow()->getMousePosition();

    int objectId = -1;
    bool isCubeSelected = false;
    bool isFrontCubeSelected = false;

    switch (this->state) {
        case GameState::RUNNING:
            if (mouseState[Graphene::MouseButton::BUTTON_LEFT] || mouseState[Graphene::MouseButton::BUTTON_RIGHT]) {
                int pickupX = x * this->pickupBuffer->getWidth() / this->getWindow()->getWidth();
                int pickupY = (this->getWindow()->getHeight() - y) * this->pickupBuffer->getHeight() / this->getWindow()->getHeight();
                this->pickupBuffer->getPixel(pickupX, pickupY, GL_RED_INTEGER, GL_INT, &objectId);

                isCubeSelected = (std::find(this->puzzleObjects.begin(), this->puzzleObjects.end(), objectId) != this->puzzleObjects.end());
                isFrontCubeSelected = (isCubeSelected && std::get<2>(this->puzzle->getCubePosition(objectId)) == 0);
            }

            if (mouseState[Graphene::MouseButton::BUTTON_LEFT] && !mouseState[Graphene::MouseButton::BUTTON_RIGHT]) {
                if (isFrontCubeSelected) {
                    this->rotateCube(objectId, motionDirection);
                }
            } else if (mouseState[Graphene::MouseButton::BUTTON_RIGHT] && !mouseState[Graphene::MouseButton::BUTTON_LEFT]) {
                if (isCubeSelected) {
                    this->rotateCube(-1, motionDirection);
                }
            }
            break;

        default:
            break;
    }
}

void Rubik::onKeyboardKey(Graphene::KeyboardKey key, bool state) {
    static bool pausePressed = false;
    static float rotationSpeed = 0.0f;

    switch (this->state) {
        case GameState::RUNNING:
            if (key == Graphene::KeyboardKey::KEY_S) {
                if (state) {
                    if (rotationSpeed == 0.0f) {
                        rotationSpeed = this->puzzle->getRotationSpeed();
                        this->puzzle->setRotationSpeed(rotationSpeed * 2.0f);
                    }
                } else {
                    this->puzzle->setRotationSpeed(rotationSpeed);
                    rotationSpeed = 0.0f;
                }
            }
            // Fall through

        case GameState::PAUSED:
            if (key == Graphene::KeyboardKey::KEY_P) {
                if (state) {
                    if (!pausePressed) {
                        this->state = (this->state == GameState::RUNNING) ? GameState::PAUSED : GameState::RUNNING;
                        pausePressed = true;
                    }
                } else {
                    pausePressed = false;
                }
            }
            break;

        default:
            break;
    }
}

void Rubik::onSetup() {
    auto& renderManager = Graphene::GetRenderManager();
    auto& objectManager = Graphene::GetObjectManager();

    renderManager.setShader(Graphene::RenderStep::BUFFER, objectManager.createShader("shaders/object_pickup.shader"));
    renderManager.setRenderCallback(Graphene::RenderStep::BUFFER, [](const std::shared_ptr<Graphene::Object> object) {
        auto& renderManager = Graphene::GetRenderManager();

        auto shader = renderManager.getShader(renderManager.getRenderStep());
        shader->setUniform("objectId", object->getId());
    });

    this->setupScene();
    this->setupUI();
}

void Rubik::onTeardown() {
    Graphene::GetRenderManager().setShader(Graphene::RenderStep::BUFFER, nullptr);
}

void Rubik::onIdle() {
    this->updateScene();
    this->updateUI();

    const Graphene::KeyboardState& keyboardState = this->getWindow()->getKeyboardState();
    if (this->state == GameState::RUNNING && !keyboardState[Graphene::KeyboardKey::KEY_S]) {
        this->gameTime += this->getFrameTime();
    }
}

void Rubik::setupScene() {
    /* Setup scene */

    auto scene = this->createScene();
    scene->setAmbientEnergy(0.2f);

    auto player = scene->createNode();
    auto cube = scene->createNode();
    this->puzzle = std::make_shared<Puzzle>();

    auto sceneRoot = scene->getRootNode();
    sceneRoot->attachNode(player);
    sceneRoot->attachNode(cube);

    /* Populate scene with objects */

    auto& objectManager = Graphene::GetObjectManager();
    auto& renderManager = Graphene::GetRenderManager();
    renderManager.setLightPass(true);

    auto background = objectManager.createEntity("assets/background.entity");
    background->translate(0.0f, 0.0f, 5.0f);
    background->scale(25.0f, 25.0f, 1.0f);
    sceneRoot->attachObject(background);

    auto camera = objectManager.createCamera(Graphene::ProjectionType::PERSPECTIVE);
    auto pickupCamera = objectManager.createCamera(Graphene::ProjectionType::PERSPECTIVE);
    auto light = objectManager.createLight(Graphene::LightType::DIRECTED);

    player->attachObject(camera);
    player->attachObject(pickupCamera);
    player->attachObject(light);
    player->translate(0.25f, -0.25f, -4.5f);

    for (int i = -1; i <= 1; i++) {
        for (int j = -1; j <= 1; j++) {
            for (int k = -1; k <= 1; k++) {
                auto cubepart = scene->createNode();
                cube->attachNode(cubepart);

                auto entity = objectManager.createEntity("assets/cubepart.entity");
                entity->translate(static_cast<float>(i), static_cast<float>(j), static_cast<float>(k));
                cubepart->attachObject(entity);

                this->puzzle->attachCube(entity);
                this->puzzleObjects.push_back(entity->getId());
            }
        }
    }

    cube->roll(-30.0f);
    cube->yaw(-30.0f);
    this->puzzle->shuffle(this->shuffles);

    /* Update default viewport with camera */

    auto window = this->getWindow();
    auto viewport = window->createViewport(0, 0, window->getWidth(), window->getHeight());
    viewport->setCamera(camera);

    /* Create framebuffer for object ID rendering and picking */

    this->pickupBuffer = this->createFrameBuffer(window->getWidth() / 2, window->getHeight() / 2, GL_R32I);
    auto pickupViewport = pickupBuffer->createViewport(0, 0, this->pickupBuffer->getWidth(), this->pickupBuffer->getHeight());
    pickupViewport->setCamera(pickupCamera);
}

void Rubik::setupUI() {
    /* Setup scene */

    auto uiScene = this->createScene();
    auto uiRootNode = uiScene->getRootNode();

    /* Populate scene with objects */

    auto& objectManager = Graphene::GetObjectManager();
    auto camera = objectManager.createCamera(Graphene::ProjectionType::ORTHOGRAPHIC);
    camera->setNearPlane(-1.0f);
    camera->setFarPlane(1.0f);

    auto font = std::make_shared<Graphene::Font>("fonts/dejavu-sans.ttf", 14);
    this->timeLabel = std::make_shared<Graphene::Label>(200, 20, font);
    this->movesLabel = std::make_shared<Graphene::Label>(200, 20, font);
    this->promptLabel = std::make_shared<Graphene::Label>(200, 20, font);

    uiRootNode->attachObject(camera);
    uiRootNode->attachObject(this->timeLabel);
    uiRootNode->attachObject(this->movesLabel);
    uiRootNode->attachObject(this->promptLabel);

    /* Arrange UI elements */

    auto window = this->getWindow();

    auto uiLayout = std::make_shared<Graphene::Layout>();
    uiLayout->addComponent(this->timeLabel, 10, window->getHeight() - 20);
    uiLayout->addComponent(this->movesLabel, 10, window->getHeight() - 40);
    uiLayout->addComponent(this->promptLabel, 10, 10);

    /* Update viewport with camera */

    auto overlay = window->createOverlay(0, 0, window->getWidth(), window->getHeight());
    overlay->setCamera(camera);
    overlay->setLayout(uiLayout);
}

void Rubik::updateScene() {
    const Graphene::KeyboardState& keyboardState = this->getWindow()->getKeyboardState();

    switch (this->state) {
        case GameState::RUNNING:
            if (this->puzzle->isSolved()) {
                this->state = GameState::FINISHED;
            } else if (keyboardState[Graphene::KeyboardKey::KEY_ESCAPE]) {
                this->state = GameState::QUIT;
            } else if (keyboardState[Graphene::KeyboardKey::KEY_S]) {
                this->puzzle->selectCube(this->puzzleObjects[std::rand() % this->puzzleObjects.size()]);
                this->puzzle->setAnimationState(static_cast<AnimationState>(std::rand() % 4 + 1));
            }

            this->puzzle->update(this->getFrameTime());
            break;

        case GameState::QUIT:
            if (keyboardState[Graphene::KeyboardKey::KEY_Y]) {
                this->exit(0);
            } else if (keyboardState[Graphene::KeyboardKey::KEY_N]) {
                this->state = GameState::RUNNING;
            }
            break;

        case GameState::FINISHED:
            if (keyboardState[Graphene::KeyboardKey::KEY_N]) {
                this->exit(0);
            } else if (keyboardState[Graphene::KeyboardKey::KEY_Y]) {
                this->moves = 0;
                this->gameTime = 0.0f;
                this->state = GameState::RUNNING;
                this->puzzle->shuffle(this->shuffles);
            }
            break;

        default:
            break;
    }
}

void Rubik::updateUI() {
    int gameTime = static_cast<int>(this->gameTime);
    int seconds = gameTime % 60;
    int minutes = gameTime / 60;
    int hours = gameTime / 3600;

    std::wstringstream time;
    time << "Time: " << std::setw(2) << std::setfill(L'0') << hours << ":"
                     << std::setw(2) << std::setfill(L'0') << minutes << ":"
                     << std::setw(2) << std::setfill(L'0') << seconds;
    this->timeLabel->setText(time.str());

    std::wstringstream moves;
    moves << "Moves: " << this->moves;
    this->movesLabel->setText(moves.str());

    switch (this->state) {
        case GameState::FINISHED:
            this->promptLabel->setText(L"Done! New game? Y/N");
            this->promptLabel->setVisible(true);
            break;

        case GameState::PAUSED:
            this->promptLabel->setText(L"Paused");
            this->promptLabel->setVisible(true);
            break;

        case GameState::QUIT:
            this->promptLabel->setText(L"Quit? Y/N");
            this->promptLabel->setVisible(true);
            break;

        default:
            this->promptLabel->setVisible(false);
            break;
    }
}

void Rubik::rotateCube(int objectId, const Math::Vec3& direction) {
    if (this->state == GameState::PAUSED) {
        return;  // No action on pause
    }

    if (direction.length() < 1.5f) {
        return;  // Movement is too short
    }

    if (objectId != -1 && this->puzzle->getAnimationState() == AnimationState::IDLE) {
        this->moves++;
    }

    float xDirection = direction.get(Math::Vec3::X);
    float yDirection = direction.get(Math::Vec3::Y);
    AnimationState puzzleState = AnimationState::IDLE;

    if (fabsf(xDirection) > fabsf(yDirection)) {
        puzzleState = (xDirection > 0) ? AnimationState::RIGHT_ROTATION : AnimationState::LEFT_ROTATION;
    } else {
        puzzleState = (yDirection > 0) ? AnimationState::DOWN_ROTATION : AnimationState::UP_ROTATION;
    }

    this->puzzle->selectCube(objectId);
    this->puzzle->setAnimationState(puzzleState);
}

}  // namespace Rubik
<|endoftext|>


<commit_before>#ifndef SEARCH_HH
#define SEARCH_HH

#include <vector>
#include <deque>

#include <float.h>

#include "Ngram.hh"
#include "Expander.hh"
#include "Vocabulary.hh"

class HypoPath {
public:
  inline HypoPath(int word_id, int frame, HypoPath *prev);
  inline ~HypoPath();
  inline void link() { m_reference_count++; }
  inline static void unlink(HypoPath *path);
  int word_id;
  int frame;
  HypoPath *prev;
  static int count;
private:
  int m_reference_count;
};

HypoPath::HypoPath(int word_id, int frame, HypoPath *prev)
  : word_id(word_id), frame(frame), prev(prev), m_reference_count(0)
{
  if (prev)
    prev->link();
  count++;
}

HypoPath::~HypoPath()
{
  count--;
}

void
HypoPath::unlink(HypoPath *path)
{
  while (path->m_reference_count == 1) {
    HypoPath *prev = path->prev;
    delete path;
    path = prev;
    if (!path)
      return;
  }
  path->m_reference_count--;
}

class Hypo {
public:
  inline Hypo();
  inline Hypo(int frame, double log_prob, HypoPath *path);
  inline Hypo(const Hypo &t);
  inline Hypo &operator=(const Hypo &hypo);
  inline ~Hypo();
  inline void add_path(int word_id, int frame);
  bool operator<(const Hypo &h) const { return log_prob > h.log_prob; }

  int frame;
  double log_prob;
  HypoPath *path;
};

Hypo::Hypo()
  : frame(0), log_prob(0), path(NULL)
{
}

Hypo::Hypo(int frame, double log_prob, HypoPath *path)
 : frame(frame), log_prob(log_prob), path(path)
{
  if (path)
    path->link();
}

Hypo::Hypo(const Hypo &h)
  : frame(h.frame),
    log_prob(h.log_prob),
    path(h.path)
{
  if (path)
    path->link();
}

Hypo&
Hypo::operator=(const Hypo &h)
{
  if (this == &h)
    return *this;

  HypoPath *old_path = path;

  frame = h.frame;
  log_prob = h.log_prob;
  path = h.path;

  if (path)
    path->link();

  if (old_path)
    HypoPath::unlink(old_path);

  return *this;
}

Hypo::~Hypo()
{
  if (path)
    HypoPath::unlink(path);
}

void
Hypo::add_path(int word_id, int frame)
{
  HypoPath *old_path = path;
  path = new HypoPath(word_id, frame, path);
  path->link();
  if (old_path)
    HypoPath::unlink(old_path);
}

class HypoStack : private std::vector<Hypo> {
public:
  inline HypoStack() : m_best_log_prob(-1e10) { }

  // Inherited from vector
  inline Hypo &operator[](int index)
    { return std::vector<Hypo>::operator[](index); }
  inline Hypo &at(int index) { return std::vector<Hypo>::operator[](index); }
  inline size_type size() const { return std::vector<Hypo>::size(); }
  inline void reserve(int size) { std::vector<Hypo>::reserve(size); }
  inline bool empty() const { return std::vector<Hypo>::empty(); }

  // New functions
  inline void add(const Hypo &hypo);
  inline void partial_sort(int top);
  inline void sort();
  inline void prune(int top); // does not sort if not necessary
  inline void clear();
  void prune_similar(int length);

  // Status
  inline void reset_best() { m_best_log_prob = -1e10; m_best_index = -1; }
  inline double best_log_prob() const { return m_best_log_prob; }
  inline int best_index() const { return m_best_index; }

private:
  double m_best_log_prob;
  int m_best_index;
};

void
HypoStack::add(const Hypo &hypo)
{
  if (hypo.log_prob > m_best_log_prob) {
    m_best_log_prob = hypo.log_prob;
    m_best_index = size();
  }
  std::vector<Hypo>::push_back(hypo);
}

void
HypoStack::partial_sort(int top)
{
<<<<<<< Search.hh
//    if (top < m_num_sorted)
//      return;

=======
>>>>>>> 1.14
  if (top == 0 || top >= size()) {
    top = size();
    sort();
  }
  else {
    std::partial_sort(begin(), begin() + top, end());
    m_best_index = 0;
  }
}

void
HypoStack::sort()
{
  std::sort(begin(), end());
  m_best_index = 0;
}

// Assumes sort
void
HypoStack::prune(int top)
{
  if (top == 0)
    clear();
  else if (top < size())
    resize(top);
}

void
HypoStack::clear()
{
  std::vector<Hypo>::clear();
  reset_best();
}

class Search {
public:

  Search(Expander &expander, const Vocabulary &vocabulary,
	 const Ngram &ngram);

  // Debug
  void debug_print_hypo(Hypo &hypo);
  void debug_print_history(Hypo &hypo);

  // Operate
  void init_search(int expand_window, int stacks, int reserved_hypos);
  void sort_stack(int frame, int top = 0);
  bool expand(int frame);
  void move_buffer(int frame);
  void go(int frame);
  bool run();
  void prune_similar(int frame, int length);

  // Info
  inline int frame() const { return m_frame; }
  inline int first_frame() const { return m_first_frame; }
  inline int last_frame() const { return m_last_frame; }
  int frame2stack(int frame) const;
  inline HypoStack &stack(int frame) { return m_stacks[frame2stack(frame)]; }
  inline const HypoStack &stack(int frame) const
    { return m_stacks[frame2stack(frame)]; }

  // Options
  void set_hypo_limit(int hypo_limit) { m_hypo_limit = hypo_limit; }
  void set_word_limit(int word_limit) { m_word_limit = word_limit; }
  void set_word_beam(double word_beam) { m_word_beam = word_beam; }
  void set_lm_scale(double lm_scale) { m_lm_scale = lm_scale; }
  void set_lm_offset(double lm_offset) { m_lm_offset = lm_offset; }
  void set_prune_similar(int prune_similar) { m_prune_similar = prune_similar; }
  void set_beam(double beam) { m_beam = beam; }
  void set_global_beam(double beam) { m_global_beam = beam; }
  void set_verbose(bool verbose) { m_verbose = verbose; }

  // Exceptions
  struct ForgottenFrame : public std::exception {
    virtual const char *what() const throw()
      { return "Search: forgotten frame"; }
  };

  struct FutureFrame : public std::exception {
    virtual const char *what() const throw()
      { return "Search: future frame"; }
  };

private:
  void circulate(int &stack);

  Expander &m_expander;
  const Vocabulary &m_vocabulary;
  const Ngram &m_ngram;

  // State
  int m_frame;

  // Stacks
  int m_first_frame;
  int m_last_frame;
  int m_first_stack;
  int m_last_stack;
  std::vector<HypoStack> m_stacks;

  // options
  int m_expand_window;
  double m_lm_scale;
  double m_lm_offset;
  bool m_verbose;

  // Pruning options
  int m_word_limit;	// How many best words are expanded
  double m_word_beam;   // Do not expand words outside this beam
  int m_prune_similar;  // Prune similar N-word endings
  int m_hypo_limit;	// How many best hypos in a stack are expanded
  double m_beam;

  // Global pruning
  double m_global_best;
  double m_global_beam;
  int m_global_frame;

  // Temporary variables
  std::deque<int> m_history;
};

#endif /* SEARCH_HH */
<commit_msg>*** empty log message ***<commit_after>#ifndef SEARCH_HH
#define SEARCH_HH

#include <vector>
#include <deque>

#include <float.h>

#include "Ngram.hh"
#include "Expander.hh"
#include "Vocabulary.hh"

class HypoPath {
public:
  inline HypoPath(int word_id, int frame, HypoPath *prev);
  inline ~HypoPath();
  inline void link() { m_reference_count++; }
  inline static void unlink(HypoPath *path);
  int word_id;
  int frame;
  HypoPath *prev;
  static int count;
private:
  int m_reference_count;
};

HypoPath::HypoPath(int word_id, int frame, HypoPath *prev)
  : word_id(word_id), frame(frame), prev(prev), m_reference_count(0)
{
  if (prev)
    prev->link();
  count++;
}

HypoPath::~HypoPath()
{
  count--;
}

void
HypoPath::unlink(HypoPath *path)
{
  while (path->m_reference_count == 1) {
    HypoPath *prev = path->prev;
    delete path;
    path = prev;
    if (!path)
      return;
  }
  path->m_reference_count--;
}

class Hypo {
public:
  inline Hypo();
  inline Hypo(int frame, double log_prob, HypoPath *path);
  inline Hypo(const Hypo &t);
  inline Hypo &operator=(const Hypo &hypo);
  inline ~Hypo();
  inline void add_path(int word_id, int frame);
  bool operator<(const Hypo &h) const { return log_prob > h.log_prob; }

  int frame;
  double log_prob;
  HypoPath *path;
};

Hypo::Hypo()
  : frame(0), log_prob(0), path(NULL)
{
}

Hypo::Hypo(int frame, double log_prob, HypoPath *path)
 : frame(frame), log_prob(log_prob), path(path)
{
  if (path)
    path->link();
}

Hypo::Hypo(const Hypo &h)
  : frame(h.frame),
    log_prob(h.log_prob),
    path(h.path)
{
  if (path)
    path->link();
}

Hypo&
Hypo::operator=(const Hypo &h)
{
  if (this == &h)
    return *this;

  HypoPath *old_path = path;

  frame = h.frame;
  log_prob = h.log_prob;
  path = h.path;

  if (path)
    path->link();

  if (old_path)
    HypoPath::unlink(old_path);

  return *this;
}

Hypo::~Hypo()
{
  if (path)
    HypoPath::unlink(path);
}

void
Hypo::add_path(int word_id, int frame)
{
  HypoPath *old_path = path;
  path = new HypoPath(word_id, frame, path);
  path->link();
  if (old_path)
    HypoPath::unlink(old_path);
}

class HypoStack : private std::vector<Hypo> {
public:
  inline HypoStack() : m_best_log_prob(-1e10) { }

  // Inherited from vector
  inline Hypo &operator[](int index)
    { return std::vector<Hypo>::operator[](index); }
  inline Hypo &at(int index) { return std::vector<Hypo>::operator[](index); }
  inline size_type size() const { return std::vector<Hypo>::size(); }
  inline void reserve(int size) { std::vector<Hypo>::reserve(size); }
  inline bool empty() const { return std::vector<Hypo>::empty(); }

  // New functions
  inline void add(const Hypo &hypo);
  inline void partial_sort(int top);
  inline void sort();
  inline void prune(int top); // does not sort if not necessary
  inline void clear();
  void prune_similar(int length);

  // Status
  inline void reset_best() { m_best_log_prob = -1e10; m_best_index = -1; }
  inline double best_log_prob() const { return m_best_log_prob; }
  inline int best_index() const { return m_best_index; }

private:
  double m_best_log_prob;
  int m_best_index;
};

void
HypoStack::add(const Hypo &hypo)
{
  if (hypo.log_prob > m_best_log_prob) {
    m_best_log_prob = hypo.log_prob;
    m_best_index = size();
  }
  std::vector<Hypo>::push_back(hypo);
}

void
HypoStack::partial_sort(int top)
{
  if (top == 0 || top >= size()) {
    top = size();
    sort();
  }
  else {
    std::partial_sort(begin(), begin() + top, end());
    m_best_index = 0;
  }
}

void
HypoStack::sort()
{
  std::sort(begin(), end());
  m_best_index = 0;
}

// Assumes sort
void
HypoStack::prune(int top)
{
  if (top == 0)
    clear();
  else if (top < size())
    resize(top);
}

void
HypoStack::clear()
{
  std::vector<Hypo>::clear();
  reset_best();
}

class Search {
public:

  Search(Expander &expander, const Vocabulary &vocabulary,
	 const Ngram &ngram);

  // Debug
  void debug_print_hypo(Hypo &hypo);
  void debug_print_history(Hypo &hypo);

  // Operate
  void init_search(int expand_window, int stacks, int reserved_hypos);
  void sort_stack(int frame, int top = 0);
  bool expand(int frame);
  void move_buffer(int frame);
  void go(int frame);
  bool run();
  void prune_similar(int frame, int length);

  // Info
  inline int frame() const { return m_frame; }
  inline int first_frame() const { return m_first_frame; }
  inline int last_frame() const { return m_last_frame; }
  int frame2stack(int frame) const;
  inline HypoStack &stack(int frame) { return m_stacks[frame2stack(frame)]; }
  inline const HypoStack &stack(int frame) const
    { return m_stacks[frame2stack(frame)]; }

  // Options
  void set_hypo_limit(int hypo_limit) { m_hypo_limit = hypo_limit; }
  void set_word_limit(int word_limit) { m_word_limit = word_limit; }
  void set_word_beam(double word_beam) { m_word_beam = word_beam; }
  void set_lm_scale(double lm_scale) { m_lm_scale = lm_scale; }
  void set_lm_offset(double lm_offset) { m_lm_offset = lm_offset; }
  void set_prune_similar(int prune_similar) { m_prune_similar = prune_similar; }
  void set_beam(double beam) { m_beam = beam; }
  void set_global_beam(double beam) { m_global_beam = beam; }
  void set_verbose(bool verbose) { m_verbose = verbose; }

  // Exceptions
  struct ForgottenFrame : public std::exception {
    virtual const char *what() const throw()
      { return "Search: forgotten frame"; }
  };

  struct FutureFrame : public std::exception {
    virtual const char *what() const throw()
      { return "Search: future frame"; }
  };

private:
  void circulate(int &stack);

  Expander &m_expander;
  const Vocabulary &m_vocabulary;
  const Ngram &m_ngram;

  // State
  int m_frame;

  // Stacks
  int m_first_frame;
  int m_last_frame;
  int m_first_stack;
  int m_last_stack;
  std::vector<HypoStack> m_stacks;

  // options
  int m_expand_window;
  double m_lm_scale;
  double m_lm_offset;
  bool m_verbose;

  // Pruning options
  int m_word_limit;	// How many best words are expanded
  double m_word_beam;   // Do not expand words outside this beam
  int m_prune_similar;  // Prune similar N-word endings
  int m_hypo_limit;	// How many best hypos in a stack are expanded
  double m_beam;

  // Global pruning
  double m_global_best;
  double m_global_beam;
  int m_global_frame;

  // Temporary variables
  std::deque<int> m_history;
};

#endif /* SEARCH_HH */
<|endoftext|>


<commit_before>/*
 * AscEmu Framework based on ArcEmu MMORPG Server
 * Copyright (C) 2014-2016 AscEmu Team <http://www.ascemu.org/>
 * Copyright (C) 2008-2012 ArcEmu Team <http://www.ArcEmu.org/>
 * Copyright (C) 2005-2007 Ascent Team
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "StdAfx.h"

//////////////////////////////////////////////////////////////////////////////////////////
/// This function handles CMSG_NAME_QUERY:
//////////////////////////////////////////////////////////////////////////////////////////
void WorldSession::HandleNameQueryOpcode(WorldPacket& recv_data)
{
    CHECK_PACKET_SIZE(recv_data, 8);
    uint64 guid;
    recv_data >> guid;

    PlayerInfo* pn = objmgr.GetPlayerInfo((uint32)guid);

    if (!pn)
        return;

    LOG_DEBUG("Received CMSG_NAME_QUERY for: %s", pn->name);

    WoWGuid pguid((uint64)pn->guid); //VLack: The usual new style guid handling on 3.1.2
    WorldPacket data(SMSG_NAME_QUERY_RESPONSE, strlen(pn->name) + 35);
    //    data << pn->guid << uint32(0);    //highguid
    data << pguid;
    data << uint8(0); //VLack: usual, new-style guid with an uint8
    data << pn->name;
    data << uint8(0);       // this is a string showed besides players name (eg. in combat log), a custom title ?
    data << uint8(pn->race);
    data << uint8(pn->gender);
    data << uint8(pn->cl);
    //    data << uint8(0);            // 2.4.0, why do i get the feeling blizz is adding custom classes or custom titles? (same thing in who list)
    data << uint8(0); //VLack: tell the server this name is not declined... (3.1 fix?)
    SendPacket(&data);
}

//////////////////////////////////////////////////////////////////////////////////////////
/// This function handles CMSG_QUERY_TIME:
//////////////////////////////////////////////////////////////////////////////////////////
void WorldSession::HandleQueryTimeOpcode(WorldPacket& recv_data)
{

    WorldPacket data(SMSG_QUERY_TIME_RESPONSE, 4 + 4);
    data << (uint32)UNIXTIME;
    data << (uint32)0; //VLack: 3.1; thanks Stewart for reminding me to have the correct structure even if it seems the old one still works.
    SendPacket(&data);

}

//////////////////////////////////////////////////////////////////////////////////////////
/// This function handles CMSG_CREATURE_QUERY:
//////////////////////////////////////////////////////////////////////////////////////////
void WorldSession::HandleCreatureQueryOpcode(WorldPacket& recv_data)
{
    CHECK_INWORLD_RETURN

    CHECK_PACKET_SIZE(recv_data, 12);

    WorldPacket data(SMSG_CREATURE_QUERY_RESPONSE, 250); //VLack: thanks Aspire, this was 146 before
    uint32 entry;
    uint64 guid;
    CreatureInfo const* ci;

    recv_data >> entry;
    recv_data >> guid;

    if (entry == 300000)
    {
        data << entry;
        data << "WayPoint";
        data << uint8(0);
        data << uint8(0);
        data << uint8(0);
        data << "Level is WayPoint ID";
        for (uint8 i = 0; i < 8; i++)
        {
            data << uint32(0);
        }
        data << uint8(0);
    }
    else
    {
        ci = sMySQLStore.GetCreatureInfo(entry);
        if (ci == NULL)
            return;

        LocalizedCreatureName* lcn = (language > 0) ? sLocalizationMgr.GetLocalizedCreatureName(entry, language) : NULL;

        if (lcn == NULL)
        {
            LOG_DETAIL("WORLD: CMSG_CREATURE_QUERY '%s'", ci->Name.c_str());
            data << entry;
            data << ci->Name;       // name of the creature
            data << uint8(0);       // name2, always seems to be empty
            data << uint8(0);       // name3, always seems to be empty
            data << uint8(0);       // name4, always seems to be empty
            data << ci->SubName;    // this is the title/guild of the creature
        }
        else
        {
            LOG_DETAIL("WORLD: CMSG_CREATURE_QUERY '%s' (localized to %s)", ci->Name, lcn->Name);
            data << entry;
            data << lcn->Name;
            data << uint8(0);
            data << uint8(0);
            data << uint8(0);
            data << lcn->SubName;
        }
        data << ci->info_str;       // this is a string in 2.3.0 Example: stormwind guard has : "Direction"
        data << ci->Flags1;         // flags like skinnable
        data << ci->Type;           // humanoid, beast, etc
        data << ci->Family;         // petfamily
        data << ci->Rank;           // normal, elite, etc
        data << ci->killcredit[0];  // quest kill credit 1
        data << ci->killcredit[1];  // quest kill credit 2
        data << ci->Male_DisplayID;
        data << ci->Female_DisplayID;
        data << ci->Male_DisplayID2;
        data << ci->Female_DisplayID2;
        data << ci->unkfloat1;
        data << ci->unkfloat2;
        data << ci->Leader;         // faction leader

        // these are the 6 seperate quest items a creature can drop
        for (uint8 i = 0; i < 6; ++i)
        {
            data << uint32(ci->QuestItems[i]);
        }
        data << ci->waypointid;
    }

    SendPacket(&data);
}

//////////////////////////////////////////////////////////////////////////////////////////
/// This function handles CMSG_GAMEOBJECT_QUERY:
//////////////////////////////////////////////////////////////////////////////////////////
void WorldSession::HandleGameObjectQueryOpcode(WorldPacket& recv_data)
{
    CHECK_INWORLD_RETURN

    CHECK_PACKET_SIZE(recv_data, 12);
    WorldPacket data(SMSG_GAMEOBJECT_QUERY_RESPONSE, 900);

    uint32 entryID;
    uint64 guid;

    recv_data >> entryID;
    recv_data >> guid;

    LOG_DETAIL("WORLD: CMSG_GAMEOBJECT_QUERY '%u'", entryID);

    auto gameobject_info = GameObjectNameStorage.LookupEntry(entryID);
    if (gameobject_info == nullptr)
        return;

    LocalizedGameObjectName* lgn = (language > 0) ? sLocalizationMgr.GetLocalizedGameObjectName(entryID, language) : NULL;

    data << entryID;                // unique identifier of the GO template
    data << gameobject_info->type;           // type of the gameobject
    data << gameobject_info->display_id;      // displayid/modelid of the gameobject

    // Name of the gameobject
    if (lgn)
        data << lgn->Name;
    else
        data << gameobject_info->name;

    data << uint8(0);               // name2, always seems to be empty
    data << uint8(0);               // name3, always seems to be empty
    data << uint8(0);               // name4, always seems to be empty
    data << gameobject_info->category_name;  // Category string of the GO, like "attack", "pvp", "point", etc
    data << gameobject_info->cast_bar_text;  // text displayed when using the go, like "collecting", "summoning" etc
    data << gameobject_info->Unkstr;
    data << gameobject_info->raw.parameter_0;     // spellfocus id, ex.: spell casted when interacting with the GO
    data << gameobject_info->raw.parameter_1;
    data << gameobject_info->raw.parameter_2;
    data << gameobject_info->raw.parameter_3;
    data << gameobject_info->raw.parameter_4;
    data << gameobject_info->raw.parameter_5;
    data << gameobject_info->raw.parameter_6;
    data << gameobject_info->raw.parameter_7;
    data << gameobject_info->raw.parameter_8;
    data << gameobject_info->raw.parameter_9;
    data << gameobject_info->raw.parameter_10;
    data << gameobject_info->raw.parameter_11;
    data << gameobject_info->raw.parameter_12;
    data << gameobject_info->raw.parameter_13;
    data << gameobject_info->raw.parameter_14;
    data << gameobject_info->raw.parameter_15;
    data << gameobject_info->raw.parameter_16;
    data << gameobject_info->raw.parameter_17;
    data << gameobject_info->raw.parameter_18;
    data << gameobject_info->raw.parameter_19;
    data << gameobject_info->raw.parameter_20;
    data << gameobject_info->raw.parameter_21;
    data << gameobject_info->raw.parameter_22;
    data << gameobject_info->raw.parameter_23;
    data << float(gameobject_info->size);       // scaling of the GO

    // questitems that the go can contain
    for (uint8 i = 0; i < 6; ++i)
    {
        data << uint32(gameobject_info->QuestItems[i]);
    }

    SendPacket(&data);
}

//////////////////////////////////////////////////////////////////////////////////////////
/// This function handles MSG_CORPSE_QUERY:
//////////////////////////////////////////////////////////////////////////////////////////
void WorldSession::HandleCorpseQueryOpcode(WorldPacket& recv_data)
{
    CHECK_INWORLD_RETURN

    LOG_DETAIL("WORLD: Received MSG_CORPSE_QUERY");

    Corpse* pCorpse;
    WorldPacket data(MSG_CORPSE_QUERY, 25);
    MapInfo* pMapinfo;

    pCorpse = objmgr.GetCorpseByOwner(GetPlayer()->GetLowGUID());
    if (pCorpse)
    {
        pMapinfo = WorldMapInfoStorage.LookupEntry(pCorpse->GetMapId());
        if (pMapinfo)
        {
            if (pMapinfo->type == INSTANCE_NULL || pMapinfo->type == INSTANCE_BATTLEGROUND)
            {
                data << uint8(0x01);            //show ?
                data << pCorpse->GetMapId();    // mapid (that tombstones shown on)
                data << pCorpse->GetPositionX();
                data << pCorpse->GetPositionY();
                data << pCorpse->GetPositionZ();
                data << pCorpse->GetMapId();    //instance mapid (needs to be same as mapid to be able to recover corpse)
                data << uint32(0);
                SendPacket(&data);
            }
            else
            {
                data << uint8(0x01);            //show ?
                data << pMapinfo->repopmapid;   // mapid (that tombstones shown on)
                data << pMapinfo->repopx;
                data << pMapinfo->repopy;
                data << pMapinfo->repopz;
                data << pCorpse->GetMapId();    //instance mapid (needs to be same as mapid to be able to recover corpse)
                data << uint32(0);
                SendPacket(&data);
            }
        }
        else
        {
            data << uint8(0x01);                //show ?
            data << pCorpse->GetMapId();        // mapid (that tombstones shown on)
            data << pCorpse->GetPositionX();
            data << pCorpse->GetPositionY();
            data << pCorpse->GetPositionZ();
            data << pCorpse->GetMapId();        //instance mapid (needs to be same as mapid to be able to recover corpse)
            data << uint32(0);
            SendPacket(&data);
        }
    }
}

void WorldSession::HandlePageTextQueryOpcode(WorldPacket& recv_data)
{
    CHECK_INWORLD_RETURN

    CHECK_PACKET_SIZE(recv_data, 4);
    uint32 pageid = 0;
    recv_data >> pageid;

    while (pageid)
    {
        ItemPage* page = ItemPageStorage.LookupEntry(pageid);
        if (!page)
            return;

        LocalizedItemPage* lpi = (language > 0) ? sLocalizationMgr.GetLocalizedItemPage(pageid, language) : NULL;
        WorldPacket data(SMSG_PAGE_TEXT_QUERY_RESPONSE, 1000);
        data << pageid;
        if (lpi)
            data << lpi->Text;
        else
            data << page->text;

        data << page->next_page;
        pageid = page->next_page;
        SendPacket(&data);
    }
}

//////////////////////////////////////////////////////////////////////////////////////////
/// This function handles CMSG_ITEM_NAME_QUERY:
//////////////////////////////////////////////////////////////////////////////////////////
void WorldSession::HandleItemNameQueryOpcode(WorldPacket& recv_data)
{
    CHECK_INWORLD_RETURN
    CHECK_PACKET_SIZE(recv_data, 4);

    uint32 itemid;

    recv_data >> itemid;
    recv_data.read_skip<uint64>();

    WorldPacket reply(SMSG_ITEM_NAME_QUERY_RESPONSE, 100);
    reply << itemid;

    std::string Name = ("Unknown Item");

    ItemPrototype const* proto = sMySQLStore.GetItemProto(itemid);
    if (proto != nullptr)
    {
        LocalizedItem* li = (language > 0) ? sLocalizationMgr.GetLocalizedItem(itemid, language) : NULL;
        if (li)
            Name = li->Name;
        else
            Name = proto->Name;

        reply << Name;
        reply << proto->InventoryType;
    }
    else
    {
        reply << Name;
    }

    SendPacket(&reply);
}

void WorldSession::HandleInrangeQuestgiverQuery(WorldPacket& recv_data)
{
    CHECK_INWORLD_RETURN;

    WorldPacket data(SMSG_QUESTGIVER_STATUS_MULTIPLE, 1000);
    Object::InRangeSet::iterator itr;
    Creature* pCreature;
    uint32 count = 0;
    data << count;

    // 32 count
    // <foreach count>
    //    64 guid
    //    8 status

    for (itr = _player->m_objectsInRange.begin(); itr != _player->m_objectsInRange.end(); ++itr)
    {
        if (!(*itr)->IsCreature())
            continue;

        pCreature = static_cast<Creature*>(*itr);

        if (pCreature->isQuestGiver())
        {
            data << pCreature->GetGUID();
            data << uint8(sQuestMgr.CalcStatus(pCreature, _player));
            ++count;
        }
    }

    *(uint32*)(data.contents()) = count;
    SendPacket(&data);
}

void WorldSession::HandleAchievmentQueryOpcode(WorldPacket& recv_data)
{
    CHECK_INWORLD_RETURN;

    uint64 guid = recv_data.unpackGUID();               // Get the inspectee's GUID
    Player* pTarget = objmgr.GetPlayer((uint32)guid);
    if (!pTarget)
    {
        return;
    }
#ifdef ENABLE_ACHIEVEMENTS
    pTarget->GetAchievementMgr().SendAllAchievementData(GetPlayer());
#endif
}
<commit_msg>Travis: Again attempt to solve (last) gcc error<commit_after>/*
 * AscEmu Framework based on ArcEmu MMORPG Server
 * Copyright (C) 2014-2016 AscEmu Team <http://www.ascemu.org/>
 * Copyright (C) 2008-2012 ArcEmu Team <http://www.ArcEmu.org/>
 * Copyright (C) 2005-2007 Ascent Team
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "StdAfx.h"

//////////////////////////////////////////////////////////////////////////////////////////
/// This function handles CMSG_NAME_QUERY:
//////////////////////////////////////////////////////////////////////////////////////////
void WorldSession::HandleNameQueryOpcode(WorldPacket& recv_data)
{
    CHECK_PACKET_SIZE(recv_data, 8);
    uint64 guid;
    recv_data >> guid;

    PlayerInfo* pn = objmgr.GetPlayerInfo((uint32)guid);

    if (!pn)
        return;

    LOG_DEBUG("Received CMSG_NAME_QUERY for: %s", pn->name);

    WoWGuid pguid((uint64)pn->guid); //VLack: The usual new style guid handling on 3.1.2
    WorldPacket data(SMSG_NAME_QUERY_RESPONSE, strlen(pn->name) + 35);
    //    data << pn->guid << uint32(0);    //highguid
    data << pguid;
    data << uint8(0); //VLack: usual, new-style guid with an uint8
    data << pn->name;
    data << uint8(0);       // this is a string showed besides players name (eg. in combat log), a custom title ?
    data << uint8(pn->race);
    data << uint8(pn->gender);
    data << uint8(pn->cl);
    //    data << uint8(0);            // 2.4.0, why do i get the feeling blizz is adding custom classes or custom titles? (same thing in who list)
    data << uint8(0); //VLack: tell the server this name is not declined... (3.1 fix?)
    SendPacket(&data);
}

//////////////////////////////////////////////////////////////////////////////////////////
/// This function handles CMSG_QUERY_TIME:
//////////////////////////////////////////////////////////////////////////////////////////
void WorldSession::HandleQueryTimeOpcode(WorldPacket& recv_data)
{

    WorldPacket data(SMSG_QUERY_TIME_RESPONSE, 4 + 4);
    data << (uint32)UNIXTIME;
    data << (uint32)0; //VLack: 3.1; thanks Stewart for reminding me to have the correct structure even if it seems the old one still works.
    SendPacket(&data);

}

//////////////////////////////////////////////////////////////////////////////////////////
/// This function handles CMSG_CREATURE_QUERY:
//////////////////////////////////////////////////////////////////////////////////////////
void WorldSession::HandleCreatureQueryOpcode(WorldPacket& recv_data)
{
    CHECK_INWORLD_RETURN

    CHECK_PACKET_SIZE(recv_data, 12);

    WorldPacket data(SMSG_CREATURE_QUERY_RESPONSE, 250); //VLack: thanks Aspire, this was 146 before
    uint32 entry;
    uint64 guid;
    CreatureInfo const* ci;

    recv_data >> entry;
    recv_data >> guid;

    if (entry == 300000)
    {
        data << entry;
        data << "WayPoint";
        data << uint8(0);
        data << uint8(0);
        data << uint8(0);
        data << "Level is WayPoint ID";
        for (uint8 i = 0; i < 8; i++)
        {
            data << uint32(0);
        }
        data << uint8(0);
    }
    else
    {
        ci = sMySQLStore.GetCreatureInfo(entry);
        if (ci == NULL)
            return;

        LocalizedCreatureName* lcn = (language > 0) ? sLocalizationMgr.GetLocalizedCreatureName(entry, language) : NULL;

        if (lcn == NULL)
        {
            LOG_DETAIL("WORLD: CMSG_CREATURE_QUERY '%s'", ci->Name.c_str());
            data << entry;
            data << ci->Name;       // name of the creature
            data << uint8(0);       // name2, always seems to be empty
            data << uint8(0);       // name3, always seems to be empty
            data << uint8(0);       // name4, always seems to be empty
            data << ci->SubName;    // this is the title/guild of the creature
        }
        else
        {
            LOG_DETAIL("WORLD: CMSG_CREATURE_QUERY '%s' (localized to %s)", ci->Name.c_str(), lcn->Name);
            data << entry;
            data << lcn->Name;
            data << uint8(0);
            data << uint8(0);
            data << uint8(0);
            data << lcn->SubName;
        }
        data << ci->info_str;       // this is a string in 2.3.0 Example: stormwind guard has : "Direction"
        data << ci->Flags1;         // flags like skinnable
        data << ci->Type;           // humanoid, beast, etc
        data << ci->Family;         // petfamily
        data << ci->Rank;           // normal, elite, etc
        data << ci->killcredit[0];  // quest kill credit 1
        data << ci->killcredit[1];  // quest kill credit 2
        data << ci->Male_DisplayID;
        data << ci->Female_DisplayID;
        data << ci->Male_DisplayID2;
        data << ci->Female_DisplayID2;
        data << ci->unkfloat1;
        data << ci->unkfloat2;
        data << ci->Leader;         // faction leader

        // these are the 6 seperate quest items a creature can drop
        for (uint8 i = 0; i < 6; ++i)
        {
            data << uint32(ci->QuestItems[i]);
        }
        data << ci->waypointid;
    }

    SendPacket(&data);
}

//////////////////////////////////////////////////////////////////////////////////////////
/// This function handles CMSG_GAMEOBJECT_QUERY:
//////////////////////////////////////////////////////////////////////////////////////////
void WorldSession::HandleGameObjectQueryOpcode(WorldPacket& recv_data)
{
    CHECK_INWORLD_RETURN

    CHECK_PACKET_SIZE(recv_data, 12);
    WorldPacket data(SMSG_GAMEOBJECT_QUERY_RESPONSE, 900);

    uint32 entryID;
    uint64 guid;

    recv_data >> entryID;
    recv_data >> guid;

    LOG_DETAIL("WORLD: CMSG_GAMEOBJECT_QUERY '%u'", entryID);

    auto gameobject_info = GameObjectNameStorage.LookupEntry(entryID);
    if (gameobject_info == nullptr)
        return;

    LocalizedGameObjectName* lgn = (language > 0) ? sLocalizationMgr.GetLocalizedGameObjectName(entryID, language) : NULL;

    data << entryID;                // unique identifier of the GO template
    data << gameobject_info->type;           // type of the gameobject
    data << gameobject_info->display_id;      // displayid/modelid of the gameobject

    // Name of the gameobject
    if (lgn)
        data << lgn->Name;
    else
        data << gameobject_info->name;

    data << uint8(0);               // name2, always seems to be empty
    data << uint8(0);               // name3, always seems to be empty
    data << uint8(0);               // name4, always seems to be empty
    data << gameobject_info->category_name;  // Category string of the GO, like "attack", "pvp", "point", etc
    data << gameobject_info->cast_bar_text;  // text displayed when using the go, like "collecting", "summoning" etc
    data << gameobject_info->Unkstr;
    data << gameobject_info->raw.parameter_0;     // spellfocus id, ex.: spell casted when interacting with the GO
    data << gameobject_info->raw.parameter_1;
    data << gameobject_info->raw.parameter_2;
    data << gameobject_info->raw.parameter_3;
    data << gameobject_info->raw.parameter_4;
    data << gameobject_info->raw.parameter_5;
    data << gameobject_info->raw.parameter_6;
    data << gameobject_info->raw.parameter_7;
    data << gameobject_info->raw.parameter_8;
    data << gameobject_info->raw.parameter_9;
    data << gameobject_info->raw.parameter_10;
    data << gameobject_info->raw.parameter_11;
    data << gameobject_info->raw.parameter_12;
    data << gameobject_info->raw.parameter_13;
    data << gameobject_info->raw.parameter_14;
    data << gameobject_info->raw.parameter_15;
    data << gameobject_info->raw.parameter_16;
    data << gameobject_info->raw.parameter_17;
    data << gameobject_info->raw.parameter_18;
    data << gameobject_info->raw.parameter_19;
    data << gameobject_info->raw.parameter_20;
    data << gameobject_info->raw.parameter_21;
    data << gameobject_info->raw.parameter_22;
    data << gameobject_info->raw.parameter_23;
    data << float(gameobject_info->size);       // scaling of the GO

    // questitems that the go can contain
    for (uint8 i = 0; i < 6; ++i)
    {
        data << uint32(gameobject_info->QuestItems[i]);
    }

    SendPacket(&data);
}

//////////////////////////////////////////////////////////////////////////////////////////
/// This function handles MSG_CORPSE_QUERY:
//////////////////////////////////////////////////////////////////////////////////////////
void WorldSession::HandleCorpseQueryOpcode(WorldPacket& recv_data)
{
    CHECK_INWORLD_RETURN

    LOG_DETAIL("WORLD: Received MSG_CORPSE_QUERY");

    Corpse* pCorpse;
    WorldPacket data(MSG_CORPSE_QUERY, 25);
    MapInfo* pMapinfo;

    pCorpse = objmgr.GetCorpseByOwner(GetPlayer()->GetLowGUID());
    if (pCorpse)
    {
        pMapinfo = WorldMapInfoStorage.LookupEntry(pCorpse->GetMapId());
        if (pMapinfo)
        {
            if (pMapinfo->type == INSTANCE_NULL || pMapinfo->type == INSTANCE_BATTLEGROUND)
            {
                data << uint8(0x01);            //show ?
                data << pCorpse->GetMapId();    // mapid (that tombstones shown on)
                data << pCorpse->GetPositionX();
                data << pCorpse->GetPositionY();
                data << pCorpse->GetPositionZ();
                data << pCorpse->GetMapId();    //instance mapid (needs to be same as mapid to be able to recover corpse)
                data << uint32(0);
                SendPacket(&data);
            }
            else
            {
                data << uint8(0x01);            //show ?
                data << pMapinfo->repopmapid;   // mapid (that tombstones shown on)
                data << pMapinfo->repopx;
                data << pMapinfo->repopy;
                data << pMapinfo->repopz;
                data << pCorpse->GetMapId();    //instance mapid (needs to be same as mapid to be able to recover corpse)
                data << uint32(0);
                SendPacket(&data);
            }
        }
        else
        {
            data << uint8(0x01);                //show ?
            data << pCorpse->GetMapId();        // mapid (that tombstones shown on)
            data << pCorpse->GetPositionX();
            data << pCorpse->GetPositionY();
            data << pCorpse->GetPositionZ();
            data << pCorpse->GetMapId();        //instance mapid (needs to be same as mapid to be able to recover corpse)
            data << uint32(0);
            SendPacket(&data);
        }
    }
}

void WorldSession::HandlePageTextQueryOpcode(WorldPacket& recv_data)
{
    CHECK_INWORLD_RETURN

    CHECK_PACKET_SIZE(recv_data, 4);
    uint32 pageid = 0;
    recv_data >> pageid;

    while (pageid)
    {
        ItemPage* page = ItemPageStorage.LookupEntry(pageid);
        if (!page)
            return;

        LocalizedItemPage* lpi = (language > 0) ? sLocalizationMgr.GetLocalizedItemPage(pageid, language) : NULL;
        WorldPacket data(SMSG_PAGE_TEXT_QUERY_RESPONSE, 1000);
        data << pageid;
        if (lpi)
            data << lpi->Text;
        else
            data << page->text;

        data << page->next_page;
        pageid = page->next_page;
        SendPacket(&data);
    }
}

//////////////////////////////////////////////////////////////////////////////////////////
/// This function handles CMSG_ITEM_NAME_QUERY:
//////////////////////////////////////////////////////////////////////////////////////////
void WorldSession::HandleItemNameQueryOpcode(WorldPacket& recv_data)
{
    CHECK_INWORLD_RETURN
    CHECK_PACKET_SIZE(recv_data, 4);

    uint32 itemid;

    recv_data >> itemid;
    recv_data.read_skip<uint64>();

    WorldPacket reply(SMSG_ITEM_NAME_QUERY_RESPONSE, 100);
    reply << itemid;

    std::string Name = ("Unknown Item");

    ItemPrototype const* proto = sMySQLStore.GetItemProto(itemid);
    if (proto != nullptr)
    {
        LocalizedItem* li = (language > 0) ? sLocalizationMgr.GetLocalizedItem(itemid, language) : NULL;
        if (li)
            Name = li->Name;
        else
            Name = proto->Name;

        reply << Name;
        reply << proto->InventoryType;
    }
    else
    {
        reply << Name;
    }

    SendPacket(&reply);
}

void WorldSession::HandleInrangeQuestgiverQuery(WorldPacket& recv_data)
{
    CHECK_INWORLD_RETURN;

    WorldPacket data(SMSG_QUESTGIVER_STATUS_MULTIPLE, 1000);
    Object::InRangeSet::iterator itr;
    Creature* pCreature;
    uint32 count = 0;
    data << count;

    // 32 count
    // <foreach count>
    //    64 guid
    //    8 status

    for (itr = _player->m_objectsInRange.begin(); itr != _player->m_objectsInRange.end(); ++itr)
    {
        if (!(*itr)->IsCreature())
            continue;

        pCreature = static_cast<Creature*>(*itr);

        if (pCreature->isQuestGiver())
        {
            data << pCreature->GetGUID();
            data << uint8(sQuestMgr.CalcStatus(pCreature, _player));
            ++count;
        }
    }

    *(uint32*)(data.contents()) = count;
    SendPacket(&data);
}

void WorldSession::HandleAchievmentQueryOpcode(WorldPacket& recv_data)
{
    CHECK_INWORLD_RETURN;

    uint64 guid = recv_data.unpackGUID();               // Get the inspectee's GUID
    Player* pTarget = objmgr.GetPlayer((uint32)guid);
    if (!pTarget)
    {
        return;
    }
#ifdef ENABLE_ACHIEVEMENTS
    pTarget->GetAchievementMgr().SendAllAchievementData(GetPlayer());
#endif
}
<|endoftext|>


<commit_before>// Copyright (c) 2019 - 2020 by Robert Bosch GmbH. All rights reserved.
// Copyright (c) 2020 - 2021 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
#ifndef IOX_POSH_ROUDI_INTROSPECTION_PORT_INTROSPECTION_HPP
#define IOX_POSH_ROUDI_INTROSPECTION_PORT_INTROSPECTION_HPP

#include "fixed_size_container.hpp"
#include "iceoryx_hoofs/cxx/helplets.hpp"
#include "iceoryx_hoofs/cxx/method_callback.hpp"
#include "iceoryx_hoofs/internal/concurrent/periodic_task.hpp"
#include "iceoryx_posh/iceoryx_posh_types.hpp"
#include "iceoryx_posh/internal/popo/ports/publisher_port_data.hpp"
#include "iceoryx_posh/internal/popo/ports/publisher_port_user.hpp"
#include "iceoryx_posh/mepoo/chunk_header.hpp"
#include "iceoryx_posh/roudi/introspection_types.hpp"

#include <atomic>
#include <mutex>
#include <vector>

#include <map>

namespace iox
{
namespace roudi
{
/// @brief This class handles the port introspection for RouDi.
///        It is recommended to use the PortIntrospectionType alias which sets
///        the intended template parameter required for the actual introspection.
///        The class manages a thread that periodically updates a field with port
///        introspection data to which clients may subscribe.
template <typename PublisherPort, typename SubscriberPort>
class PortIntrospection
{
  private:
    enum class ConnectionState
    {
        DEFAULT,
        SUB_REQUESTED,
        CONNECTED
    };

    using PortIntrospectionTopic = PortIntrospectionFieldTopic;
    using PortThroughputIntrospectionTopic = PortThroughputIntrospectionFieldTopic;

    class PortData
    {
      private:
        /// internal helper classes

        struct ConnectionInfo;

        struct PublisherInfo
        {
            PublisherInfo() noexcept
            {
            }

            PublisherInfo(typename PublisherPort::MemberType_t& portData) noexcept
                : portData(&portData)
                , process(portData.m_runtimeName)
                , service(portData.m_serviceDescription)
                , node(portData.m_nodeName)
            {
            }

            typename PublisherPort::MemberType_t* portData{nullptr};
            RuntimeName_t process;
            capro::ServiceDescription service;
            NodeName_t node;

            using TimePointNs_t = mepoo::TimePointNs_t;
            using DurationNs_t = mepoo::DurationNs_t;
            TimePointNs_t m_sequenceNumberTimestamp{DurationNs_t(0)};
            mepoo::SequenceNumber_t m_sequenceNumber{0U};

            /// map from indices to object pointers
            std::map<int, ConnectionInfo*> connectionMap;
            int index{-1};
        };

        struct SubscriberInfo
        {
            SubscriberInfo() noexcept = default;

            SubscriberInfo(typename SubscriberPort::MemberType_t& portData) noexcept
                : portData(&portData)
                , process(portData.m_runtimeName)
                , service(portData.m_serviceDescription)
                , node(portData.m_nodeName)
            {
            }

            typename SubscriberPort::MemberType_t* portData{nullptr};
            RuntimeName_t process;
            capro::ServiceDescription service;
            NodeName_t node;
        };

        struct ConnectionInfo
        {
            ConnectionInfo() noexcept = default;

            ConnectionInfo(typename SubscriberPort::MemberType_t& portData) noexcept
                : subscriberInfo(portData)
                , state(ConnectionState::DEFAULT)
            {
            }

            ConnectionInfo(SubscriberInfo& subscriberInfo) noexcept
                : subscriberInfo(subscriberInfo)
                , state(ConnectionState::DEFAULT)
            {
            }

            SubscriberInfo subscriberInfo;
            PublisherInfo* publisherInfo{nullptr};
            ConnectionState state{ConnectionState::DEFAULT};

            bool isConnected() const noexcept
            {
                return publisherInfo && state == ConnectionState::CONNECTED;
            }
        };

      public:
        PortData() noexcept;

        /// @brief add a publisher port to be tracked by introspection
        /// @param[in] port to be added
        /// @return returns false if the port could not be added and true otherwise
        bool addPublisher(typename PublisherPort::MemberType_t& port) noexcept;

        /// @brief add a subscriber port to be tracked by introspection
        /// @param[in] portData to be added
        /// @return returns false if the port could not be added and true otherwise
        bool addSubscriber(typename SubscriberPort::MemberType_t& portData) noexcept;

        /// @brief remove a publisher port from introspection
        /// @param[in] port publisher port to be removed
        /// @return returns false if the port could not be removed (since it did not exist)
        ///              and true otherwise
        bool removePublisher(const PublisherPort& port) noexcept;

        /// @brief remove a subscriber port from introspection
        /// @param[in] port subscriber port to be removed
        /// @return returns false if the port could not be removed (since it did not exist)
        ///              and true otherwise
        bool removeSubscriber(const SubscriberPort& port) noexcept;

        /// @brief update the state of any connection identified by the capro id of a given message
        ///        according to the message type (e.g. capro::SUB for a subscription request)
        /// @param[in] message capro message to be processed
        /// @return returns false there is no corresponding capro service and true otherwise
        bool updateConnectionState(const capro::CaproMessage& message) noexcept;

        /// @brief update the subscriber connection state identified by the unique port id and the capro id of a given
        /// message according to the message type (e.g. capro::SUB for a subscription request)
        /// @param[in] message capro message to be processed
        /// @param[in] id unique port id
        /// @return false if there is no corresponding capro service or unique port id, otherwise true
        /// @note introduced for identifying the subscriber port whose connection state has to be updated, e.g. if a
        /// subscriber unsubscribes only its connection state should be updated - not the states of all subscribers
        /// which are subscribed to the same topic
        bool updateSubscriberConnectionState(const capro::CaproMessage& message, const popo::UniquePortId& id) noexcept;

        /// @brief prepare the topic to be send based on the internal connection state of all tracked ports
        /// @param[out] topic data structure to be prepared for sending
        void prepareTopic(PortIntrospectionTopic& topic) noexcept;

        void prepareTopic(PortThroughputIntrospectionTopic& topic) noexcept;

        void prepareTopic(SubscriberPortChangingIntrospectionFieldTopic& topic) noexcept;

        /// @brief compute the next connection state based on the current connection state and a capro message type when
        /// the communication policy is OneToMany
        /// @param[in] currentState current connection state (e.g. CONNECTED)
        /// @param[in] messageType capro message type
        /// @return returns the new connection state
        template <typename T, std::enable_if_t<std::is_same<T, iox::build::OneToManyPolicy>::value>* = nullptr>
        PortIntrospection::ConnectionState getNextState(ConnectionState currentState,
                                                        capro::CaproMessageType messageType) noexcept;

        /// @brief compute the next connection state based on the current connection state and a capro message type when
        /// the communication policy is ManyToMany
        /// @param[in] currentState current connection state (e.g. CONNECTED)
        /// @param[in] messageType capro message type
        /// @return returns the new connection state
        template <typename T, std::enable_if_t<std::is_same<T, iox::build::ManyToManyPolicy>::value>* = nullptr>
        PortIntrospection::ConnectionState getNextState(ConnectionState currentState,
                                                        capro::CaproMessageType messageType) noexcept;

        /// @brief indicates whether the logical object state has changed (i.e. the data is new)
        /// @return returns true if the data is new(e.g. new connections were established), false otherwise
        bool isNew() const noexcept;

        /// @brief sets the internal flag indicating new data
        /// @param[in] value value to be set
        void setNew(bool value) noexcept;

      private:
        using PublisherContainer = FixedSizeContainer<PublisherInfo, MAX_PUBLISHERS>;
        using ConnectionContainer = FixedSizeContainer<ConnectionInfo, MAX_SUBSCRIBERS>;

        /// @brief inner map maps from unique port IDs to indices in the PublisherContainer
        std::map<capro::ServiceDescription, std::map<popo::UniquePortId, typename PublisherContainer::Index_t>>
            m_publisherMap;

        /// inner map maps from unique port IDs to indices in the ConnectionContainer
        std::map<capro::ServiceDescription, std::map<popo::UniquePortId, typename ConnectionContainer::Index_t>>
            m_connectionMap;

        /// @note we avoid allocating the objects on the heap but can still use a map
        /// to locate/remove them fast(er)
        /// max number needs to be a compile time constant
        PublisherContainer m_publisherContainer;
        ConnectionContainer m_connectionContainer;

        std::atomic<bool> m_newData;
        std::mutex m_mutex;
    };

    // end of helper classes

  public:
    PortIntrospection() noexcept;

    ~PortIntrospection() noexcept;

    // delete copy constructor and assignment operator
    PortIntrospection(PortIntrospection const&) = delete;
    PortIntrospection& operator=(PortIntrospection const&) = delete;
    // delete move constructor and assignment operator
    PortIntrospection(PortIntrospection&&) = delete;
    PortIntrospection& operator=(PortIntrospection&&) = delete;

    /// @brief add a publisher port to be tracked by introspection
    /// @param[in] port to be added
    /// @return returns false if the port could not be added and true otherwise
    bool addPublisher(typename PublisherPort::MemberType_t& port) noexcept;

    /// @brief add a subscriber port to be tracked by introspection
    /// @param[in] port to be added
    /// @return returns false if the port could not be added and true otherwise
    bool addSubscriber(typename SubscriberPort::MemberType_t& port) noexcept;

    /// @brief remove a publisher port from introspection
    /// @param[in] port publisher port to be removed
    /// @return returns false if the port could not be removed (since it did not exist)
    ///              and true otherwise
    bool removePublisher(const PublisherPort& port) noexcept;

    /// @brief remove a subscriber port from introspection
    /// @param[in] port subscriber port to be removed
    /// @return returns false if the port could not be removed (since it did not exist)
    ///              and true otherwise
    bool removeSubscriber(const SubscriberPort& port) noexcept;

    /// @brief report a capro message to introspection (since this could change the state of active connections)
    /// @param[in] message capro message to be processed
    void reportMessage(const capro::CaproMessage& message) noexcept;

    /// @brief report a capro message to introspection (since this could change the state of active connections)
    /// @param[in] message capro message to be processed
    /// @param[in] id unique port id
    /// @note introduced for identifying the subscriber port whose connection state has to be updated, e.g. if a
    /// subscriber unsubscribes only its connection state should be updated - not the states of all subscribers
    /// which are subscribed to the same topic
    void reportMessage(const capro::CaproMessage& message, const popo::UniquePortId& id) noexcept;

    /// @brief register publisher port used to send introspection
    /// @param[in] publisherPort publisher port to be registered
    /// @return true if registration was successful, false otherwise
    bool registerPublisherPort(PublisherPort&& publisherPortGeneric,
                               PublisherPort&& publisherPortThroughput,
                               PublisherPort&& publisherPortSubscriberPortsData) noexcept;

    /// @brief set the time interval used to send new introspection data
    /// @param[in] interval duration between two send invocations
    void setSendInterval(const units::Duration interval) noexcept;

    /// @brief start the internal send thread
    void run() noexcept;

    /// @brief stop the internal send thread
    void stop() noexcept;

  protected:
    /// @brief sends the port data; this is used from the unittests
    void sendPortData() noexcept;

    /// @brief sends the throughput data; this is used from the unittests
    void sendThroughputData() noexcept;

    /// @brief sends the subscriberport changing data, this is used from the unittests
    void sendSubscriberPortsData() noexcept;

    /// @brief calls the three specific send functions from above, this is used from the periodic task
    void send() noexcept;

  protected:
    cxx::optional<PublisherPort> m_publisherPort;
    cxx::optional<PublisherPort> m_publisherPortThroughput;
    cxx::optional<PublisherPort> m_publisherPortSubscriberPortsData;

  private:
    PortData m_portData;

    units::Duration m_sendInterval{units::Duration::fromSeconds(1U)};
    concurrent::PeriodicTask<cxx::MethodCallback<void>> m_publishingTask{
        concurrent::PeriodicTaskManualStart, "PortIntr", *this, &PortIntrospection::send};
};

/// @brief typedef for the templated port introspection class that is used by RouDi for the
/// actual port introspection functionality.
using PortIntrospectionType = PortIntrospection<PublisherPortUserType, SubscriberPortUserType>;

} // namespace roudi
} // namespace iox

#include "port_introspection.inl"

#endif // IOX_POSH_ROUDI_INTROSPECTION_PORT_INTROSPECTION_HPP
<commit_msg>iox-#1252 Remove unused include<commit_after>// Copyright (c) 2019 - 2020 by Robert Bosch GmbH. All rights reserved.
// Copyright (c) 2020 - 2021 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
#ifndef IOX_POSH_ROUDI_INTROSPECTION_PORT_INTROSPECTION_HPP
#define IOX_POSH_ROUDI_INTROSPECTION_PORT_INTROSPECTION_HPP

#include "fixed_size_container.hpp"
#include "iceoryx_hoofs/cxx/helplets.hpp"
#include "iceoryx_hoofs/cxx/method_callback.hpp"
#include "iceoryx_hoofs/internal/concurrent/periodic_task.hpp"
#include "iceoryx_posh/iceoryx_posh_types.hpp"
#include "iceoryx_posh/internal/popo/ports/publisher_port_data.hpp"
#include "iceoryx_posh/internal/popo/ports/publisher_port_user.hpp"
#include "iceoryx_posh/mepoo/chunk_header.hpp"
#include "iceoryx_posh/roudi/introspection_types.hpp"

#include <atomic>
#include <mutex>

#include <map>

namespace iox
{
namespace roudi
{
/// @brief This class handles the port introspection for RouDi.
///        It is recommended to use the PortIntrospectionType alias which sets
///        the intended template parameter required for the actual introspection.
///        The class manages a thread that periodically updates a field with port
///        introspection data to which clients may subscribe.
template <typename PublisherPort, typename SubscriberPort>
class PortIntrospection
{
  private:
    enum class ConnectionState
    {
        DEFAULT,
        SUB_REQUESTED,
        CONNECTED
    };

    using PortIntrospectionTopic = PortIntrospectionFieldTopic;
    using PortThroughputIntrospectionTopic = PortThroughputIntrospectionFieldTopic;

    class PortData
    {
      private:
        /// internal helper classes

        struct ConnectionInfo;

        struct PublisherInfo
        {
            PublisherInfo() noexcept
            {
            }

            PublisherInfo(typename PublisherPort::MemberType_t& portData) noexcept
                : portData(&portData)
                , process(portData.m_runtimeName)
                , service(portData.m_serviceDescription)
                , node(portData.m_nodeName)
            {
            }

            typename PublisherPort::MemberType_t* portData{nullptr};
            RuntimeName_t process;
            capro::ServiceDescription service;
            NodeName_t node;

            using TimePointNs_t = mepoo::TimePointNs_t;
            using DurationNs_t = mepoo::DurationNs_t;
            TimePointNs_t m_sequenceNumberTimestamp{DurationNs_t(0)};
            mepoo::SequenceNumber_t m_sequenceNumber{0U};

            /// map from indices to object pointers
            std::map<int, ConnectionInfo*> connectionMap;
            int index{-1};
        };

        struct SubscriberInfo
        {
            SubscriberInfo() noexcept = default;

            SubscriberInfo(typename SubscriberPort::MemberType_t& portData) noexcept
                : portData(&portData)
                , process(portData.m_runtimeName)
                , service(portData.m_serviceDescription)
                , node(portData.m_nodeName)
            {
            }

            typename SubscriberPort::MemberType_t* portData{nullptr};
            RuntimeName_t process;
            capro::ServiceDescription service;
            NodeName_t node;
        };

        struct ConnectionInfo
        {
            ConnectionInfo() noexcept = default;

            ConnectionInfo(typename SubscriberPort::MemberType_t& portData) noexcept
                : subscriberInfo(portData)
                , state(ConnectionState::DEFAULT)
            {
            }

            ConnectionInfo(SubscriberInfo& subscriberInfo) noexcept
                : subscriberInfo(subscriberInfo)
                , state(ConnectionState::DEFAULT)
            {
            }

            SubscriberInfo subscriberInfo;
            PublisherInfo* publisherInfo{nullptr};
            ConnectionState state{ConnectionState::DEFAULT};

            bool isConnected() const noexcept
            {
                return publisherInfo && state == ConnectionState::CONNECTED;
            }
        };

      public:
        PortData() noexcept;

        /// @brief add a publisher port to be tracked by introspection
        /// @param[in] port to be added
        /// @return returns false if the port could not be added and true otherwise
        bool addPublisher(typename PublisherPort::MemberType_t& port) noexcept;

        /// @brief add a subscriber port to be tracked by introspection
        /// @param[in] portData to be added
        /// @return returns false if the port could not be added and true otherwise
        bool addSubscriber(typename SubscriberPort::MemberType_t& portData) noexcept;

        /// @brief remove a publisher port from introspection
        /// @param[in] port publisher port to be removed
        /// @return returns false if the port could not be removed (since it did not exist)
        ///              and true otherwise
        bool removePublisher(const PublisherPort& port) noexcept;

        /// @brief remove a subscriber port from introspection
        /// @param[in] port subscriber port to be removed
        /// @return returns false if the port could not be removed (since it did not exist)
        ///              and true otherwise
        bool removeSubscriber(const SubscriberPort& port) noexcept;

        /// @brief update the state of any connection identified by the capro id of a given message
        ///        according to the message type (e.g. capro::SUB for a subscription request)
        /// @param[in] message capro message to be processed
        /// @return returns false there is no corresponding capro service and true otherwise
        bool updateConnectionState(const capro::CaproMessage& message) noexcept;

        /// @brief update the subscriber connection state identified by the unique port id and the capro id of a given
        /// message according to the message type (e.g. capro::SUB for a subscription request)
        /// @param[in] message capro message to be processed
        /// @param[in] id unique port id
        /// @return false if there is no corresponding capro service or unique port id, otherwise true
        /// @note introduced for identifying the subscriber port whose connection state has to be updated, e.g. if a
        /// subscriber unsubscribes only its connection state should be updated - not the states of all subscribers
        /// which are subscribed to the same topic
        bool updateSubscriberConnectionState(const capro::CaproMessage& message, const popo::UniquePortId& id) noexcept;

        /// @brief prepare the topic to be send based on the internal connection state of all tracked ports
        /// @param[out] topic data structure to be prepared for sending
        void prepareTopic(PortIntrospectionTopic& topic) noexcept;

        void prepareTopic(PortThroughputIntrospectionTopic& topic) noexcept;

        void prepareTopic(SubscriberPortChangingIntrospectionFieldTopic& topic) noexcept;

        /// @brief compute the next connection state based on the current connection state and a capro message type when
        /// the communication policy is OneToMany
        /// @param[in] currentState current connection state (e.g. CONNECTED)
        /// @param[in] messageType capro message type
        /// @return returns the new connection state
        template <typename T, std::enable_if_t<std::is_same<T, iox::build::OneToManyPolicy>::value>* = nullptr>
        PortIntrospection::ConnectionState getNextState(ConnectionState currentState,
                                                        capro::CaproMessageType messageType) noexcept;

        /// @brief compute the next connection state based on the current connection state and a capro message type when
        /// the communication policy is ManyToMany
        /// @param[in] currentState current connection state (e.g. CONNECTED)
        /// @param[in] messageType capro message type
        /// @return returns the new connection state
        template <typename T, std::enable_if_t<std::is_same<T, iox::build::ManyToManyPolicy>::value>* = nullptr>
        PortIntrospection::ConnectionState getNextState(ConnectionState currentState,
                                                        capro::CaproMessageType messageType) noexcept;

        /// @brief indicates whether the logical object state has changed (i.e. the data is new)
        /// @return returns true if the data is new(e.g. new connections were established), false otherwise
        bool isNew() const noexcept;

        /// @brief sets the internal flag indicating new data
        /// @param[in] value value to be set
        void setNew(bool value) noexcept;

      private:
        using PublisherContainer = FixedSizeContainer<PublisherInfo, MAX_PUBLISHERS>;
        using ConnectionContainer = FixedSizeContainer<ConnectionInfo, MAX_SUBSCRIBERS>;

        /// @brief inner map maps from unique port IDs to indices in the PublisherContainer
        std::map<capro::ServiceDescription, std::map<popo::UniquePortId, typename PublisherContainer::Index_t>>
            m_publisherMap;

        /// inner map maps from unique port IDs to indices in the ConnectionContainer
        std::map<capro::ServiceDescription, std::map<popo::UniquePortId, typename ConnectionContainer::Index_t>>
            m_connectionMap;

        /// @note we avoid allocating the objects on the heap but can still use a map
        /// to locate/remove them fast(er)
        /// max number needs to be a compile time constant
        PublisherContainer m_publisherContainer;
        ConnectionContainer m_connectionContainer;

        std::atomic<bool> m_newData;
        std::mutex m_mutex;
    };

    // end of helper classes

  public:
    PortIntrospection() noexcept;

    ~PortIntrospection() noexcept;

    // delete copy constructor and assignment operator
    PortIntrospection(PortIntrospection const&) = delete;
    PortIntrospection& operator=(PortIntrospection const&) = delete;
    // delete move constructor and assignment operator
    PortIntrospection(PortIntrospection&&) = delete;
    PortIntrospection& operator=(PortIntrospection&&) = delete;

    /// @brief add a publisher port to be tracked by introspection
    /// @param[in] port to be added
    /// @return returns false if the port could not be added and true otherwise
    bool addPublisher(typename PublisherPort::MemberType_t& port) noexcept;

    /// @brief add a subscriber port to be tracked by introspection
    /// @param[in] port to be added
    /// @return returns false if the port could not be added and true otherwise
    bool addSubscriber(typename SubscriberPort::MemberType_t& port) noexcept;

    /// @brief remove a publisher port from introspection
    /// @param[in] port publisher port to be removed
    /// @return returns false if the port could not be removed (since it did not exist)
    ///              and true otherwise
    bool removePublisher(const PublisherPort& port) noexcept;

    /// @brief remove a subscriber port from introspection
    /// @param[in] port subscriber port to be removed
    /// @return returns false if the port could not be removed (since it did not exist)
    ///              and true otherwise
    bool removeSubscriber(const SubscriberPort& port) noexcept;

    /// @brief report a capro message to introspection (since this could change the state of active connections)
    /// @param[in] message capro message to be processed
    void reportMessage(const capro::CaproMessage& message) noexcept;

    /// @brief report a capro message to introspection (since this could change the state of active connections)
    /// @param[in] message capro message to be processed
    /// @param[in] id unique port id
    /// @note introduced for identifying the subscriber port whose connection state has to be updated, e.g. if a
    /// subscriber unsubscribes only its connection state should be updated - not the states of all subscribers
    /// which are subscribed to the same topic
    void reportMessage(const capro::CaproMessage& message, const popo::UniquePortId& id) noexcept;

    /// @brief register publisher port used to send introspection
    /// @param[in] publisherPort publisher port to be registered
    /// @return true if registration was successful, false otherwise
    bool registerPublisherPort(PublisherPort&& publisherPortGeneric,
                               PublisherPort&& publisherPortThroughput,
                               PublisherPort&& publisherPortSubscriberPortsData) noexcept;

    /// @brief set the time interval used to send new introspection data
    /// @param[in] interval duration between two send invocations
    void setSendInterval(const units::Duration interval) noexcept;

    /// @brief start the internal send thread
    void run() noexcept;

    /// @brief stop the internal send thread
    void stop() noexcept;

  protected:
    /// @brief sends the port data; this is used from the unittests
    void sendPortData() noexcept;

    /// @brief sends the throughput data; this is used from the unittests
    void sendThroughputData() noexcept;

    /// @brief sends the subscriberport changing data, this is used from the unittests
    void sendSubscriberPortsData() noexcept;

    /// @brief calls the three specific send functions from above, this is used from the periodic task
    void send() noexcept;

  protected:
    cxx::optional<PublisherPort> m_publisherPort;
    cxx::optional<PublisherPort> m_publisherPortThroughput;
    cxx::optional<PublisherPort> m_publisherPortSubscriberPortsData;

  private:
    PortData m_portData;

    units::Duration m_sendInterval{units::Duration::fromSeconds(1U)};
    concurrent::PeriodicTask<cxx::MethodCallback<void>> m_publishingTask{
        concurrent::PeriodicTaskManualStart, "PortIntr", *this, &PortIntrospection::send};
};

/// @brief typedef for the templated port introspection class that is used by RouDi for the
/// actual port introspection functionality.
using PortIntrospectionType = PortIntrospection<PublisherPortUserType, SubscriberPortUserType>;

} // namespace roudi
} // namespace iox

#include "port_introspection.inl"

#endif // IOX_POSH_ROUDI_INTROSPECTION_PORT_INTROSPECTION_HPP
<|endoftext|>