Merge branch 'develop' into feat-enet

# Conflicts:
#	CONTRIBUTING.md
#	Phoenix/Client/Include/Client/Game.hpp
#	Phoenix/Client/Source/Client.cpp
#	Phoenix/Client/Source/Game.cpp
#	Phoenix/Client/Source/Player.cpp
#	Phoenix/Common/Include/Common/CMakeLists.txt
#	Phoenix/Common/Source/CMakeLists.txt
develop
Austin 2020-04-27 20:20:05 -07:00
commit 16688dab98
40 changed files with 1297 additions and 669 deletions

View File

@ -1,5 +1,7 @@
# GENTEN STUDIOS CONTRIBITING GUIDELINES
These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. However a PR may have changes requested on it if it does not adhear to guidelines outlined here.
If you are just browsing through quick before starting, it's worth keeping [the PR checklist](#pr-checklist) in mind before you open a new PR to help make your PR review go smoother.
## TABLE OF CONTENTS
@ -18,24 +20,24 @@ Familiarity with Git, specifically with using branches, is a must for interactin
**The Master Branch** is the *most* stable source for the code, this contains major/ minor releases and nothing else. Only release and hotfix branches can be pushed here.
- All Genten Developers and Staff Members must approve any push to the master branch - unless said otherwise (e.g. last minute hotfixes).
- The code must be thoroughly tested on all supported systems before being pushed here.
- Pre-compiled distributable code should be pushed to the releases section of GitHub when pushing code to the Master branch.
- The release version should be incremented any time code is pushed to the release branch. See [Versioning and releases](#VERSIONING-AND-RELEASES)
**Hot Fix Branches** are a method for pushing code to the master branch after a release. These should be minor fixes that add no functionality (unless the bug restricts a certain functionality) to the project.
**Release Branches** are the staging area for a release. Similar to a hotfix branch only bug testing is to be done here and no new features should be added.
- A 3/4 developer approval to push to the release branch.
- The code must be tested on all supported systems before being pushed here.
**Release Branches** are the staging area for a release. Similar to a hotfix branch only bug testing is to be done here and no new features should be added.
- A > 75% active developer approval to push to the release branch.
**The Develop Branch** Is where new features are added, however not where they are worked on. Commits should be merged from a feature branch into the develop branch and from nowhere else.
- It takes a greater than 2/3 developer approval to push to the develop branch.
- The code must be tested on all supported systems before being pushed here.
- It takes at least 2 developer approvals to push to the develop branch.
- CI must pass for all supported systems before being pushed here.
**Feature Branches** are where the action happens, each feature branch contains the addition of a single new feature. This is where all main development work happens and it is required to create a feature branch to push code. Any Genten developer can accept code into a feature branch.
- These branches must follow the naming convention of ``feat-[feature name]`` (e.g. ``feat-voxels``)
- A single developer can approve merging code into a feature branch.
- A feature branch may also potentially only exist on a fork if multiple people are not collaborating on a feature branch.
- These branches should follow the naming convention of ``feat-[feature name]`` (e.g. ``feat-voxels``)
- Sub branches will generally follow a similar naming convention of ``feat-[feature name]-[subfeature]`` (e.g. ``feat-voxels-blocks``)
### Pull Requests
- A pull request from a contributors repository can be approved by a single developer, this code can only be pulled into feature branches and not directly into the development or higher branches.
### PR checklist
This section lists the requirements for merging code. All of these requirements must be satisfied before code is merged into any branch.
@ -43,7 +45,7 @@ All Branches:
- Accomplish the feature(s) it was designed to accomplish
- Has the branch it's merging into merged onto itself and all conflicts are resolved
- Clean of all binaries and other non-source material
- Code is documented with doxygen style comments
- All classes, functions, and variables are documented with doxygen style comments
- Complies with style guide and other contributing guidelines
- All Developer reviewer comments are resolved
- Code is formatted with cLang
@ -89,7 +91,7 @@ Functions should follow the `camelCase` convention, where the first word has a l
```cpp
int doSomething();
int goddIsTodd();
int wowThisIsACoolFunction();
int wowThisIsACoolFunction();
```
#### VARIABLES
There are a few things to know about naming variables. Most all variables must follow the camelCase convention, however, private member variables of a class must use an `m_` prefix before them. However, constants and macros should be ALL_CAPITALS_USING_UNDERSCORES_INSTEAD_OF_SPACES. Hopefully that makes sense, if not, heres an example to make things make sense:
@ -135,15 +137,16 @@ class FooBar {
public:
/**
* @brief This does an important thing
* @param desc A description of the thing
* @return A very important integer. (probably an error code or something)
*/
int doThing(std::string desc);
*
* @param desc A description of the provided parameter
* @return What does the value returned by this function mean?
*/
int doThing(std::string desc);
float randomNumber; //< This is a single line description for a member
float randomNumber; //< This is a single line description for a member
/// @brief This member variable requires a longer single line comment. (filling space)
bool foobarred;
/// @brief This member variable requires a longer single line comment. (filling space)
bool foobarred;
};
```
Multiline Doxygen comments should start with a /** and end with a normal */. The second asterisk is something that Doxygen recogises through its own interpreter. There are two ways for single line comments, choose whichever one looks right, a slightly longer one however should use the second method, just so we dont have to become grannies to read.
@ -158,6 +161,4 @@ When choosing a third party, we need to make sure the third party is right for o
### 2. Install
When working with C++ code, we should use a submodule for third parties if possible. We also want to keep the third party code seperate from our own source code so it should be placed in the ThirdParties folder of the project.
### 3. Never modify the third party
To help with maintainability, third parties should have no modified code in them. When it comes to updating the third party it should be as easy as replacing the current version with the new version.
#### </b> {#contributing}
To help with maintainability, third parties should have no modified code in them. When it comes to updating the third party it should be as easy as replacing the current version with the new version. If it is absolutely needed, we may choose to maintain a fork of a third party in another repo, however it is preferred to submit a PR to their repo instead

View File

@ -0,0 +1,8 @@
#version 330 core
out vec4 outColor;
void main()
{
outColor = vec4(0.0, 0.0, 0.0, 1.0);
}

View File

@ -0,0 +1,11 @@
#version 330 core
in vec3 position;
uniform mat4 u_view;
uniform mat4 u_projection;
void main()
{
gl_Position = u_projection * u_view * vec4(position, 1.0);
}

View File

@ -1,12 +1,52 @@
#version 330 core
in vec3 pass_UV;
in vec3 pass_normal;
flat in uint pass_color;
// in vec3 pass_pos;
uniform sampler2DArray u_TexArray;
uniform float u_AmbientStrength;
uniform vec3 u_LightDir;
uniform float u_Brightness;
out vec4 out_FragColor;
void main()
{
out_FragColor = texture(u_TexArray, pass_UV);
int color_i = int(pass_color);
/*
color_i = (binary) rrrrggggbbbbaaaa
r => bits needed for the red component
g => bits needed for the green component
b => bits needed for the blue component
a => bits needed for the alpha component
*/
const float lightIntensity = float((color_i & 15)) / 15.0;
vec4 objectColor = vec4(
float((color_i & 61440) >> 12) / 15.0,
float((color_i & 3840) >> 8 ) / 15.0,
float((color_i & 240) >> 4 ) / 15.0,
1.0
);
const vec3 lightColor = vec3(1.0, 1.0, 1.0);
// -- ambient lighting --
vec3 ambient = u_AmbientStrength * lightColor;
// -- diffuse lighting --
// assuming we don't need normalize(pass_normal) because
// vectors are pre normalized
vec3 norm = pass_normal;
vec3 lightDir = normalize(-u_LightDir);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;
// -- output color --
vec4 result = vec4(ambient, 1.0) + vec4(diffuse, 1.0) * objectColor * lightIntensity;
out_FragColor = result * texture(u_TexArray, pass_UV) * u_Brightness;
}

View File

@ -2,15 +2,25 @@
layout (location = 0) in vec3 a_Vertex;
layout (location = 1) in vec3 a_UV;
layout (location = 2) in vec3 a_Normal;
layout (location = 3) in uint a_Color;
// layout (location = 4) in vec3 a_Pos;
uniform mat4 u_model;
uniform mat4 u_view;
uniform mat4 u_projection;
out vec3 pass_UV;
out vec3 pass_normal;
flat out uint pass_color;
// out vec3 pass_pos;
void main()
{
gl_Position = u_projection * u_view * u_model * vec4(a_Vertex, 1.f); // don't need model right now either.
pass_UV = a_UV;
pass_normal = a_Normal;
pass_color = a_Color;
// pass_pos = a_Pos;
}

View File

@ -37,6 +37,8 @@
#include <Client/Player.hpp>
#include <Client/EscapeMenu.hpp>
#include <Common/CMS/ModManager.hpp>
#include <deque>
#include <enet/enet.h>
@ -88,6 +90,8 @@ namespace phx::client
ui::ChatWindow* m_chat = nullptr;
cms::ModManager* m_modManager;
EscapeMenu* m_escapeMenu = nullptr;
GameTools* m_gameDebug = nullptr;
bool m_followCam = true;

View File

@ -196,6 +196,9 @@ namespace phx::gfx
const int m_vertexAttributeLocation = 0;
const int m_uvAttributeLocation = 1;
const int m_normalAttributeLocation = 2;
const int m_colorAttributeLocation = 3;
// const int m_posAttributeLocation = 4;
AssociativeTextureTable m_textureTable;
};

View File

@ -32,6 +32,7 @@
#include <Client/Events/IEventListener.hpp>
#include <Common/Singleton.hpp>
#include <Common/CMS/ModManager.hpp>
#include <functional>
#include <string>
@ -64,10 +65,7 @@ namespace phx::client
InputMap();
~InputMap();
/**
* @brief Initializes the Lua API for the inputMap
*/
void initialize();
void registerAPI(cms::ModManager* manager);
/**
* @brief Forwards call to any matching registered callbacks

View File

@ -36,7 +36,11 @@
#pragma once
#include <Client/Graphics/ChunkView.hpp>
#include <Client/Graphics/ShaderPipeline.hpp>
#include <Common/CMS/ModManager.hpp>
#include <glad/glad.h>
#include <entt/entt.hpp>
namespace phx
@ -52,18 +56,25 @@ namespace phx
{
public:
// temporary until a proper management system is put in place.
explicit Player(voxels::ChunkView* world, entt::registry* registry);
explicit Player(entt::registry* registry);
void registerAPI(cms::ModManager* manager);
void setWorld(voxels::ChunkView* world);
math::Ray getTarget() const;
bool action1();
bool action2();
/// @brief Gets the entity for the player used in ECS
entt::entity getEntity() {return m_entity;};
entt::entity getEntity() { return m_entity; }
/// @brief Gets the direction something is facing based on its rotation
static math::vec3 rotToDir(math::vec3 m_rotation);
/// @brief Gets the direction something is facing based on its rotation
static math::vec3 rotToDir(math::vec3 m_rotation);
/// @brief Render the selection box around the pointed block
void renderSelectionBox(const math::mat4 view, const math::mat4 proj);
// don't forget to free the ressource afterward
char* getBitPackedState();
@ -73,5 +84,9 @@ namespace phx
voxels::ChunkView* m_world;
entt::registry* m_registry;
entt::entity m_entity;
GLuint m_vao;
GLuint m_vbo;
gfx::ShaderPipeline m_pipeline;
};
} // namespace phx

View File

@ -29,7 +29,9 @@
#include <Client/Client.hpp>
#include <Client/Game.hpp>
#include <Client/SplashScreen.hpp>
#include <cstring>
#include <Common/Logger.hpp>
#include <Common/Settings.hpp>
using namespace phx::client;
using namespace phx;
@ -116,6 +118,8 @@ void Client::onEvent(events::Event e)
void Client::run()
{
Logger::get()->initialize({});
Settings::get()->load("settings.txt");
SplashScreen* splashScreen = new SplashScreen();

View File

@ -30,14 +30,16 @@
#include <Client/Crosshair.hpp>
#include <Client/Game.hpp>
#include <Common/CMS/ModManager.hpp>
#include <Common/Serialization/Serializer.hpp>
#include <Common/Voxels/BlockRegistry.hpp>
#include <Common/Commander.hpp>
#include <Common/ContentLoader.hpp>
#include <Common/Position.hpp>
#include <Common/Actor.hpp>
#include <Common/Serialization/Serializer.hpp>
#include <Common/Commander.hpp>
#include <Common/Position.hpp>
#include <cmath>
using namespace phx::client;
using namespace phx;
@ -58,23 +60,37 @@ static void rawEcho(const std::string& input, std::ostringstream& cout)
}
Game::Game(gfx::Window* window, entt::registry* registry)
: Layer("Game"), m_window(window), m_registry(registry)
: Layer("Game"), m_window(window), m_registry(registry)
{
ContentManager::get()->lua["core"]["print"] =
/**
* @addtogroup luaapi
*
* @subsubsection coreprint core.print(text)
* @brief Prints text to the players terminal
*
* @param text The text to be outputted to the terminal
*
*/
[=](const std::string& text) { m_chat->cout << text << "\n"; };
const std::string save = "save1";
voxels::BlockRegistry::get()->initialise();
std::fstream fileStream;
std::vector<std::string> toLoad;
myGame = this;
fileStream.open("Saves/" + save + "/Mods.txt");
if (!fileStream.is_open())
{
std::cout << "Error opening save file";
exit(EXIT_FAILURE);
}
std::string input;
while (std::getline(fileStream, input))
{
toLoad.push_back(input);
}
m_modManager = new cms::ModManager(toLoad, {"Modules"});
voxels::BlockRegistry::get()->registerAPI(m_modManager);
m_modManager->registerFunction("core.print", [=](const std::string& text) {
m_chat->cout << text << "\n";
});
Settings::get()->registerAPI(m_modManager);
InputMap::get()->registerAPI(m_modManager);
CommandBook::get()->registerAPI(m_modManager);
}
Game::~Game() { delete m_chat; }
@ -127,23 +143,30 @@ void Game::onAttach()
/// @TODO replace with network callback
m_chat->registerCallback(rawEcho);
const std::string save = "save1";
m_player = new Player(m_registry);
m_player->registerAPI(m_modManager);
printf("%s", "Loading Modules\n");
if (!ContentManager::get()->loadModules(save))
float progress = 0.f;
auto result = m_modManager->load(&progress);
if (!result.ok)
{
signalRemoval();
LOG_FATAL("MODDING") << "An error has occured.";
exit(EXIT_FAILURE);
}
printf("%s", "Registering world\n");
printf("%s", "Registering world\n");
const std::string save = "save1";
m_world = new voxels::ChunkView(3, voxels::Map(save, "map1"));
m_player = new Player(m_world, m_registry);
m_player->setWorld(m_world);
m_camera = new gfx::FPSCamera(m_window, m_registry);
m_camera->setActor(m_player->getEntity());
m_registry->emplace<Hand>(m_player->getEntity(), voxels::BlockRegistry::get()->getFromRegistryID(0));
m_registry->emplace<Hand>(
m_player->getEntity(),
voxels::BlockRegistry::get()->getFromRegistryID(0));
printf("%s", "Prepare rendering\n");
printf("%s", "Prepare rendering\n");
m_renderPipeline.prepare("Assets/SimpleWorld.vert",
"Assets/SimpleWorld.frag",
gfx::ChunkRenderer::getRequiredShaderLayout());
@ -153,16 +176,17 @@ void Game::onAttach()
const math::mat4 model;
m_renderPipeline.setMatrix("u_model", model);
printf("%s", "Register GUI\n");
printf("%s", "Register GUI\n");
Client::get()->pushLayer(new Crosshair(m_window));
m_escapeMenu = new EscapeMenu(m_window);
if (Client::get()->isDebugLayerActive())
{
m_gameDebug = new GameTools(&m_followCam, &m_playerHand, m_player, m_registry);
m_gameDebug =
new GameTools(&m_followCam, &m_playerHand, m_player, m_registry);
Client::get()->pushLayer(m_gameDebug);
}
printf("%s", "Game layer attached");
printf("%s", "Game layer attached");
}
void Game::onDetach()
@ -212,13 +236,13 @@ void Game::onEvent(events::Event& e)
break;
case events::Keys::KEY_E:
m_playerHand++;
m_registry->get<Hand>(m_player->getEntity()).hand =
m_registry->get<Hand>(m_player->getEntity()).hand =
voxels::BlockRegistry::get()->getFromRegistryID(m_playerHand);
e.handled = true;
break;
case events::Keys::KEY_R:
m_playerHand--;
m_registry->get<Hand>(m_player->getEntity()).hand =
m_registry->get<Hand>(m_player->getEntity()).hand =
voxels::BlockRegistry::get()->getFromRegistryID(m_playerHand);
e.handled = true;
break;
@ -226,8 +250,8 @@ void Game::onEvent(events::Event& e)
if (Client::get()->isDebugLayerActive())
if (m_gameDebug == nullptr)
{
m_gameDebug =
new GameTools(&m_followCam, &m_playerHand, m_player, m_registry);
m_gameDebug = new GameTools(&m_followCam, &m_playerHand,
m_player, m_registry);
Client::get()->pushLayer(m_gameDebug);
}
else
@ -296,8 +320,16 @@ void Game::tick(float dt)
}
}
m_camera->tick(dt);
// temp, will change in the future, based on game time
static math::vec3 lightdir(0.f, -1.f, 0.f);
static float time = 0.f;
time += dt;
lightdir.y = std::sin(time);
lightdir.x = std::cos(time);
m_camera->tick(dt);
static std::size_t sequence;
sequence++;
@ -342,8 +374,12 @@ void Game::tick(float dt)
m_renderPipeline.activate();
m_renderPipeline.setMatrix("u_view", m_camera->calculateViewMatrix());
m_renderPipeline.setMatrix("u_projection", m_camera->getProjection());
m_renderPipeline.setFloat("u_AmbientStrength", 0.7f);
m_renderPipeline.setVector3("u_LightDir", lightdir);
m_renderPipeline.setFloat("u_Brightness", 0.6f);
m_world->render();
m_player->renderSelectionBox(m_camera->calculateViewMatrix(), m_camera->getProjection());
}
void Game::sendMessage(const std::string& input, std::ostringstream& cout)

View File

@ -173,6 +173,34 @@ void ChunkMesher::addBlockFace(voxels::BlockType* block, BlockFace face,
{
const std::size_t texLayer = m_texTable.at(block->textures[static_cast<std::size_t>(face)]);
math::vec3 normals;
switch (face)
{
case BlockFace::FRONT:
normals = math::vec3(0, 0, -1);
break;
case BlockFace::LEFT:
normals = math::vec3(-1, 0, 0);
break;
case BlockFace::BACK:
normals = math::vec3(0, 0, 1);
break;
case BlockFace::RIGHT:
normals = math::vec3(1, 0, 0);
break;
case BlockFace::TOP:
normals = math::vec3(0, 1, 0);
break;
case BlockFace::BOTTOM:
normals = math::vec3(0, -1, 0);
break;
}
for (int i = 0; i < NUM_VERTS_IN_FACE; ++i)
{
math::vec3 blockVertices = CUBE_VERTS[(static_cast<int>(face) * NUM_FACES_IN_CUBE) + i];
@ -190,6 +218,16 @@ void ChunkMesher::addBlockFace(voxels::BlockType* block, BlockFace face,
m_mesh.push_back(cubeUVs.y);
m_mesh.push_back(static_cast<float>(texLayer));
m_mesh.push_back(normals.x);
m_mesh.push_back(normals.y);
m_mesh.push_back(normals.z);
m_mesh.push_back(block->color);
// m_mesh.push_back(x);
// m_mesh.push_back(y);
// m_mesh.push_back(z);
}
}

View File

@ -49,6 +49,13 @@ struct Vertex
float u;
float v;
float tex;
float norm_x;
float norm_y;
float norm_z;
unsigned color;
// float pos_x;
// float pos_y;
// float pos_z;
};
ChunkRenderer::ChunkRenderer(const std::size_t visibleChunks)
@ -148,8 +155,23 @@ void ChunkRenderer::submitChunk(const std::vector<float>& mesh, math::vec3 pos)
sizeof(Vertex),
reinterpret_cast<void*>(offsetof(Vertex, u)));
glVertexAttribPointer(m_normalAttributeLocation, 3, GL_FLOAT, GL_TRUE,
sizeof(Vertex),
reinterpret_cast<void*>(offsetof(Vertex, norm_x)));
glVertexAttribPointer(m_colorAttributeLocation, 1, GL_UNSIGNED_INT, GL_FALSE,
sizeof(Vertex),
reinterpret_cast<void*>(offsetof(Vertex, color)));
// glVertexAttribPointer(m_posAttributeLocation, 3, GL_FLOAT, GL_FALSE,
// sizeof(Vertex),
// reinterpret_cast<void*>(offsetof(Vertex, pos_x)));
glEnableVertexAttribArray(m_vertexAttributeLocation);
glEnableVertexAttribArray(m_uvAttributeLocation);
glEnableVertexAttribArray(m_normalAttributeLocation);
glEnableVertexAttribArray(m_colorAttributeLocation);
// glEnableVertexAttribArray(m_posAttributeLocation);
m_buffers.insert({pos, {vao, buf, mesh.size()}});
}

View File

@ -28,8 +28,6 @@
#include <Client/InputMap.hpp>
#include <Common/ContentLoader.hpp>
#include <SDL.h>
using namespace phx::client;
@ -37,84 +35,34 @@ using namespace phx;
InputMap::InputMap()
{
ContentManager::get()->lua["core"]["input"] =
/**
* @addtogroup luaapi
*
* ---
* @subsection coreinput core.input
* @brief Interfaces with the player
*/
ContentManager::get()->lua.create_table();
ContentManager::get()->lua["core"]["input"]["registerInput"] =
/**
* @addtogroup luaapi
*
* @subsubsection coreplayerregisterinput
* core.player.registerInput(string uniqueName, string displayName,
* string defaultKey)
* @brief Registers a new input
* @param uniqueName the unique name for the input in the format
* "modname:inputname"
* @param displayName the display name that is shown to the user when
* binding keys
* @param defaultKey the default key that is first registered if the
* user does not set one
* @warning The default key is currently not functional, it will be set
* to 0 every time
*
* @return The input index (*Not functional at this time)
*
* @todo convert from string provided by lua to enum
*/
[this](std::string uniqueName, std::string displayName,
std::string defaultKey) {
registerInput(uniqueName, displayName, events::Keys::KEY_0);
return m_currentInputRef;
};
ContentManager::get()->lua["core"]["input"]["getInput"] =
/**
* @addtogroup luaapi
*
* @subsubsection coreplayergetInput core.player.getInput(int input)
* @brief Gets the state of the input
* @param input The reference of the input you are looking for
*
* @return The state of the input
*/
[this](int input) { return getState(input); };
ContentManager::get()->lua["core"]["input"]["getInputRef"] =
/**
* @addtogroup luaapi
*
* @subsubsection coreplayergetInputRef core.player.getInputRef(string
* uniqueName)
* @brief Gets the state of the input
* @param uniqueName the unique name used when registering the input
*
* @return The reference of the input
*/
[this](std::string uniqueName) { return getReference(uniqueName); };
ContentManager::get()->lua["core"]["input"]["registerCallback"] =
/**
* @addtogroup luaapi
*
* @subsubsection coreplayerregisterCallback
* core.player.getRegisterCallback(int input, function f)
* @brief Registers a callback to be called when an input event happens
* @param input The reference of the input you are looking for
* @param f The function that is called
*/
[this](int input, sol::function f) { attachCallbackToInput(input, f); };
m_uniqueInputs["core:none"] = m_currentInputRef;
m_inputs[m_currentInputRef] = {"None", "core:none"};
++m_currentInputRef;
}
InputMap::~InputMap() = default;
void InputMap::initialize()
void InputMap::registerAPI(cms::ModManager* manager)
{
m_uniqueInputs["core:none"] = m_currentInputRef;
m_inputs[m_currentInputRef] = {"None", "core:none"};
++m_currentInputRef;
manager->registerFunction(
"core.input.registerInput",
[this](std::string uniqueName, std::string displayName,
std::string defaultKey) {
registerInput(uniqueName, displayName, events::Keys::KEY_0);
return m_currentInputRef;
});
manager->registerFunction("core.input.getInput",
[this](int input) { return getState(input); });
manager->registerFunction(
"core.input.getInputRef",
[this](std::string uniqueName) { return getReference(uniqueName); });
manager->registerFunction("core.input.registerCallback",
[this](int input, sol::function f) {
attachCallbackToInput(input, f);
});
}
void InputMap::onEvent(events::Event e)

View File

@ -37,4 +37,3 @@ int main(int argc, char** argv)
return 0;
}

View File

@ -28,13 +28,12 @@
#include <Client/Player.hpp>
#include <Common/Voxels/BlockRegistry.hpp>
#include <Common/ContentLoader.hpp>
#include <Common/Commander.hpp>
#include <Common/Voxels/BlockRegistry.hpp>
#include <Common/Actor.hpp>
#include <Common/Movement.hpp>
#include <Common/Position.hpp>
#include <Common/Actor.hpp>
#include <cstring>
@ -42,97 +41,55 @@ using namespace phx;
static const float RAY_INCREMENT = 0.5f;
Player::Player(voxels::ChunkView* world, entt::registry* registry)
: m_world(world), m_registry(registry)
Player::Player(entt::registry* registry)
: m_registry(registry)
{
m_entity = m_registry->create();
m_registry->emplace<Position>( m_entity,
math::vec3{0, 0, 0}, math::vec3{0, 0, 0});
m_registry->emplace<Movement>(m_entity, DEFAULT_MOVE_SPEED);
m_entity = m_registry->create();
m_registry->emplace<Position>(m_entity, math::vec3 {0, 0, 0},
math::vec3 {0, 0, 0});
m_registry->emplace<Movement>(m_entity, DEFAULT_MOVE_SPEED);
ContentManager::get()->lua["core"]["player"] =
/**
* @addtogroup luaapi
*
* ---
* @subsection coreplayer core.player
* @brief Interfaces with the player
*/
ContentManager::get()->lua.create_table();
ContentManager::get()->lua["core"]["player"]["getSpeed"] =
/**
* @addtogroup luaapi
*
* @subsubsection coreplayergetspeed core.player.getSpeed()
* @brief Gets the players speed
*
* @return The players speed
*/
[this]() {
return m_registry->get<Movement>(m_entity).moveSpeed;
};
ContentManager::get()->lua["core"]["player"]["setSpeed"] =
/**
* @addtogroup luaapi
*
* @subsubsection coreplayersetspeed core.player.setSpeed(speed)
* @brief Sets the player speed
*
* @param key The new speed to set the player to
*
*/
[this](int speed) {
m_registry->get<Movement>(m_entity).moveSpeed = speed;
};
ContentManager::get()->lua["core"]["player"]["getPosition"] =
/**
* @addtogroup luaapi
*
* @subsubsection coreplayergetposition core.player.getPosition()
* @brief Gets the players position
*
* @return The players position in a table
*
* @par Example usage
*
* @code
* print(core.player.getPosition().x)
* print(core.player.getPosition().y)
* print(core.player.getPosition().z)
* @endcode
*/
[this]() {
sol::table pos = ContentManager::get()->lua.create_table();
pos["x"] = m_registry->get<Position>(m_entity).position.x;
pos["y"] = m_registry->get<Position>(m_entity).position.y;
pos["z"] = m_registry->get<Position>(m_entity).position.z;
return pos;
};
ContentManager::get()->lua["core"]["player"]["setPosition"] =
/**
* @addtogroup luaapi
*
* @subsubsection coreplayersetposition core.player.setPosition(position)
* @brief Sets the player's position
*
* @param posx The x component of the players position
* @param posy The y component of the players position
* @param posz The z component of the players position
*
*/
[this](int posx, int posy, int posz) {
m_registry->get<Position>(m_entity).position = {posx, posy, posz};
};
CommandBook::get()->add(
"tp", "Teleports player to supplied coordinates \n /tp <x> <y> <z>",
"all", [this](const std::vector<std::string>& args) {
m_registry->get<Position>(m_entity).position = {
std::stoi(args[0]), std::stoi(args[1]), std::stoi(args[2])};
});
CommandBook::get()->add("tp",
"Teleports player to supplied coordinates \n /tp <x> <y> <z>",
"all",
[this](const std::vector<std::string>& args){
m_registry->get<Position>(m_entity).position =
{std::stoi(args[0]), std::stoi(args[1]), std::stoi(args[2])};
});
glGenVertexArrays(1, &m_vao);
glGenBuffers(1, &m_vbo);
std::vector<gfx::ShaderLayout> layout;
layout.emplace_back("position", 0);
m_pipeline.prepare("Assets/SimpleLines.vert", "Assets/SimpleLines.frag", layout);
}
void Player::registerAPI(cms::ModManager* manager)
{
manager->registerFunction("core.player.getSpeed", [this]() {
return m_registry->get<Movement>(m_entity).moveSpeed;
});
manager->registerFunction("core.player.setSpeed", [this](int speed) {
m_registry->get<Movement>(m_entity).moveSpeed = speed;
});
manager->registerFunction("core.player.getPosition", [this]() {
sol::table pos;
pos["x"] = m_registry->get<Position>(m_entity).position.x;
pos["y"] = m_registry->get<Position>(m_entity).position.y;
pos["z"] = m_registry->get<Position>(m_entity).position.z;
return pos;
});
manager->registerFunction(
"core.player.setPosition", [this](int posx, int posy, int posz) {
m_registry->get<Position>(m_entity).position = {posx, posy, posz};
});
}
void Player::setWorld(voxels::ChunkView* world) { m_world = world; }
math::Ray Player::getTarget() const
{
math::vec3 pos = (m_registry->get<Position>(m_entity).position / 2.f) + .5f;
@ -193,8 +150,7 @@ bool Player::action2()
math::vec3 back = ray.backtrace(RAY_INCREMENT);
back.floor();
m_world->setBlockAt(
back, m_registry->get<Hand>(getEntity()).hand);
m_world->setBlockAt(back, m_registry->get<Hand>(getEntity()).hand);
return true;
}
@ -205,10 +161,89 @@ bool Player::action2()
return false;
}
math::vec3 Player::rotToDir(math::vec3 m_rotation){
return {std::cos(m_rotation.y) * std::sin(m_rotation.x),
std::sin(m_rotation.y),
std::cos(m_rotation.y) * std::cos(m_rotation.x)};
math::vec3 Player::rotToDir(math::vec3 m_rotation)
{
return {std::cos(m_rotation.y) * std::sin(m_rotation.x),
std::sin(m_rotation.y),
std::cos(m_rotation.y) * std::cos(m_rotation.x)};
}
void Player::renderSelectionBox(const math::mat4 view, const math::mat4 proj)
{
auto pos = getTarget().getCurrentPosition();
pos.floor();
// do not waste cpu time if we aren't targetting a solid block
if (m_world->getBlockAt(pos)->category != voxels::BlockCategory::SOLID)
return;
// voxel position to camera position
pos.x = (pos.x - 0.5f) * 2.f;
pos.y = (pos.y - 0.5f) * 2.f;
pos.z = (pos.z - 0.5f) * 2.f;
/*
1 +--------+ 2
/| /|
/ | 3 / |
0 +--------+ |
| |6 | |
| x-----|--+ 7
| / | /
|/ |/
5 +--------+ 4
*/
const float more = 2.001f;
const float less = 0.001f;
float vertices[] = {
pos.x + more, pos.y + more, pos.z - less, // 0-1
pos.x - less, pos.y + more, pos.z - less,
pos.x - less, pos.y + more, pos.z - less, // 1-2
pos.x - less, pos.y + more, pos.z + more,
pos.x - less, pos.y + more, pos.z + more, // 2-3
pos.x + more, pos.y + more, pos.z + more,
pos.x + more, pos.y + more, pos.z + more, // 3-4
pos.x + more, pos.y - less, pos.z + more,
pos.x + more, pos.y - less, pos.z + more, // 4-5
pos.x + more, pos.y - less, pos.z - less,
pos.x + more, pos.y - less, pos.z - less, // 5-6
pos.x - less, pos.y - less, pos.z - less,
pos.x - less, pos.y - less, pos.z - less, // 6-7
pos.x - less, pos.y - less, pos.z + more,
pos.x - less, pos.y - less, pos.z + more, // 7-4
pos.x + more, pos.y - less, pos.z + more,
pos.x - less, pos.y - less, pos.z + more, // 7-2
pos.x - less, pos.y + more, pos.z + more,
pos.x - less, pos.y + more, pos.z - less, // 1-6
pos.x - less, pos.y - less, pos.z - less,
pos.x + more, pos.y + more, pos.z - less, // 0-3
pos.x + more, pos.y + more, pos.z + more,
pos.x + more, pos.y + more, pos.z - less, // 0-5
pos.x + more, pos.y - less, pos.z - less
};
glBindVertexArray(m_vao);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
glEnableVertexAttribArray(0);
m_pipeline.activate();
m_pipeline.setMatrix("u_view", view);
m_pipeline.setMatrix("u_projection", proj);
glDrawArrays(GL_LINES, 0, 24);
}
char* Player::getBitPackedState()

View File

@ -0,0 +1,9 @@
set(currentDir ${CMAKE_CURRENT_LIST_DIR})
set(cmsHeaders
${currentDir}/Mod.hpp
${currentDir}/ModAPI.hpp
${currentDir}/ModManager.hpp
${currentDir}/ModManager.inl
PARENT_SCOPE
)

View File

@ -26,65 +26,65 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
/**
* @file ContentLoader.hpp
* @brief Implements Lua module loading system.
*
* @copyright Copyright (c) 2020 Genten Studios
*
*/
#pragma once
#include <Common/Singleton.hpp>
#include <sol/sol.hpp>
#include <vector>
#include <string>
#include <vector>
namespace phx
namespace phx::cms
{
/// @brief A lightweight struct to store a module and the list of
/// dependencies that module has.
struct Mod
{
/// @brief The name of the module, should match the folder name.
std::string name;
/// @brief List of dependencies that the module needs in order to load.
std::vector<std::string> dependencies;
/**
* @brief Constructor for mod object, folder matching mod name with a
* dependencies.txt inside modules folder must exist.
*/
explicit Mod(std::string name);
~Mod() = default;
};
class ContentManager : public Singleton<ContentManager>
/**
* @brief Class to encapsulate a "mod".
*
* This class is literally just to load dependencies and provide an easy way
* to get the name and path of the mod. It's tidier to store a vector of
* mods than a vector of names, paths, and dependencies.
*/
class Mod
{
public:
using Dependencies = std::vector<std::string>;
public:
Mod() = delete;
/**
* @brief Loads necessary lua modules required to load a save file.
* @brief Constructs a mod object and reads the dependency list.
* @param modName The name of the mod.
* @param modPath The path that the mod is found in.
*
* @param save The save file to be loaded.
* @return true If the function successfully loaded all modules.
* @return false If the function failed to load modules, details will
* be outputted to the terminal.
*
* @todo Add proper error handling instead of returning a boolean.
* Mod Path should be the path that the mod is found in, not the
* directory of the mod itself.
*/
bool loadModules(const std::string& save);
Mod(const std::string& modName, const std::string& modPath);
sol::state lua;
/**
* @brief Gets the name of the mod.
* @return The name of the mod.
*/
const std::string& getName() const;
std::string currentMod;
/**
* @brief Gets the parent folder of the mod.
* @return The path that the mod resides in.
*
* It will return the folder that the mod resides in, not the mod's
* folder itself. Combine with getName() to make a whole path.
*/
const std::string& getPath() const;
ContentManager();
/**
* @brief Gets the mod's dependencies.
* @return An array of dependencies for the mod.
*/
const Dependencies& getDependencies() const;
private:
std::string m_name;
std::string m_path;
Dependencies m_dependencies;
};
}; // namespace phx
} // namespace phx::mods

View File

@ -0,0 +1,98 @@
// Copyright 2019-20 Genten Studios
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include <functional>
#include <string>
#include <vector>
#include <unordered_map>
namespace phx::cms
{
// not documenting since we don't have an exact lifetime on the existance of
// these.
class Privileges
{
public:
// each player object stores this, list of all privileges they have.
using PrivList = std::vector<std::string>;
public:
Privileges() = default;
~Privileges() = default;
void registerPrivilege(const std::string& priv);
void privilegeExists(const std::string& priv) const;
const PrivList& getPrivileges() const { return m_privileges; }
///////////////////////
// utility functions //
///////////////////////
static bool hasPrivilege(const PrivList& privList,
const std::string& priv);
static PrivList parsePrivList(const std::string& commaDelimList);
private:
PrivList m_privileges;
};
class CommandBook
{
public:
using CommandFunction =
std::function<std::string(std::vector<std::string>)>;
struct Command
{
std::string help;
Privileges::PrivList privs;
CommandFunction func;
};
public:
CommandBook();
~CommandBook() = default;
// will allow for a command to work from multiple different privileges.
void registerCommand(const std::string& command,
const std::string& help,
const Privileges::PrivList& privileges,
const CommandFunction& func);
bool commandExists(const std::string& command) const;
const Command* getCommand(const std::string& command) const;
private:
// key is the actual command.
std::unordered_map<std::string, Command> m_commands;
};
} // namespace phx::mods

View File

@ -0,0 +1,143 @@
// Copyright 2019-20 Genten Studios
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include <Common/Logger.hpp>
#include <sol/sol.hpp>
#include <functional>
#include <queue>
#include <vector>
namespace phx::cms
{
/**
* @brief A class to handle all Mods that require loading.
*
* To be used in conjunction with saves. Each save stores a list of mods
* associated to the world stored.
*/
class ModManager
{
public:
/**
* @brief The status of the ModManager during load.
*
* This will help tell if there was an error while loading mods.
*/
struct Status
{
/// @brief Boolean saying whether the operation was ok or not.
bool ok;
/// @brief If the operation failed, the error message will be stored
/// in this.
std::string what;
};
using ModList = std::vector<std::string>;
public:
ModManager() = delete;
/**
* @brief Constructs the manager by initializing the Lua state.
* @param toLoad The list of mods to load.
* @param paths The list of directories a mod could be found in.
*
* The allowance for extra paths to be used makes sure that someone can
* have mods stored in different directories. This is especially helpful
* for developers since they can work on their mod and store it
* somewhere much more convenient for them while still being able to use
* it.
*/
explicit ModManager(const ModList& toLoad, const ModList& paths);
/**
* @brief Registers a C++ function into Lua.
* @tparam F The function signature for the function being registered.
* @param funcName The name the function should have in the Lua.
* @param func The function itself, can be a lambda or std::function.
*
* FuncName should be laid out like so:
* namespace.namespace.functionName
*
* Function hierarchies of greater than 3 sections are not allowed, so
* only two namespaces are allowed as of right now. For example:
* core.block.register(...)
*/
template <typename F>
void registerFunction(const std::string& funcName, F func);
// unimplemented function, exists as a reminder for when we need it.
template <typename T>
void exposeVariable(const std::string& name, T var);
/**
* @brief Loads the mods into the Lua state, ready for the game.
* @param The current progress of the loading.
* @return The status of the load, can dictate fail or success.
*/
Status load(float* progress);
/**
* @brief Cleans up mods once ready.
*
* Currently unimplemented, but will be implemented with a mod.cleanup
* function registered in lua for safely shutting down the game.
*/
void cleanup();
/**
* @brief Gets the list of mods being loaded.
* @return The list of loaded mods.
*/
const ModList& getModList() const;
/**
* @brief Gets the path of the currently loading mod.
* @return The path of the mod being currently loaded.
*
* This is useful when loading mods since some functions don't have
* information on the currently loading mod, making it difficult to use
* things like textures that are packaged with the mod.
*/
const std::string& getCurrentModPath() const;
private:
std::vector<std::string> m_modsRequired;
std::vector<std::string> m_modPaths;
std::string m_currentModPath;
sol::state m_luaState;
};
} // namespace phx::cms
#include "ModManager.inl"

View File

@ -0,0 +1,99 @@
// Copyright 2019-20 Genten Studios
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#pragma once
// Header exists in ModManager.hpp
namespace phx::cms
{
// can do template<typename RtnType, typename... Args>
// but allowing for anything makes sure you can capture lambdas.
template <typename F>
void ModManager::registerFunction(const std::string& funcName, F func)
{
// splitting the function name by periods.
// something like core.block.register would be broken down into
// core, block and register. core and block need to be created as
// Lua tables, however register is just set to the function since
// it's the actual name of it.
std::vector<std::string> branches;
std::stringstream sstream(funcName);
std::string substr;
while (std::getline(sstream, substr, '.'))
{
branches.push_back(substr);
}
// malformed request.
if (branches.empty())
return;
// limiting to 3 levels of hierarchy for a v1. Otherwise deep
// infinite recursion doesn't work.
if (branches.size() == 1)
{
m_luaState[branches[0]] = func;
return;
}
if (branches.size() == 2)
{
if (!m_luaState[branches[0]].valid())
{
m_luaState[branches[0]] = m_luaState.create_table();
}
m_luaState[branches[0]][branches[1]] = func;
}
if (branches.size() == 3)
{
if (!m_luaState[branches[0]].valid())
{
m_luaState[branches[0]] = m_luaState.create_table();
}
if (!m_luaState[branches[0]][branches[1]].valid())
{
m_luaState[branches[0]][branches[1]] =
m_luaState.create_table();
}
m_luaState[branches[0]][branches[1]][branches[2]] = func;
}
if (branches.size() > 3)
{
LOG_WARNING("MODDING")
<< "Function hierarchies of more than 3 are not currently "
"supported. Please limit function names to the depth of "
"\"core.block.register\".";
}
}
} // namespace phx::mods

View File

@ -1,23 +1,23 @@
add_subdirectory(Math)
add_subdirectory(Voxels)
add_subdirectory(CMS)
set(currentDir ${CMAKE_CURRENT_LIST_DIR})
set(Headers
${mathHeaders}
${voxelHeaders}
${mathHeaders}
${voxelHeaders}
${cmsHeaders}
${currentDir}/CoreIntrinsics.hpp
${currentDir}/EnumTools.hpp
${currentDir}/Singleton.hpp
${currentDir}/FileIO.hpp
${currentDir}/Logger.hpp
${currentDir}/CoreIntrinsics.hpp
${currentDir}/EnumTools.hpp
${currentDir}/Singleton.hpp
${currentDir}/FileIO.hpp
${currentDir}/Logger.hpp
${currentDir}/ContentLoader.hpp
${currentDir}/Settings.hpp
${currentDir}/Commander.hpp
${currentDir}/Position.hpp
${currentDir}/Movement.hpp
${currentDir}/Input.hpp
${currentDir}/Settings.hpp
${currentDir}/Commander.hpp
${currentDir}/Position.hpp
${currentDir}/Input.hpp
PARENT_SCOPE
)
PARENT_SCOPE
)

View File

@ -38,6 +38,7 @@
#pragma once
#include <Common/Singleton.hpp>
#include <Common/CMS/ModManager.hpp>
#include <functional>
#include <ostream>
@ -68,7 +69,10 @@ namespace phx
std::vector<std::string> m_permission;
std::vector<CommandFunction> m_functions;
CommandBook();
CommandBook() = default;
~CommandBook() = default;
void registerAPI(cms::ModManager* manager);
/**
* @brief Registers a command in the command registry.

View File

@ -38,7 +38,7 @@
#include <utility>
#define LOGGER_INTERNAL(verbosity, component) \
if (phx::Logger::get() == nullptr && \
if (phx::Logger::get() == nullptr || \
verbosity > phx::Logger::get()->getVerbosity()) \
{ \
; \

View File

@ -37,6 +37,7 @@
#pragma once
#include <Common/Singleton.hpp>
#include <Common/CMS/ModManager.hpp>
#include <string>
#include <unordered_map>
@ -88,7 +89,7 @@ namespace phx
* @param defaultValue The default value for the setting upon creation.
*/
Setting(std::string name, std::string key, int defaultValue, json* json_);
/**
* @brief Sets the value of an already existing setting.
*
@ -153,6 +154,8 @@ namespace phx
public:
Settings();
void registerAPI(cms::ModManager* manager);
/**
* @brief Adds a new setting.
*

View File

@ -101,6 +101,9 @@ namespace phx::voxels
/// @brief Callback for when the block is interacted with.
BlockCallback onInteract;
/// @brief (temporary) a bitpacked color: rrrrggggbbbbaaaa (a letter = 1 bit)
unsigned color = 0b1111111111111111;
/**
* @brief An array of texture paths used for rendering the block.
*

View File

@ -31,6 +31,7 @@
#include <Common/Singleton.hpp>
#include <Common/Voxels/Block.hpp>
#include <Common/Voxels/TextureRegistry.hpp>
#include <Common/CMS/ModManager.hpp>
#include <vector>
@ -62,11 +63,11 @@ namespace phx::voxels
class BlockRegistry : public Singleton<BlockRegistry>
{
public:
/// @brief Registers respective Lua methods.
BlockRegistry();
~BlockRegistry() = default;
// @brief Initializes the registry.
void initialise();
void registerAPI(cms::ModManager* manager);
/**
* @brief Registers a block in the registry.

View File

@ -0,0 +1,8 @@
set(currentDir ${CMAKE_CURRENT_LIST_DIR})
set(cmsSources
${currentDir}/Mod.cpp
${currentDir}/ModAPI.cpp
${currentDir}/ModManager.cpp
PARENT_SCOPE
)

View File

@ -0,0 +1,69 @@
// Copyright 2019-20 Genten Studios
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <Common/Logger.hpp>
#include <Common/CMS/Mod.hpp>
#include <fstream>
using namespace phx::cms;
Mod::Mod(const std::string& modName, const std::string& modPath)
{
m_name = modName;
m_path = modPath;
{
std::fstream deps;
deps.open(m_path + "/" + modName + "/Dependencies.txt");
if (!deps.is_open())
{
LOG_WARNING("MODDING")
<< "Mod " << modName << " does not include a Dependencies.txt!";
// assuming no deps, even if it causes failure further down.
return;
}
std::string input;
while (std::getline(deps, input))
{
if (!input.empty())
{
m_dependencies.push_back(input);
}
}
deps.close();
}
}
const Mod::Dependencies& Mod::getDependencies() const { return m_dependencies; }
const std::string& Mod::getPath() const { return m_path; }
const std::string& Mod::getName() const { return m_name; }

View File

@ -0,0 +1,156 @@
// Copyright 2019-20 Genten Studios
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <Common/Logger.hpp>
#include <Common/CMS/ModAPI.hpp>
#include <algorithm>
#include <sstream>
using namespace phx::cms;
void Privileges::registerPrivilege(const std::string& priv)
{
const auto it = std::find(m_privileges.begin(), m_privileges.end(), priv);
if (it == m_privileges.end())
{
m_privileges.push_back(priv);
}
}
Privileges::PrivList Privileges::parsePrivList(
const std::string& commaDelimList)
{
// split comma delimited list into array.
PrivList privs;
std::stringstream sstream(commaDelimList);
std::string substr;
while (std::getline(sstream, substr, ','))
{
privs.push_back(substr);
}
return privs;
}
bool Privileges::hasPrivilege(const PrivList& privList, const std::string& priv)
{
if (priv.length() == 0)
{
return true;
}
// search array for privilege.
const auto it = std::find(privList.begin(), privList.end(), priv);
if (it == privList.end())
{
return false;
}
return true;
}
CommandBook::CommandBook()
{
m_commands["help"] = {"Type /help [command] to learn more about a command.",
{},
[=](std::vector<std::string> args) {
if (args.empty())
{
return m_commands["help"].help;
}
return m_commands[args[0]].help;
}};
m_commands["list"] = {
"Type /list to list every command, use a privilege as a parameter to "
"see what commands are available to that role.",
{},
[=](std::vector<std::string> args) {
if (args.empty())
{
std::string commands;
for (auto& command : m_commands)
{
commands += command.first + "\n";
}
return commands;
}
std::string commands;
for (auto& command : m_commands)
{
if (Privileges::hasPrivilege(command.second.privs, args[0]))
{
commands += command.first + "\n";
}
}
return commands;
}};
m_commands["unknown"] = {
"Unknown command", {}, [](std::vector<std::string> args) {
return "An unknown command has been called";
}};
}
void CommandBook::registerCommand(const std::string& command,
const std::string& help,
const Privileges::PrivList& privilege,
const CommandFunction& func)
{
auto it = m_commands.find(command);
if (it == m_commands.end())
{
LOG_INFO("[MODDING]")
<< "The command \" " << command
<< "\" has already been defined by another mod, the formally "
"registered command shall be overwritten.";
}
m_commands[command] = {help, privilege, func};
}
bool CommandBook::commandExists(const std::string& command) const
{
return m_commands.find(command) != m_commands.end();
}
const CommandBook::Command* CommandBook::getCommand(const std::string& command) const
{
auto it = m_commands.find(command);
if (it == m_commands.end())
{
return &m_commands.at("unknown");
}
return &(it->second);
}

View File

@ -0,0 +1,173 @@
// Copyright 2019-20 Genten Studios
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <Common/CMS/ModManager.hpp>
#include <Common/CMS/Mod.hpp>
#include <Common/Logger.hpp>
using namespace phx::cms;
static void CustomPanicHandler(sol::optional<std::string> maybe_msg)
{
// \n for newline, \t for tab.
std::string error;
if (maybe_msg)
{
error += "\n\t";
error += maybe_msg.value();
error += "\n";
}
LOG_FATAL("MODDING") << "An unexpected Lua error has occured. " << error
<< "The application will now be aborted.";
}
ModManager::ModManager(const ModList& toLoad, const ModList& paths)
: m_modsRequired(toLoad), m_modPaths(paths)
{
m_luaState.open_libraries(sol::lib::base);
m_luaState.set_panic(
sol::c_call<decltype(&CustomPanicHandler), &CustomPanicHandler>);
}
ModManager::Status ModManager::load(float* progress)
{
std::queue<Mod> toLoad;
for (auto& require : m_modsRequired)
{
bool found = false;
for (auto& path : m_modPaths)
{
std::fstream file;
file.open(path + "/" + require + "/Init.lua");
if (file.is_open())
{
found = true;
toLoad.emplace(require, path);
break;
}
}
if (!found)
{
std::string pathList = "\n\t";
for (auto& path : m_modPaths)
{
pathList += path;
pathList += "\n\t";
}
LOG_FATAL("MODDING")
<< "The mod: " << require
<< " was not found in any of the provided mod directories: "
<< pathList;
return {false, "The mod: " + require + " was not found."};
}
}
std::vector<std::string> loadedMods;
while (!toLoad.empty())
{
std::size_t lastPass = toLoad.size();
for (std::size_t i = 0; i < toLoad.size(); ++i)
{
Mod& mod = toLoad.front();
bool satisfied = true;
for (auto& dependency : mod.getDependencies())
{
if (std::find(loadedMods.begin(), loadedMods.end(),
dependency) == loadedMods.end())
{
satisfied = false;
}
}
if (satisfied)
{
m_currentModPath = mod.getPath() + "/" + mod.getName() + "/";
sol::protected_function_result pfr =
m_luaState.safe_script_file(m_currentModPath + "Init.lua",
&sol::script_pass_on_error);
loadedMods.push_back(mod.getName());
// error occured if return is not valid.
if (!pfr.valid())
{
sol::error err = pfr;
std::string errString = "An error occured loading ";
errString += mod.getName();
errString += ": ";
errString += err.what();
LOG_FATAL("MODDING") << errString;
return {false, errString};
}
}
else
{
toLoad.push(toLoad.front());
}
toLoad.pop();
}
if (lastPass == toLoad.size())
{
std::string err = "The mod: ";
err += toLoad.front().getName();
err += " is missing one or more dependencies, please resolve this "
"issue before continuing.";
LOG_FATAL("MODDING") << err;
return {false, err};
}
}
// no need to put in something for "what", since nothing went wrong.
return {true};
}
void ModManager::cleanup() {}
const ModManager::ModList& ModManager::getModList() const
{
return m_modsRequired;
}
const std::string& ModManager::getCurrentModPath() const
{
return m_currentModPath;
}

View File

@ -1,19 +1,19 @@
add_subdirectory(Math)
add_subdirectory(Voxels)
add_subdirectory(CMS)
set(currentDir ${CMAKE_CURRENT_LIST_DIR})
set(Sources
${mathSources}
${voxelSources}
${mathSources}
${voxelSources}
${cmsSources}
${currentDir}/Actor.cpp
${currentDir}/ContentLoader.cpp
${currentDir}/Settings.cpp
${currentDir}/Logger.cpp
${currentDir}/Commander.cpp
${currentDir}/Input.cpp
${currentDir}/Serialization/Serializer.cpp
PARENT_SCOPE
)
PARENT_SCOPE
)

View File

@ -36,7 +36,6 @@
*/
#include <Common/Commander.hpp>
#include <Common/ContentLoader.hpp>
using namespace phx;
@ -72,54 +71,13 @@ void CommandBook::add(const std::string& command, const std::string& help,
int CommandBook::getPage() { return m_page; }
CommandBook::CommandBook()
void CommandBook::registerAPI(cms::ModManager* manager)
{
ContentManager::get()->lua["core"]["command"] =
/**
* @addtogroup luaapi
*
* ---
* @subsection corecmd core.command
* @brief Interfaces with the commander
*
*/
ContentManager::get()->lua.create_table();
ContentManager::get()->lua["core"]["command"]["register"] =
/**
* @addtogroup luaapi
*
* @subsubsection corecmdreg core.command.register
* @brief Registers a new command
*
* In the terminal typing "/" followed by a command will execute the
*command
*
* @param command The command to register
* @param help A helpstring that is printed to terminal when typing
*"/help <command>"
* @param f The callback function that is called by the commander
* The callback function must take a table as an argument
* Any arguments included when the command is executed will be passed in
*this table
*
* @b Example:
* @code {.lua}
* function hello (args)
* if args[1] == "there" then
* print("General Kenobi")
* elseif args[1] == "world" then
* print("World says hi")
* else
* print("with you, the force is not")
* end
* end
* core.command.register("Hello", "Master the arts of the Jedi you
*must", hello)
* @endcode
*/
manager->registerFunction(
"core.command.register",
[](std::string command, std::string help, sol::function f) {
CommandBook::get()->add(command, help, "all", f);
};
});
}
bool Commander::help(const std::vector<std::string>& args, std::ostream& out)
@ -282,4 +240,3 @@ void Commander::callback(const std::string& input, std::ostringstream& cout)
run(command, args, cout);
}
}

View File

@ -1,161 +0,0 @@
// Copyright 2019-20 Genten Studios
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <fstream>
#include <iostream>
#include <queue>
#include <Common/ContentLoader.hpp>
using namespace phx;
ContentManager::ContentManager()
{
currentMod = "";
lua.open_libraries(sol::lib::base);
/**
* @defgroup luaapi Lua API
*
* The lua API is . . .
*
* @tableofcontents
*
*/
lua["core"] = lua.create_table();
/**
* @addtogroup luaapi
*
* ---
* ---
* @section core core
* @brief The core API for interacting with Quartz
*/
}
Mod::Mod(std::string modName) : name(std::move(modName))
{
std::fstream fileStream;
fileStream.open("Modules/" + name + "/Dependencies.txt");
if (!fileStream.is_open())
{
std::cout << "Couldn't find dependencies file for mod: " << name
<< "\n";
return;
}
while (fileStream.peek() != EOF)
{
std::string input;
std::getline(fileStream, input);
dependencies.push_back(input);
}
fileStream.close();
}
bool ContentManager::loadModules(const std::string& save)
{
std::fstream fileStream;
std::queue<Mod> toLoad; // A queue of mods that need loaded
fileStream.open("Saves/" + save + "/Mods.txt");
if (!fileStream.is_open())
{
std::cout << "Error opening save file";
return false;
}
for (int i = 0; fileStream.peek() != EOF && i <= 10; i++)
{
std::string input;
std::getline(fileStream, input);
Mod mod = Mod(input);
toLoad.push(mod);
}
fileStream.close();
// Sort and load the mods
std::vector<std::string> loadedMods;
while (!toLoad.empty())
{
int lastPass = toLoad.size();
// For each mod that needs loaded
for (int i = 0; i < toLoad.size(); i++)
{
Mod mod = toLoad.front();
bool satisfied = true;
// For each dependency the mod has
for (const auto& dependency : mod.dependencies)
{
// If dependency is not satisfied, mark satisfied as false.
if (std::find(loadedMods.begin(), loadedMods.end(),
dependency) == loadedMods.end())
{
satisfied = false;
}
}
// If all dependencies were met, run lua and add mod to satisfied
// list Otherwise, move mod to back of load queue
if (satisfied)
{
currentMod = mod.name;
lua.script_file("Modules/" + mod.name + "/Init.lua");
loadedMods.push_back(mod.name);
}
else
{
toLoad.push(toLoad.front());
}
toLoad.pop();
}
// If we haven't loaded any new mods, throw error
if (lastPass == toLoad.size())
{
// TODO: Replace this with actuall error handling/ logging
std::cout
<< "One or more mods are missing required dependencies. \n";
std::cout << "Failed Mods:\n";
for (int i = 0; i < toLoad.size(); i++)
{
std::cout << "- " + toLoad.front().name + "\n";
toLoad.pop();
}
std::cout << "Loaded Mods:\n";
for (const auto& loadedMod : loadedMods)
{
std::cout << "- " + loadedMod + "\n";
}
return false;
}
}
return true;
}

View File

@ -26,17 +26,17 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <Common/ContentLoader.hpp>
#include <Common/Settings.hpp>
#include <climits>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <iostream>
using namespace phx;
Setting::Setting(std::string name, std::string key, int defaultValue, json* json_)
Setting::Setting(std::string name, std::string key, int defaultValue,
json* json_)
: m_name(std::move(name)), m_key(std::move(key)), m_value(defaultValue),
m_maxValue(SHRT_MAX), m_minValue(SHRT_MIN), m_default(defaultValue),
m_json(json_)
@ -47,7 +47,7 @@ bool Setting::set(int value)
{
if (value >= m_minValue && value <= m_maxValue)
{
m_value = value;
m_value = value;
(*m_json)[m_key] = value;
return true;
}
@ -66,68 +66,24 @@ int Setting::value() const { return m_value; }
int Setting::getDefault() const { return m_default; }
Settings::Settings() :
m_data(json::object())
Settings::Settings() : m_data(json::object()) {}
void Settings::registerAPI(cms::ModManager* manager)
{
ContentManager::get()->lua["core"]["setting"] =
/**
* @addtogroup luaapi
*
* ---
* @subsection coreset core.setting
* @brief Interfaces with the settings system
*/
ContentManager::get()->lua.create_table();
ContentManager::get()->lua["core"]["setting"]["register"] =
/**
* @addtogroup luaapi
*
* @subsubsection coresetreg core.setting.register(displayName, key,
* defaultValue)
* @brief Registers a setting that the player can adjust via the
* settings menu
*
* @param displayName The Display name for the setting seen in the
* settings menu
* @param key The unique key for the setting, usually in the form
* module:setting
* @param defaultValue The default value for the setting if not already
* set
*
*/
manager->registerFunction(
"core.setting.register",
[](std::string displayName, std::string key, int defaultValue) {
Settings::get()->add(displayName, key, defaultValue);
};
ContentManager::get()->lua["core"]["setting"]["get"] =
/**
* @addtogroup luaapi
*
* @subsubsection coresetget core.setting.get(key)
* @brief Gets the value of a setting based on its unique key
*
* @param key The unique key for the setting, usually in the form
* module:setting
* @return The integer value of the setting
*
*/
[](std::string key) {
return Settings::get()->getSetting(key)->value();
};
ContentManager::get()->lua["core"]["setting"]["set"] =
/**
* @addtogroup luaapi
*
* @subsubsection coresetset core.setting.set(key)
* @brief Sets the value of a setting based on its unique key
*
* @param key The unique key for the setting, usually in the form
* module:setting
* @param value The value the setting should be set to
*
*/
[](std::string key, int value) {
Settings::get()->getSetting(key)->set(value);
};
});
manager->registerFunction("core.setting.get", [](std::string key) {
return Settings::get()->getSetting(key)->value();
});
manager->registerFunction("core.setting.set",
[](std::string key, int value) {
Settings::get()->getSetting(key)->set(value);
});
}
Setting* Settings::add(const std::string& name, const std::string& key,
@ -172,4 +128,3 @@ void Settings::save(const std::string& saveFile)
file << std::setw(4) << m_data << std::endl;
file.close();
}

View File

@ -26,77 +26,34 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <Common/ContentLoader.hpp>
#include <Common/Voxels/BlockRegistry.hpp>
#include <algorithm>
#include <cstring>
using namespace phx::voxels;
using namespace phx;
BlockRegistry::BlockRegistry()
{
ContentManager::get()->lua["voxel"] =
ContentManager::get()->lua.create_table();
/**
* @addtogroup luaapi
*
* ---
* ---
* @section voxel voxel
* @brief The voxel API for interacting voxels
*/
ContentManager::get()->lua["voxel"]["block"] =
ContentManager::get()->lua.create_table();
/**
* @addtogroup luaapi
*
* ---
* @subsection voxelblock voxel.block
* @brief Interfaces with blocks
*/
ContentManager::get()->lua["voxel"]["block"]["register"] =
/**
* @addtogroup luaapi
*
* @subsubsection voxelblockreg voxel.block.register(luaBlock)
* @brief Sets the value of a setting based on its unique key
*
* @param luaBlock a table storing the data used to create a block
*
* @details The luaBlock table stores the following values: \n
* Required:
* - @b name: The display name for the block
* - @b id: The unique id for the block usually in the form module:block
*
* Optional:
* - @b category: The category of the block chosen from "Air", "Liquid",
* or "Solid" \n If not specified, the default is "Solid"
* - @b onPlace: A function that is run when the block is placed
* - @b onBreak: A function that is run when the block is broken
* - @b onInteract: A function that is run when the block is interacted
* with
* - @b textures: A table of filepaths where textures are located \n
* Filepaths are relative to to the module directory \n
* If only one filepath is specified, that will be used for
* all textures
*
* @b Example:
*
* @code {.lua}
* block = {}
* block.name = "Grass"
* block.id = "core.grass"
* block.textures = {"Assets/grass_side.png", "Assets/grass_side.png",
* "Assets/grass_side.png", "Assets/grass_side.png",
* "Assets/grass_top.png", "Assets/dirt.png"}
* block.onBreak = function (position)
* print("grass broken at" + position)
* end
* voxel.block.register(block)
* @endcode
*/
[](sol::table luaBlock) {
BlockType unknownBlock;
unknownBlock.displayName = "Unknown Block";
unknownBlock.id = "core.unknown";
unknownBlock.category = BlockCategory::SOLID;
unknownBlock.setAllTextures("Assets/unknown.png");
registerBlock(unknownBlock);
BlockType outOfBoundsBlock;
outOfBoundsBlock.displayName = "Out Of Bounds";
outOfBoundsBlock.id = "core.out_of_bounds";
outOfBoundsBlock.category = BlockCategory::AIR;
registerBlock(outOfBoundsBlock);
}
void BlockRegistry::registerAPI(cms::ModManager* manager)
{
manager->registerFunction(
"voxel.block.register", [manager](sol::table luaBlock) {
BlockType block;
{
block.displayName = luaBlock["name"];
@ -143,31 +100,14 @@ BlockRegistry::BlockRegistry()
// texture in its place
texture = luaBlock["textures"][1];
}
textures[i] = "Modules/" +
ContentManager::get()->currentMod + "/" +
texture;
textures[i] = manager->getCurrentModPath() + texture;
}
block.textures = textures;
block.textures = textures;
}
}
BlockRegistry::get()->registerBlock(block);
};
}
void BlockRegistry::initialise()
{
BlockType unknownBlock;
unknownBlock.displayName = "Unknown Block";
unknownBlock.id = "core.unknown";
unknownBlock.category = BlockCategory::SOLID;
unknownBlock.setAllTextures("Assets/unknown.png");
registerBlock(unknownBlock);
BlockType outOfBoundsBlock;
outOfBoundsBlock.displayName = "Out Of Bounds";
outOfBoundsBlock.id = "core.out_of_bounds";
outOfBoundsBlock.category = BlockCategory::AIR;
registerBlock(outOfBoundsBlock);
});
}
void BlockRegistry::registerBlock(BlockType blockInfo)
@ -210,4 +150,3 @@ BlockType* BlockRegistry::getFromRegistryID(std::size_t registryID)
}
TextureRegistry* BlockRegistry::getTextures() { return &m_textures; }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -1,3 +1,3 @@
mod1
mod3
mod2
mod2
mod3