Skip to content
This repository has been archived by the owner on Oct 11, 2020. It is now read-only.

WIP GraphQL API #142

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 40 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ function(assign_source_group)
endforeach()
endfunction(assign_source_group)

function(add_bigobj_flag target)
if(MSVC)
# MSVC requires the /bigobj flag if the number of sections gets too big.
target_compile_options(${target} PRIVATE /bigobj)
endif()
endfunction()

# Set output directories
set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/bin)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
Expand Down Expand Up @@ -100,6 +107,7 @@ endif()

# Automatically search for source files.
include_directories("${CMAKE_SOURCE_DIR}/src")
include_directories(graphqlschema ${CMAKE_CURRENT_BINARY_DIR}/src)
file(GLOB_RECURSE source_files
"src/*.hpp"
"src/*.cpp"
Expand Down Expand Up @@ -143,12 +151,39 @@ message(STATUS "STATIC_IMAGE_RESOURCES: ${STATIC_IMAGE_RESOURCES}")
message(STATUS "STATIC_FONT_RESOURCES: ${STATIC_FONT_RESOURCES}")
message(STATUS "STATIC_TYPE_SYSTEM_RESOURCES: ${STATIC_TYPE_SYSTEM_RESOURCES}")

# If really needed: here you can enable generating the graphql API (stubs)
# afterwards you will need to implement functionality for each stub.

option(REGENERATE_GRAPHQL_API_STUBS "Replace the GraphQL API implementation with new stubs." OFF)
# if(REGENERATE_GRAPHQL_API_STUBS)
find_program (SCHEMAGEN schemagen PATHS ${CONAN_BIN_DIRS_CPPGRAPHQLGEN})

file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/src/graphql)
add_custom_command(
OUTPUT
src/graphql/EntitiesSchema.cpp
src/graphql/EntitiesSchema.h
COMMAND ${SCHEMAGEN} --schema="${CMAKE_CURRENT_SOURCE_DIR}/src/graphql/schema.entities.graphql" --prefix="Entities" --namespace="entities"
DEPENDS ${SCHEMAGEN} src/graphql/schema.entities.graphql
WORKING_DIRECTORY src/graphql
COMMENT "Generating mock GraphQL Schema files"
)

# force the generation of samples on the default build target
add_custom_target(regenerate_graphql_schema ALL
DEPENDS
src/graphql/EntitiesSchema.cpp
)
# endif(REGENERATE_GRAPHQL_API_STUBS)

list(REMOVE_ITEM LIBRARY_SOURCE_FILES "src/graphql/EntitiesSchema.cpp")
add_library(graphqlschema STATIC src/graphql/EntitiesSchema.cpp)
target_include_directories(graphqlschema PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/src/graphql)
add_bigobj_flag(graphqlschema)

# Entity-system library
add_library(inexor-entity-system-lib ${LIBRARY_SOURCE_FILES})
target_link_libraries(inexor-entity-system-lib ${CONAN_LIBS})
#if(APPLE) <- Fohlen, can you verify this is not needed?
# target_link_libraries(inexor-entity-system-lib "-framework CoreFoundation")
#endif()
target_link_libraries(inexor-entity-system-lib graphqlschema ${CONAN_LIBS})

# Inexor standalone application.
add_executable(inexor-standalone
Expand All @@ -157,7 +192,7 @@ add_executable(inexor-standalone
${STATIC_IMAGE_RESOURCES}
${STATIC_FONT_RESOURCES}
${STATIC_TYPE_SYSTEM_RESOURCES})
target_link_libraries(inexor-standalone PUBLIC inexor-entity-system-lib)
target_link_libraries(inexor-standalone PUBLIC graphqlschema inexor-entity-system-lib)
target_link_libraries(inexor-standalone PUBLIC ${CONAN_LIBS})
target_link_libraries(inexor-standalone PRIVATE Corrade::Utility)
if(APPLE)
Expand Down
44 changes: 24 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,6 @@ In contrast to Sauerbraten, Inexor adds a lot of functionality and strives to st
The goal of this project is to be more flexible and create an environment where development is easy, fast and where creativity can prosper.


## How is Inexor organized?

We are a non-hierarchical organization. This means we are simply a group of people with different ideas working together without a leader making all the decisions. Anyone of us is free to work on the particular things they want to.
For this organization to work properly we rely on good communication. We are on Telegram and Mumble pretty much everyday. Every so often we organize official Mumble meetings to discuss our roadmap and strategies.

We are open for new people!

## Where are we headed?

Our goal is to make the game as moddable and developer-friendly as possible.
Even though we might have refactored most of the code at some point, Inexor should always feel like Sauerbraten gameplay wise.

A popular stance among the Sauerbraten community is "that's impossible", and this is what we want to prove wrong. Our answer to remarks like "things are best like they are" is: standing still means falling behind.

## The entity-system

The entity-system will lay the basis on the other systems of the game will be built on.
For more information take a look at the [wiki](https://inexor.org/wiki/features/Entity-System.html).

## Build process

Building the `entity-system` is quite straightforward:
Expand All @@ -33,7 +14,11 @@ Building the `entity-system` is quite straightforward:

To build the entity-system one needs:

- a compiler of choice (`gcc`, `clang`, `MSVC`)
- a compiler of choice
- Clang/LLVM >= 5
- Visual C++ >= 15.3 / Visual Studio >= 2017
- Xcode >= 10.2
- GCC >= 9
- `cmake`
- [`conan`](https://conan.io)

Expand All @@ -54,6 +39,25 @@ Once installed invoke `doxygen` from the `entity-system` directoy like so:

`doxygen`

## How is Inexor organized?

We are a non-hierarchical organization. This means we are simply a group of people with different ideas working together without a leader making all the decisions. Anyone of us is free to work on the particular things they want to.
For this organization to work properly we rely on good communication. We are on Telegram and Mumble pretty much everyday. Every so often we organize official Mumble meetings to discuss our roadmap and strategies.

We are open for new people!

## Where are we headed?

Our goal is to make the game as moddable and developer-friendly as possible.
Even though we might have refactored most of the code at some point, Inexor should always feel like Sauerbraten gameplay wise.

A popular stance among the Sauerbraten community is "that's impossible", and this is what we want to prove wrong. Our answer to remarks like "things are best like they are" is: standing still means falling behind.

## The entity-system

The entity-system will lay the basis on the other systems of the game will be built on.
For more information take a look at the [wiki](https://inexor.org/wiki/features/Entity-System.html).

## Join us

You have already accomplished the first step by reading this readme. Congratz!
Expand Down
28 changes: 13 additions & 15 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,25 @@ class InexorConan(ConanFile):

requires = (
"benchmark/1.5.0",
"glm/0.9.9.5",
"gtest/1.8.1",
"spdlog/1.4.2",

"boost_property_tree/1.69.0@bincrafters/stable",
"boost_signals2/1.69.0@bincrafters/stable",
"boost_range/1.69.0@bincrafters/stable",
"freetype/2.9.1@bincrafters/stable",
"openal/1.19.0@bincrafters/stable",

"magic_enum/0.6.3@neargye/stable",

"jsonformoderncpp/3.7.0@vthiery/stable",

"boost-di/1.1.0@inexorgame/stable",
"boost-te/19.Jan.19@inexorgame/stable",
"cpp.react/legacy1@inexorgame/stable",

"crossguid/06-03-19@inexorgame/testing",
"cppgraphqlgen/3.0.4@inexorgame/testing",
"cpp.react/legacy1@inexorgame/stable",
"freetype/2.9.1@bincrafters/stable",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please try if you can update to the CCI variant of freetype and version 2.10.1

Suggested change
"freetype/2.9.1@bincrafters/stable",
"freetype/2.10.1",

"glm/0.9.9.5",
"gtest/1.8.1",
"jsonformoderncpp/3.7.0@vthiery/stable",
"magic_enum/0.6.3@neargye/stable",
"magnum/2019.01@inexorgame/testing",
"magnum_plugins/2019.01@inexorgame/testing"
"magnum_plugins/2019.01@inexorgame/testing",
"openal/1.19.0@bincrafters/stable",
"restinio/0.6.0.1@inexorgame/testing",
"spdlog/1.4.2",
"fmt/6.0.0"
)

generators = "cmake"
Expand All @@ -52,7 +50,7 @@ class InexorConan(ConanFile):
"magnum_plugins:with_stbtruetypefont": True,
"magnum_plugins:with_freetypefont": True,
"magnum_plugins:build_plugins_static": True,
"TBB:shared": True,
"TBB:shared": True
}

def imports(self):
Expand Down
47 changes: 0 additions & 47 deletions schema/entities.gql

This file was deleted.

110 changes: 109 additions & 1 deletion src/InexorApplication.cpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
#include "InexorApplication.hpp"

#include <graphqlservice/JSONResponse.h>
#include <utility>

#include "spdlog/spdlog.h"

#include "restinio/all.hpp"
#include <nlohmann/json.hpp>

namespace inexor {

// Static instances of the Inexor application(s)
std::vector<InexorApplication *> InexorApplication::instances;

InexorApplication::InexorApplication(EntitySystemModulePtr entity_system_module, TypeSystemModulePtr type_system_module, ConfigurationModulePtr configuration_module, EntitySystemDebuggerPtr entity_system_debugger,
VisualScriptingSystemModulePtr visual_scripting_system_module, CommandModulePtr command_module, ClientModulePtr client_module, LogManagerPtr log_manager)
VisualScriptingSystemModulePtr visual_scripting_system_module, CommandModulePtr command_module, ClientModulePtr client_module, QueryPtr query, LogManagerPtr log_manager)
{
this->entity_system_module = std::move(entity_system_module);
this->type_system_module = std::move(type_system_module);
Expand All @@ -20,6 +24,7 @@ InexorApplication::InexorApplication(EntitySystemModulePtr entity_system_module,
this->command_module = std::move(command_module);
this->client_module = std::move(client_module);
this->log_manager = std::move(log_manager);
this->query = std::move(query);
this->running = false;
}

Expand Down Expand Up @@ -65,6 +70,16 @@ void InexorApplication::pre_init(int argc, char *argv[])

}

void replaceAll(std::string& str, const std::string& from, const std::string& to) {
if(from.empty())
return;
size_t start_pos = 0;
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
}
}

void InexorApplication::init()
{
spdlog::get(LOGGER_NAME)->info("Starting Inexor...");
Expand All @@ -77,6 +92,99 @@ void InexorApplication::init()

// Initialize the rendering.
client_module->init_components();



// restinio::run(
// restinio::on_this_thread()
// .port(8080)
// .address("localhost")
// .request_handler([](auto req) {
// return req->create_response().set_body("Hello, World!").done();
// }));
// TODO: Move the GraphQL to it's own component

std::thread restinio_thread([this]() {
auto service = std::make_shared<graphql::entities::Operations>(query);
restinio::run(restinio::on_this_thread()
.port(31415)
.address("192.168.255.206")
.request_handler([service] (auto req) {
if (restinio::http_method_post() == req->header().method()) {
spdlog::get(LOGGER_NAME)->info("POST");
try
{
std::string req_body = req->body();
spdlog::get(LOGGER_NAME)->info(req_body);
using json = nlohmann::json;
// spdlog::get(LOGGER_NAME)->info("1");
// auto test = json::parse("{\n"
// "\"operationName\": null,\n"
// "\"variables\": {},\n"
// "\"query\": \"{ entity_types { name attributes { uuid } } }\"\n"
// "}");
spdlog::get(LOGGER_NAME)->info("1");
req_body.erase(std::remove(req_body.begin(), req_body.end(), '\n'), req_body.end());
spdlog::get(LOGGER_NAME)->info("2");
auto req_body_json = json::parse(req_body);
spdlog::get(LOGGER_NAME)->info(req_body_json);
auto req_query_json = req_body_json["query"];
std::string query_string = req_query_json.dump();
query_string = query_string.substr(1, query_string.size() - 2);
// TODO: unescape all "invalid" characters
replaceAll(query_string, "\\n", "");
spdlog::get(LOGGER_NAME)->info(query_string);

// std::string query_string = "{"operationName":null,"variables":{},"query":"{ entity_types { name attributes { uuid } } }";
spdlog::get(LOGGER_NAME)->info("GraphQL query string: {}", query_string);
graphql::peg::ast graphql_query = graphql::peg::parseString(std::move(query_string));
if (!graphql_query.root)
{
throw std::runtime_error("Unknown error: No query root");
}
spdlog::get(LOGGER_NAME)->info("Executing GraphQL query...");

auto result = graphql::response::toJSON(service->resolve(nullptr, *graphql_query.root, "", graphql::response::Value(graphql::response::Type::Map)).get());
spdlog::get(LOGGER_NAME)->info("GraphQL response: {}", result);

return req->create_response().append_header("Access-Control-Allow-Origin", "*").set_body(result).done();
} catch (const std::runtime_error &ex)
{
spdlog::get(LOGGER_NAME)->error("GraphQL query failed: {}", ex.what());
return req->create_response(restinio::status_internal_server_error())
.append_header(restinio::http_field::server, "Inexor GraphQL")
.append_header(restinio::http_field::access_control_allow_origin, "*")
.append_header(restinio::http_field::access_control_allow_headers, "*")
.set_body("error")
.done();
}
} else if (restinio::http_method_options() == req->header().method()) {
spdlog::get(LOGGER_NAME)->info("HTTP OPTIONS");
return req->create_response()
.append_header(restinio::http_field::server, "Inexor GraphQL")
.append_header(restinio::http_field::access_control_allow_origin, "*")
.append_header(restinio::http_field::access_control_allow_headers, "*")
.append_header_date_field()
// .append_header(restinio::http_field::content_type, "text/plain; charset=utf-8")
.set_body("")
.done();
} else {
spdlog::get(LOGGER_NAME)->error("Unknown http method {}", req->header().method());
return req->create_response(restinio::status_internal_server_error())
.append_header(restinio::http_field::server, "Inexor GraphQL")
.append_header(restinio::http_field::access_control_allow_origin, "*")
.append_header(restinio::http_field::access_control_allow_headers, "*")
.set_body("Unknown http method")
.done();

}
})
);
});
restinio_thread.detach();



}

void InexorApplication::run()
Expand Down
Loading