Merge pull request #216 from GentenStudios/feat-mods

Improve CMS through enhanced error handling.
develop
Toby plowy (Toby109tt) 2020-04-27 23:18:16 +02:00 committed by GitHub
commit b5e0e267a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1036 additions and 654 deletions

View File

@ -37,6 +37,8 @@
#include <Client/Player.hpp>
#include <Client/EscapeMenu.hpp>
#include <Common/CMS/ModManager.hpp>
namespace phx::client
{
/**
@ -77,6 +79,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

@ -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,10 +36,11 @@
#pragma once
#include <Client/Graphics/ChunkView.hpp>
#include <glad/glad.h>
#include <Client/Graphics/ShaderPipeline.hpp>
#include <Common/CMS/ModManager.hpp>
#include <glad/glad.h>
#include <entt/entt.hpp>
namespace phx
@ -55,18 +56,22 @@ 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);

View File

@ -30,8 +30,7 @@
#include <Client/Game.hpp>
#include <Client/SplashScreen.hpp>
#include <Common/Commander.hpp>
#include <Common/ContentLoader.hpp>
#include <Common/Logger.hpp>
#include <Common/Settings.hpp>
using namespace phx::client;
@ -119,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,13 +30,11 @@
#include <Client/Crosshair.hpp>
#include <Client/Game.hpp>
#include <Common/Voxels/BlockRegistry.hpp>
#include <Common/Commander.hpp>
#include <Common/ContentLoader.hpp>
#include <Common/Position.hpp>
#include <Common/Actor.hpp>
#include <Common/Commander.hpp>
#include <Common/CMS/ModManager.hpp>
#include <Common/Position.hpp>
#include <Common/Voxels/BlockRegistry.hpp>
#include <cmath>
@ -51,51 +49,74 @@ 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;
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; }
void Game::onAttach()
{
/// @todo Replace this with logger
printf("%s", "Attaching game layer\n");
/// @todo Replace this with logger
printf("%s", "Attaching game layer\n");
m_chat = new ui::ChatWindow("Chat Window", 5,
"Type /help for a command list and help.");
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());
@ -105,16 +126,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()
@ -149,13 +171,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;
@ -163,8 +185,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

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)
@ -142,8 +90,9 @@ Input* InputMap::registerInput(const std::string& uniqueName,
const std::string& displayName,
events::Keys defaultKey)
{
m_uniqueInputs[uniqueName] = m_currentInputRef;
m_inputs[m_currentInputRef] = {displayName, uniqueName, defaultKey, defaultKey};
m_uniqueInputs[uniqueName] = m_currentInputRef;
m_inputs[m_currentInputRef] = {displayName, uniqueName, defaultKey,
defaultKey};
++m_currentInputRef;
return &m_inputs[m_currentInputRef - 1];
}
@ -194,8 +143,7 @@ bool InputMap::getState(InputRef primaryKey)
bool InputMap::getState(Input* input)
{
return SDL_GetKeyboardState(
nullptr)[static_cast<SDL_Scancode>(input->key)];
return SDL_GetKeyboardState(nullptr)[static_cast<SDL_Scancode>(input->key)];
}
InputMap::InputRef InputMap::getReference(const std::string& uniqueName)

View File

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

View File

@ -28,116 +28,66 @@
#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>
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);
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);
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])};
});
}
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;
@ -198,8 +148,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;
}
@ -210,10 +159,11 @@ 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)

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,22 +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}/Settings.hpp
${currentDir}/Commander.hpp
${currentDir}/Position.hpp
${currentDir}/Movement.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

@ -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,15 +1,16 @@
add_subdirectory(Math)
add_subdirectory(Voxels)
add_subdirectory(CMS)
set(currentDir ${CMAKE_CURRENT_LIST_DIR})
set(Sources
${mathSources}
${voxelSources}
${mathSources}
${voxelSources}
${cmsSources}
${currentDir}/ContentLoader.cpp
${currentDir}/Settings.cpp
${currentDir}/Logger.cpp
${currentDir}/Commander.cpp
${currentDir}/Settings.cpp
${currentDir}/Logger.cpp
${currentDir}/Commander.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; }