Commit d5910ef8 authored by NTAuthority's avatar NTAuthority

allow multiple related console contexts to exist, also load gta.dat slightly...

allow multiple related console contexts to exist, also load gta.dat slightly differently (though nothing close to my final way) - also fix the tokenizer up a bit
parent 07b43c86
Pipeline #37 skipped
#pragma once
#include <Console.h>
#include <Console.Commands.h>
namespace krt
{
class ConsoleCommand
{
private:
int m_token;
ConsoleCommandManager* m_manager;
public:
template <typename TFunction>
ConsoleCommand(const std::string& name, TFunction function)
: ConsoleCommand(ConsoleCommandManager::GetDefaultInstance(), name, function)
{
}
template<typename TFunction>
ConsoleCommand(console::Context* context, const std::string& name, TFunction function)
: ConsoleCommand(context->GetCommandManager(), name, function)
{
}
template <typename TFunction>
ConsoleCommand(ConsoleCommandManager* manager, const std::string& name, TFunction function)
: m_manager(manager)
{
auto functionRef = detail::make_function(function);
m_token = m_manager->Register(name, [=](ConsoleExecutionContext& context) {
return internal::ConsoleCommandFunction<decltype(functionRef)>::Call(functionRef, context);
});
}
~ConsoleCommand()
{
if (m_token != -1)
{
m_manager->Unregister(m_token);
m_token = -1;
}
}
};
}
\ No newline at end of file
#pragma once
#include <atomic>
#include <functional>
#include <list>
......@@ -16,6 +17,11 @@
namespace krt
{
namespace console
{
class Context;
}
// console execution context
struct ConsoleExecutionContext
{
......@@ -34,11 +40,13 @@ class ConsoleCommandManager
using THandler = std::function<bool(ConsoleExecutionContext& context)>;
public:
ConsoleCommandManager();
ConsoleCommandManager(console::Context* context);
~ConsoleCommandManager();
void Register(const std::string& name, const THandler& handler);
int Register(const std::string& name, const THandler& handler);
void Unregister(int token);
void Invoke(const std::string& commandName, const ProgramArguments& arguments);
......@@ -50,25 +58,25 @@ class ConsoleCommandManager
std::string name;
THandler function;
inline Entry(const std::string& name, const THandler& function)
: name(name), function(function)
int token;
inline Entry(const std::string& name, const THandler& function, int token)
: name(name), function(function), token(token)
{
}
};
private:
console::Context* m_parentContext;
std::multimap<std::string, Entry, IgnoreCaseLess> m_entries;
std::shared_timed_mutex m_mutex;
public:
static inline ConsoleCommandManager* GetInstance()
{
return ms_instance;
}
std::atomic<int> m_curToken;
private:
static ConsoleCommandManager* ms_instance;
public:
static ConsoleCommandManager* GetDefaultInstance();
};
template <typename TArgument, typename TConstraint = void>
......@@ -97,7 +105,7 @@ struct ConsoleArgumentType<TArgument, std::enable_if_t<std::is_integral<TArgumen
{
try
{
*out = std::stoi(input);
*out = std::stoull(input);
// no way to know if an integer is valid this lazy way, sadly :(
return true;
......@@ -109,6 +117,24 @@ struct ConsoleArgumentType<TArgument, std::enable_if_t<std::is_integral<TArgumen
}
};
template <typename TArgument>
struct ConsoleArgumentType<TArgument, std::enable_if_t<std::is_floating_point<TArgument>::value>>
{
static bool Parse(const std::string& input, TArgument* out)
{
try
{
*out = std::stod(input);
return true;
}
catch (...)
{
return false;
}
}
};
namespace internal
{
template <typename TArgument>
......@@ -173,18 +199,4 @@ struct ConsoleCommandFunction<std::function<void(Args...)>>
}
};
}
class ConsoleCommand
{
public:
template <typename TFunction>
ConsoleCommand(const std::string& name, TFunction function)
{
auto functionRef = detail::make_function(function);
ConsoleCommandManager::GetInstance()->Register(name, [=](ConsoleExecutionContext& context) {
return internal::ConsoleCommandFunction<decltype(functionRef)>::Call(functionRef, context);
});
}
};
}
\ No newline at end of file
#pragma once
#include <Console.Commands.h>
#include <ProgramArguments.h>
namespace krt
{
class Console
namespace console
{
class Context
{
public:
static void ExecuteSingleCommand(const std::string& command);
Context();
Context(Context* fallbackContext);
void ExecuteSingleCommand(const std::string& command);
void ExecuteSingleCommand(const ProgramArguments& arguments);
void AddToBuffer(const std::string& text);
void ExecuteBuffer();
inline ConsoleCommandManager* GetCommandManager()
{
return m_commandManager.get();
}
static void ExecuteSingleCommand(const ProgramArguments& arguments);
inline Context* GetFallbackContext()
{
return m_fallbackContext;
}
static ProgramArguments Tokenize(const std::string& line);
private:
Context* m_fallbackContext;
static void AddToBuffer(const std::string& text);
std::unique_ptr<ConsoleCommandManager> m_commandManager;
static void ExecuteBuffer();
std::string m_commandBuffer;
std::mutex m_commandBufferMutex;
};
Context* GetDefaultContext();
void ExecuteSingleCommand(const std::string& command);
void ExecuteSingleCommand(const ProgramArguments& arguments);
ProgramArguments Tokenize(const std::string& line);
void AddToBuffer(const std::string& text);
void ExecuteBuffer();
}
}
\ No newline at end of file
#include <StdInc.h>
#include <Console.h>
#include <Console.Commands.h>
#include <utils/IteratorView.h>
......@@ -6,7 +7,8 @@
namespace krt
{
ConsoleCommandManager::ConsoleCommandManager()
ConsoleCommandManager::ConsoleCommandManager(console::Context* parentContext)
: m_parentContext(parentContext)
{
}
......@@ -14,16 +16,38 @@ ConsoleCommandManager::~ConsoleCommandManager()
{
}
void ConsoleCommandManager::Register(const std::string& name, const THandler& handler)
int ConsoleCommandManager::Register(const std::string& name, const THandler& handler)
{
auto lock = exclusive_lock_acquire<std::shared_timed_mutex>(m_mutex);
m_entries.insert({name, Entry{name, handler}});
int token = m_curToken.fetch_add(1);
m_entries.insert({ name, Entry{name, handler, token} });
return token;
}
void ConsoleCommandManager::Unregister(int token)
{
auto lock = exclusive_lock_acquire<std::shared_timed_mutex>(m_mutex);
// look through the list for a matching token
for (auto it = m_entries.begin(); it != m_entries.end(); it++)
{
if (it->second.token == token)
{
// erase and return immediately (so we won't increment a bad iterator)
m_entries.erase(it);
return;
}
}
}
void ConsoleCommandManager::Invoke(const std::string& commandString)
{
assert(!"Not implemented (needs tokenizer)");
ProgramArguments arguments = console::Tokenize(commandString);
std::string command = arguments.Shift();
return Invoke(command, arguments);
}
void ConsoleCommandManager::Invoke(const std::string& commandName, const ProgramArguments& arguments)
......@@ -36,6 +60,14 @@ void ConsoleCommandManager::Invoke(const std::string& commandName, const Program
if (entryPair.first == entryPair.second)
{
// try in the fallback context first
console::Context* fallbackContext = m_parentContext->GetFallbackContext();
if (fallbackContext)
{
return fallbackContext->GetCommandManager()->Invoke(commandName, arguments);
}
// TODO: replace with console stream output
printf("No such command %s.\n", commandName.c_str());
return;
......@@ -64,6 +96,8 @@ void ConsoleCommandManager::Invoke(const std::string& commandName, const Program
}
}
static ConsoleCommandManager g_manager;
ConsoleCommandManager* ConsoleCommandManager::ms_instance = &g_manager;
ConsoleCommandManager* ConsoleCommandManager::GetDefaultInstance()
{
return console::GetDefaultContext()->GetCommandManager();
}
}
\ No newline at end of file
......@@ -6,14 +6,28 @@
namespace krt
{
void Console::ExecuteSingleCommand(const std::string& command)
namespace console
{
Context::Context()
: Context(GetDefaultContext())
{
}
Context::Context(Context* fallbackContext)
: m_fallbackContext(fallbackContext)
{
m_commandManager = std::make_unique<ConsoleCommandManager>(this);
}
void Context::ExecuteSingleCommand(const std::string& command)
{
ProgramArguments arguments = Tokenize(command);
ExecuteSingleCommand(arguments);
}
void Console::ExecuteSingleCommand(const ProgramArguments& arguments)
void Context::ExecuteSingleCommand(const ProgramArguments& arguments)
{
// early out if no command nor arguments were passed
if (arguments.Count() == 0)
......@@ -26,62 +40,59 @@ void Console::ExecuteSingleCommand(const ProgramArguments& arguments)
std::string command = localArgs.Shift();
// run the command through the command manager
ConsoleCommandManager::GetInstance()->Invoke(command, localArgs);
m_commandManager->Invoke(command, localArgs);
}
static std::string g_commandBuffer;
static std::mutex g_commandBufferMutex;
void Console::AddToBuffer(const std::string& text)
void Context::AddToBuffer(const std::string& text)
{
std::lock_guard<std::mutex> guard(g_commandBufferMutex);
g_commandBuffer += text;
std::lock_guard<std::mutex> guard(m_commandBufferMutex);
m_commandBuffer += text;
}
void Console::ExecuteBuffer()
void Context::ExecuteBuffer()
{
std::vector<std::string> toExecute;
{
std::lock_guard<std::mutex> guard(g_commandBufferMutex);
std::lock_guard<std::mutex> guard(m_commandBufferMutex);
while (g_commandBuffer.length() > 0)
while (m_commandBuffer.length() > 0)
{
// parse the command up to the first occurence of a newline/semicolon
// parse the command up to the first occurrence of a newline/semicolon
int i = 0;
bool inQuote = false;
size_t cbufLength = g_commandBuffer.length();
size_t cbufLength = m_commandBuffer.length();
for (i = 0; i < cbufLength; i++)
{
if (g_commandBuffer[i] == '"')
if (m_commandBuffer[i] == '"')
{
inQuote = !inQuote;
}
// break if a semicolon
if (!inQuote && g_commandBuffer[i] == ';')
if (!inQuote && m_commandBuffer[i] == ';')
{
break;
}
// or a newline
if (g_commandBuffer[i] == '\r' || g_commandBuffer[i] == '\n')
if (m_commandBuffer[i] == '\r' || m_commandBuffer[i] == '\n')
{
break;
}
}
std::string command = g_commandBuffer.substr(0, i);
std::string command = m_commandBuffer.substr(0, i);
if (cbufLength > i)
{
g_commandBuffer = g_commandBuffer.substr(i + 1);
m_commandBuffer = m_commandBuffer.substr(i + 1);
}
else
{
g_commandBuffer.clear();
m_commandBuffer.clear();
}
// and add the command for execution when the mutex is unlocked
......@@ -95,7 +106,42 @@ void Console::ExecuteBuffer()
}
}
ProgramArguments Console::Tokenize(const std::string& line)
// default context functions
Context* GetDefaultContext()
{
static std::unique_ptr<Context> defaultContext;
static std::once_flag flag;
std::call_once(flag, [] ()
{
// nullptr is important - we don't have ourselves to fall back on!
defaultContext = std::make_unique<Context>(nullptr);
});
return defaultContext.get();
}
void ExecuteSingleCommand(const std::string& command)
{
return GetDefaultContext()->ExecuteSingleCommand(command);
}
void ExecuteSingleCommand(const ProgramArguments& arguments)
{
return GetDefaultContext()->ExecuteSingleCommand(arguments);
}
void AddToBuffer(const std::string& text)
{
return GetDefaultContext()->AddToBuffer(text);
}
void ExecuteBuffer()
{
return GetDefaultContext()->ExecuteBuffer();
}
ProgramArguments Tokenize(const std::string& line)
{
int i = 0;
int j = 0;
......@@ -208,16 +254,25 @@ ProgramArguments Console::Tokenize(const std::string& line)
break;
}
// # comments are one character long
if (i < lineLength)
{
if (line[i] == '#')
{
return ProgramArguments{ args };
}
}
if (i < (lineLength - 1))
{
if ((line[i] == '/' && line[i + 1] == '/') || line[i] == '#')
if ((line[i] == '/' && line[i + 1] == '/'))
{
break;
return ProgramArguments{ args };
}
if (line[i] == '/' && line[i + 1] == '*')
{
break;
return ProgramArguments{ args };
}
}
......@@ -226,8 +281,13 @@ ProgramArguments Console::Tokenize(const std::string& line)
i++;
}
args.push_back(arg.str());
j++;
std::string argStr = arg.str();
if (!argStr.empty())
{
args.push_back(argStr);
j++;
}
if (i >= lineLength)
{
......@@ -235,4 +295,5 @@ ProgramArguments Console::Tokenize(const std::string& line)
}
}
}
}
}
\ No newline at end of file
......@@ -13,7 +13,7 @@
#include <vfs/Manager.h>
#include <Console.h>
#include <Console.Commands.h>
#include <Console.CommandHelpers.h>
#include <Streaming.tests.h>
......@@ -43,12 +43,9 @@ int Main::Run(const ProgramArguments& arguments)
printf("%d %d %s\n", a, b, c.c_str());
});
Console::ExecuteSingleCommand("a 5 4 1000");
Console::AddToBuffer("a 1 2 3; a 1 2 \"hi world\"\r\na 1 2");
Console::ExecuteBuffer();
ConsoleCommandManager::GetInstance()->Invoke("a", ProgramArguments{ "5", "4", "a" });
ConsoleCommandManager::GetInstance()->Invoke("a", ProgramArguments{ "5", "4", "1000" });
console::ExecuteSingleCommand("a 5 4 1000");
console::AddToBuffer("a 1 2 3; a 1 2 \"hi world\"\r\na 1 2");
console::ExecuteBuffer();
// ))))))))))))))
streaming::FaultTest1();
......
......@@ -6,7 +6,7 @@
#include <fstream>
#include "Console.h"
#include "Console.Commands.h"
#include "Console.CommandHelpers.h"
#include "CdImageDevice.h"
#include "vfs\Manager.h"
......@@ -39,7 +39,7 @@ Game::Game( void ) : streaming( GAME_NUM_STREAMING_CHANNELS ), texManager( strea
if ( stricmp( computerName, "FALLARBOR" ) == 0 )
{
// Bas' thing.
this->gameDir = "S:\\Games\\Steam\\steamapps\\common\\Grand Theft Auto 3\\";
this->gameDir = "S:\\Games\\CleanSA\\GTA San Andreas\\";
}
else if ( stricmp( computerName, "DESKTOP" ) == 0 )
{
......@@ -439,37 +439,27 @@ void Game::LoadIPLFile( std::string relPath )
}
ConsoleCommand iplLoadCmd( "IPL",
[] ( const std::string& fileName )
void Game::RunCommandFile( std::string relPath )
{
theGame->LoadIPLFile( fileName );
});
vfs::StreamPtr stream = vfs::OpenRead(this->GetGamePath(relPath));
std::vector<uint8_t> string = stream->ReadToEnd();
ConsoleCommand imgMountCmd( "IMG",
[] ( const std::string& path )
{
theGame->LoadIMG( path );
});
console::Context localConsole;
void Game::RunCommandFile( std::string relPath )
{
std::stringstream cmdFile = get_file_stream( this->GetGamePath( relPath ) );
ConsoleCommand iplLoadCmd(&localConsole, "IPL",
[] (const std::string& fileName)
{
theGame->LoadIPLFile(fileName);
});
// Process commands in lines!
while ( cmdFile.eof() == false )
{
std::string cmdLine;
// Skip whitespace.
cmdLine = get_cfg_line( cmdFile );
ConsoleCommand imgMountCmd(&localConsole, "IMG",
[] (const std::string& path)
{
theGame->LoadIMG(path);
});
// Ignore commented lines.
if ( !ignore_line( cmdLine ) )
{
// Execute!
Console::ExecuteSingleCommand( cmdLine );
}
}
localConsole.AddToBuffer(std::string(reinterpret_cast<char*>(string.data()), string.size()));
localConsole.ExecuteBuffer();
}
vfs::DevicePtr Game::FindDevice( std::string genPath, std::string& devPathOut )
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment