Browse Source

Add logger and support for querying CLI variables

master
C. J. Howard 5 years ago
parent
commit
6172c221cd
Signed by: cjhoward GPG Key ID: 03E1FABA9C3EC195
6 changed files with 191 additions and 155 deletions
  1. +11
    -21
      src/debug/command-interpreter.cpp
  2. +10
    -4
      src/debug/command-interpreter.hpp
  3. +43
    -0
      src/debug/logger.cpp
  4. +48
    -0
      src/debug/logger.hpp
  5. +73
    -123
      src/game.cpp
  6. +6
    -7
      src/game.hpp

src/debug/console.cpp → src/debug/command-interpreter.cpp View File

@ -17,7 +17,7 @@
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "console.hpp"
#include "command-interpreter.hpp"
#include <sstream>
template<>
@ -64,15 +64,15 @@ std::string ArgumentParser::parse(const std::string& argument)
void CommandInterpreter::set(const std::string& name, const std::string& value)
{
variables[name] = value;
variableMap[name] = value;
}
void CommandInterpreter::unset(const std::string& name)
{
auto it = variables.find(name);
if (it != variables.end())
auto it = variableMap.find(name);
if (it != variableMap.end())
{
variables.erase(it);
variableMap.erase(it);
}
}
@ -81,6 +81,11 @@ const std::map& CommandInterpreter::help() const
return helpStrings;
}
const std::map<std::string, std::string>& CommandInterpreter::variables() const
{
return variableMap;
}
std::tuple<std::string, std::vector<std::string>, std::function<void()>> CommandInterpreter::interpret(const std::string& line)
{
// Split line into arguments
@ -103,7 +108,7 @@ std::tuple, std::function> Command
if (!argument.empty() && argument[0] == '$')
{
std::string variableName = argument.substr(1);
std::string variableValue = variables[variableName];
std::string variableValue = variableMap[variableName];
argument = variableValue;
}
}
@ -114,21 +119,6 @@ std::tuple, std::function> Command
// Remove command name from arguments
arguments.erase(arguments.begin());
// Check command name for member access operator '.'
std::size_t dotOperatorPosition = commandName.find('.');
if (dotOperatorPosition != std::string::npos)
{
// Get variable name and lookup value
std::string variableName = commandName.substr(0, dotOperatorPosition);
std::string variableValue = variables[variableName];
// Insert variable value at front of the argument vector
arguments.insert(arguments.begin(), variableValue);
// Remove variable name from command name
commandName = commandName.substr(dotOperatorPosition + 1);
}
// Find command linker for this command
auto linker = linkers.find(commandName);
if (linker == linkers.end())

src/debug/console.hpp → src/debug/command-interpreter.hpp View File

@ -17,8 +17,8 @@
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CONSOLE_HPP
#define CONSOLE_HPP
#ifndef COMMAND_INTERPRETER_HPP
#define COMMAND_INTERPRETER_HPP
#include <functional>
#include <map>
@ -139,6 +139,12 @@ public:
*/
const std::map<std::string, std::string>& help() const;
/**
* Returns all variables and their values.
*/
const std::map<std::string, std::string>& variables() const;
/**
* Interprets a line of text as a function call, returning the interpreted command name, argument vector, and callable function object.
*
@ -153,7 +159,7 @@ private:
// A command name-keyed map of command linkers
std::unordered_map<std::string, std::function<std::function<void()>(const std::vector<std::string>&)>> linkers;
std::map<std::string, std::string> helpStrings;
std::unordered_map<std::string, std::string> variables;
std::map<std::string, std::string> variableMap;
};
template <class... Args>
@ -175,5 +181,5 @@ void CommandInterpreter::addCommandLinker(const std::string& name, const Functio
linkers[name] = std::bind(linker, function, std::placeholders::_1);
}
#endif // CONSOLE_HPP
#endif // COMMAND_INTERPRETER_HPP

+ 43
- 0
src/debug/logger.cpp View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2017-2019 Christopher J. Howard
*
* This file is part of Antkeeper Source Code.
*
* Antkeeper Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "logger.hpp"
#include <iostream>
Logger::Logger():
os(&std::cout)
{}
Logger::~Logger()
{}
void Logger::redirect(std::ostream* stream)
{
os = stream;
}
void Logger::log(const std::string& text)
{
if (os)
{
(*os) << text;
os->flush();
}
}

+ 48
- 0
src/debug/logger.hpp View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2017-2019 Christopher J. Howard
*
* This file is part of Antkeeper Source Code.
*
* Antkeeper Source Code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper Source Code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LOGGER_HPP
#define LOGGER_HPP
#include <ostream>
#include <string>
class Logger
{
public:
Logger();
~Logger();
/**
* Redirects log output to the specified output stream.
*
* @param stream Output stream to which log text will be written.
*/
void redirect(std::ostream* stream);
/**
* Outputs text to the log.
*/
void log(const std::string& text);
private:
std::ostream* os;
};
#endif // LOGGER_HPP

+ 73
- 123
src/game.cpp View File

@ -64,7 +64,8 @@
#include <stdexcept>
#include <thread>
#include "debug/console.hpp"
#include "debug/command-interpreter.hpp"
#include "debug/logger.hpp"
template <>
bool Game::readSetting<std::string>(const std::string& name, std::string* value) const
@ -174,13 +175,11 @@ Game::Game(int argc, char* argv[]):
dataPath = getDataPath(applicationName) + "data/";
configPath = getConfigPath(applicationName);
controlsPath = configPath + "controls/";
scriptsPath = configPath + "scripts/";
// Create nonexistent config directories
std::vector<std::string> configPaths;
configPaths.push_back(configPath);
configPaths.push_back(controlsPath);
configPaths.push_back(scriptsPath);
for (const std::string& path: configPaths)
{
if (!pathExists(path))
@ -189,18 +188,10 @@ Game::Game(int argc, char* argv[]):
}
}
// Setup logging
#if !defined(DEBUG)
std::string logFilename = configPath + "log.txt";
logFileStream.open(logFilename.c_str());
std::cout.rdbuf(logFileStream.rdbuf());
#endif
// Setup resource manager
resourceManager = new ResourceManager();
// Include resource search paths in order of priority
resourceManager->include(scriptsPath);
resourceManager->include(controlsPath);
resourceManager->include(configPath);
resourceManager->include(dataPath);
@ -505,7 +496,6 @@ void Game::setup()
screenshotQueued = false;
paused = false;
// Load model resources
try
{
@ -516,14 +506,11 @@ void Game::setup()
}
catch (const std::exception& e)
{
std::cerr << "Failed to load one or more models: \"" << e.what() << "\"" << std::endl;
logger->log("Failed to load one or more models: \"" + std::string(e.what()) + "\"\n");
close(EXIT_FAILURE);
}
time = 0.0f;
// Tools
currentTool = nullptr;
@ -534,8 +521,6 @@ void Game::setup()
worldScene->addObject(lens->getSpotlight());
lens->setSunDirection(-sunlightCamera.getForward());
// Forceps
forceps = new Forceps(forcepsModel, &animator);
forceps->setOrbitCam(orbitCam);
@ -590,45 +575,6 @@ void Game::setup()
// Load navmesh
TriangleMesh* navmesh = resourceManager->load<TriangleMesh>("sidewalk.mesh");
// Find surface
TriangleMesh::Triangle* surface = nullptr;
Vector3 barycentricPosition;
Ray ray;
ray.origin = Vector3(0, 100, 0);
ray.direction = Vector3(0, -1, 0);
auto intersection = ray.intersects(*navmesh);
if (std::get<0>(intersection))
{
surface = (*navmesh->getTriangles())[std::get<3>(intersection)];
Vector3 position = ray.extrapolate(std::get<1>(intersection));
Vector3 a = surface->edge->vertex->position;
Vector3 b = surface->edge->next->vertex->position;
Vector3 c = surface->edge->previous->vertex->position;
barycentricPosition = barycentric(position, a, b, c);
}
for (int i = 0; i < 0; ++i)
{
EntityID ant = createInstanceOf("worker-ant");
setTranslation(ant, Vector3(0.0f, 0, 0.0f));
BehaviorComponent* behavior = new BehaviorComponent();
SteeringComponent* steering = new SteeringComponent();
LeggedLocomotionComponent* locomotion = new LeggedLocomotionComponent();
componentManager->addComponent(ant, behavior);
componentManager->addComponent(ant, steering);
componentManager->addComponent(ant, locomotion);
locomotion->surface = surface;
behavior->wanderTriangle = surface;
locomotion->barycentricPosition = barycentricPosition;
}
//EntityID tool0 = createInstanceOf("lens");
int highResolutionDiameter = 3;
int mediumResolutionDiameter = highResolutionDiameter + 2;
int lowResolutionDiameter = 20;
@ -799,8 +745,11 @@ void Game::handleEvent(const ScheduledFunctionEvent& event)
void Game::setupDebugging()
{
// Setup performance sampling
performanceSampler.setSampleSize(30);
// Setup logging
logger = new Logger();
std::string logFilename = configPath + "log.txt";
logFileStream.open(logFilename.c_str());
logger->redirect(&logFileStream);
// Create CLI
cli = new CommandInterpreter();
@ -812,9 +761,15 @@ void Game::setupDebugging()
std::function<void(int, float, float, float)> setScaleCommand = [this](int id, float x, float y, float z) {
setScale(id, {x, y, z});
};
std::function<void(int, float, float, float)> setTranslationCommand = [this](int id, float x, float y, float z) {
setTranslation(id, {x, y, z});
};
std::function<void()> createInstanceCommand = std::bind(&Game::createInstance, this);
std::function<void(std::string)> createNamedInstanceCommand = std::bind(&Game::createNamedInstance, this, std::placeholders::_1);
std::function<void(std::string)> createInstanceOfCommand = std::bind(&Game::createInstanceOf, this, std::placeholders::_1);
std::function<void(std::string, std::string)> createNamedInstanceOfCommand = std::bind(&Game::createNamedInstanceOf, this, std::placeholders::_1, std::placeholders::_2);
std::function<void(float)> toggleWireframeCommand = [this](float width){ lightingPass->setWireframeLineWidth(width); };
std::function<void(std::string)> shCommand = std::bind(&Game::executeShellScript, this, std::placeholders::_1);
std::function<void()> helpCommand = [this]()
{
auto& helpStrings = cli->help();
@ -828,28 +783,47 @@ void Game::setupDebugging()
std::cout << it->second << std::endl;
}
};
std::function<void()> variablesCommand = [this]()
{
auto& variables = cli->variables();
for (auto it = variables.begin(); it != variables.end(); ++it)
{
std::cout << it->first << "=\"" << it->second << "\"" << std::endl;
}
};
std::string exitHelp = "exit";
std::string setHelp = "set <name> <value>";
std::string unsetHelp = "unset <name>";
std::string setHelp = "set <variable name> <value>";
std::string unsetHelp = "unset <variable name>";
std::string createInstanceHelp = "createinstance <template name>";
std::string createNamedInstanceHelp = "createnamedinstance <template name> <instance name>";
std::string createInstanceOfHelp = "createinstanceof <template name>";
std::string createNamedInstanceOfHelp = "createnamedinstanceof <template name> <instance name>";
std::string setTranslationHelp = "settranslation <id> <x> <y> <z>";
std::string setScaleHelp = "setscale <id> <sx> <sy> <sz>";
std::string wireframeHelp = "wireframe <width>";
std::string shHelp = "sh <filename>";
std::string variablesHelp = "variables";
cli->registerCommand("exit", exitCommand, exitHelp);
cli->registerCommand("set", setCommand, setHelp);
cli->registerCommand("unset", setCommand, unsetHelp);
cli->registerCommand("createinstance", createInstanceCommand, createInstanceHelp);
cli->registerCommand("createnamedinstance", createNamedInstanceCommand, createNamedInstanceHelp);
cli->registerCommand("createinstanceof", createInstanceOfCommand, createInstanceOfHelp);
cli->registerCommand("createnamedinstanceof", createNamedInstanceOfCommand, createNamedInstanceOfHelp);
cli->registerCommand("setscale", setScaleCommand, setScaleHelp);
cli->registerCommand("settranslation", setTranslationCommand, setTranslationHelp);
cli->registerCommand("wireframe", toggleWireframeCommand, wireframeHelp);
cli->registerCommand("sh", shCommand, shHelp);
cli->registerCommand("variables", variablesCommand, variablesHelp);
cli->registerCommand("help", helpCommand);
// Start CLI thread
std::thread cliThread(&Game::interpretCommands, this);
cliThread.detach();
// Setup performance sampling
performanceSampler.setSampleSize(30);
}
void Game::setupLocalization()
@ -2060,7 +2034,7 @@ void Game::loadControlProfile(const std::string& profileName)
auto it = controlNameMap.find(controlName);
if (it == controlNameMap.end())
{
std::cerr << "Game::loadControlProfile(): Unknown control name \"" << controlName << "\"" << std::endl;
logger->log("Game::loadControlProfile(): Unknown control name \"" + controlName + "\"\n");
continue;
}
@ -2104,7 +2078,7 @@ void Game::loadControlProfile(const std::string& profileName)
}
else
{
std::cerr << "Game::loadControlProfile(): Unknown mouse motion axis \"" << axisName << "\"" << std::endl;
logger->log("Game::loadControlProfile(): Unknown mouse motion axis \"" + axisName + "\"\n");
continue;
}
@ -2128,7 +2102,7 @@ void Game::loadControlProfile(const std::string& profileName)
}
else
{
std::cerr << "Game::loadControlProfile(): Unknown mouse wheel axis \"" << axisName << "\"" << std::endl;
logger->log("Game::loadControlProfile(): Unknown mouse wheel axis \"" + axisName + "\"\n");
continue;
}
@ -2150,7 +2124,7 @@ void Game::loadControlProfile(const std::string& profileName)
}
else
{
std::cerr << "Game::loadControlProfile(): Unknown mouse event type \"" << eventType << "\"" << std::endl;
logger->log("Game::loadControlProfile(): Unknown mouse event type \"" + eventType + "\"\n");
continue;
}
}
@ -2208,13 +2182,13 @@ void Game::loadControlProfile(const std::string& profileName)
}
else
{
std::cerr << "Game::loadControlProfile(): Unknown gamepad event type \"" << eventType << "\"" << std::endl;
logger->log("Game::loadControlProfile(): Unknown gamepad event type \"" + eventType + "\"\n");
continue;
}
}
else
{
std::cerr << "Game::loadControlProfile(): Unknown input device type \"" << deviceType << "\"" << std::endl;
logger->log("Game::loadControlProfile(): Unknown input device type \"" + deviceType + "\"\n");
continue;
}
}
@ -3106,7 +3080,6 @@ void Game::enterTitleState()
// Setup scene
Vector3 antHillTranslation = {0, 0, 0};
EntityID antHill = createInstanceOf("ant-hill");
std::cout << antHill << std::endl;
setTranslation(antHill, antHillTranslation);
// Setup camera
@ -3144,6 +3117,11 @@ void Game::enterTitleState()
// Change setting menu's back item to return to the main menu
settingsMenuBackItem->setActivatedCallback(std::bind(&Game::openMenu, this, mainMenu, 3));
#if defined(DEBUG)
// Add important entity IDs to CLI variables
cli->set("anthill", std::to_string(antHill));
#endif
// Open the main menu and select the first menu item
openMenu(mainMenu, 0);
}
@ -3268,30 +3246,16 @@ void Game::returnToMainMenu()
void Game::interpretCommands()
{
std::cout << "Antkeeper " << VERSION_STRING << std::endl;
while (1)
while (true)
{
std::cout << "> " << std::flush;
std::string line;
std::getline(std::cin, line);
bool invalid = false;
std::string commandName;
std::vector<std::string> arguments;
std::function<void()> call;
try
{
std::tie(commandName, arguments, call) = cli->interpret(line);
}
catch (const std::invalid_argument& e)
{
invalid = true;
}
auto [commandName, arguments, call] = cli->interpret(line);
if (!invalid)
{
if (call)
{
@ -3305,9 +3269,9 @@ void Game::interpretCommands()
}
}
}
else
catch (const std::invalid_argument& e)
{
commandName = line.substr(0, line.find(' '));
std::string commandName = line.substr(0, line.find(' '));
auto& helpStrings = cli->help();
if (auto it = helpStrings.find(commandName); it != helpStrings.end())
@ -3318,7 +3282,6 @@ void Game::interpretCommands()
{
std::cout << commandName << ": Invalid arguments" << std::endl;
}
}
}
}
@ -3433,6 +3396,13 @@ EntityID Game::createInstance()
return entityManager->createEntity();
}
EntityID Game::createNamedInstance(const std::string& instanceName)
{
EntityID entity = entityManager->createEntity();
cli->set(instanceName, std::to_string(entity));
return entity;
}
EntityID Game::createInstanceOf(const std::string& templateName)
{
@ -3444,6 +3414,18 @@ EntityID Game::createInstanceOf(const std::string& templateName)
return entity;
}
EntityID Game::createNamedInstanceOf(const std::string& templateName, const std::string& instanceName)
{
EntityTemplate* entityTemplate = resourceManager->load<EntityTemplate>(templateName + ".ent");
EntityID entity = entityManager->createEntity();
entityTemplate->apply(entity, componentManager);
cli->set(instanceName, std::to_string(entity));
return entity;
}
void Game::destroyInstance(EntityID entity)
{
entityManager->destroyEntity(entity);
@ -3504,37 +3486,6 @@ void Game::setTerrainPatchPosition(EntityID entity, const std::tuple&
component->position = position;
}
void Game::executeShellScript(const std::string& string)
{
TextFile* script = nullptr;
try
{
script = resourceManager->load<TextFile>(string);
}
catch (const std::exception& e)
{
std::cerr << "Failed to load shell script: \"" << e.what() << "\"" << std::endl;
return;
}
for (const std::string& line: *script)
{
if (!line.empty())
{
auto [name, arguments, call] = cli->interpret(line);
if (call)
{
call();
}
else
{
std::cout << "Unknown command " << name << std::endl;
}
}
}
}
void Game::saveScreenshot(const std::string& filename, unsigned int width, unsigned int height, unsigned char* pixels)
{
stbi_flip_vertically_on_write(1);
@ -3543,4 +3494,3 @@ void Game::saveScreenshot(const std::string& filename, unsigned int width, unsig
delete[] pixels;
}

+ 6
- 7
src/game.hpp View File

@ -72,6 +72,7 @@ class ComponentBase;
class Menu;
class MenuItem;
class CommandInterpreter;
class Logger;
enum class ComponentType;
class Game:
@ -212,7 +213,9 @@ private:
public:
EntityID createInstance();
EntityID createNamedInstance(const std::string& instanceName);
EntityID createInstanceOf(const std::string& templateName);
EntityID createNamedInstanceOf(const std::string& templateName, const std::string& instanceName);
void selectInstance(EntityID entity);
void selectNamedInstance(const std::string& instanceName);
@ -223,7 +226,6 @@ public:
void setRotation(EntityID entity, const Quaternion& rotation);
void setScale(EntityID entity, const Vector3& scale);
void setTerrainPatchPosition(EntityID entity, const std::tuple<int, int>& position);
void executeShellScript(const std::string& string);
void boxSelect(float x, float y, float w, float h);
@ -245,7 +247,6 @@ public:
std::string dataPath;
std::string configPath;
std::string controlsPath;
std::string scriptsPath;
// Settings
StringTable* settingsTable;
@ -544,11 +545,9 @@ public:
bool toggleFullscreenDisabled;
// Debugging
#if defined(DEBUG)
CommandInterpreter* cli;
std::map<std::string, std::string> helpStrings;
std::ofstream logFileStream;
#endif
Logger* logger;
std::ofstream logFileStream;
CommandInterpreter* cli;
private:
static void saveScreenshot(const std::string& filename, unsigned int width, unsigned int height, unsigned char* pixels);

Loading…
Cancel
Save