diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 04df47a5b..268855f79 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -49,6 +49,7 @@ include/dfhack/modules/World.h SET(PROJECT_SRCS Core.cpp +PluginManager.cpp VersionInfo.cpp VersionInfoFactory.cpp TileTypes.cpp diff --git a/library/Console-linux.cpp b/library/Console-linux.cpp index 28d8d53c1..125b6cc4b 100644 --- a/library/Console-linux.cpp +++ b/library/Console-linux.cpp @@ -116,4 +116,10 @@ void Console::cursor(bool enable) { dfout <<"\033[?25l"; } +} + +void Console::msleep (unsigned int msec) +{ + if (msec > 1000) sleep(msec/1000000); + usleep((msec % 1000000) * 1000); } \ No newline at end of file diff --git a/library/Console-windows.cpp b/library/Console-windows.cpp index 7e5a232df..63035c6df 100644 --- a/library/Console-windows.cpp +++ b/library/Console-windows.cpp @@ -96,14 +96,17 @@ Console::Console() clear(); // result is a terminal controlled by the parasitic code! } + Console::~Console() { FreeConsole(); } + void Console::clear() { system("cls"); } + void Console::gotoxy(int x, int y) { COORD coord = {x-1, y-1}; // Windows uses 0-based coordinates @@ -136,4 +139,9 @@ void Console::cursor(bool enable) structCursorInfo.bVisible = FALSE; SetConsoleCursorInfo( hConsoleOutput, &structCursorInfo ); } +} + +void Console::msleep (unsigned int msec) +{ + Sleep(msec); } \ No newline at end of file diff --git a/library/Core-linux.cpp b/library/Core-linux.cpp index ce48d2677..f09940f4e 100644 --- a/library/Core-linux.cpp +++ b/library/Core-linux.cpp @@ -50,7 +50,13 @@ namespace DFHack { DFLibrary * OpenPlugin (const char * filename) { - return (DFLibrary *) dlopen(filename, RTLD_NOW); + dlerror(); + DFLibrary * ret = (DFLibrary *) dlopen(filename, RTLD_NOW); + if(!ret) + { + std::cerr << dlerror() << std::endl; + } + return ret; } void * LookupPlugin (DFLibrary * plugin ,const char * function) { diff --git a/library/Core.cpp b/library/Core.cpp index 505676822..0ae8b5edc 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -30,6 +30,8 @@ distribution. #include #include #include +#include +#include using namespace std; #include "dfhack/Error.h" @@ -37,85 +39,33 @@ using namespace std; #include "dfhack/Core.h" #include "dfhack/Console.h" #include "dfhack/VersionInfoFactory.h" +#include "dfhack/PluginManager.h" #include "ModuleFactory.h" #include "dfhack/modules/Gui.h" #include "dfhack/modules/Vegetation.h" #include "dfhack/modules/Maps.h" #include "dfhack/modules/World.h" -#include "dfhack/extra/rlutil.h" #include -#ifdef LINUX_BUILD - #include - #include -#else - #include "wdirent.h" -#endif using namespace DFHack; -static int getdir (string dir, vector &files) +void cheap_tokenise(string const& input, vector &output) { - DIR *dp; - struct dirent *dirp; - if((dp = opendir(dir.c_str())) == NULL) - { - dfout << "Error(" << errno << ") opening " << dir << endl; - return errno; - } - while ((dirp = readdir(dp)) != NULL) { - files.push_back(string(dirp->d_name)); - } - closedir(dp); - return 0; + istringstream str(input); + istream_iterator cur(str), end; + output.assign(cur, end); } - -int fIOthread(void * _core) +struct IODATA { - Core * core = (Core *) _core; - -#ifdef LINUX_BUILD - string path = core->p->getPath() + "/plugins/"; - const char * searchstr = ".plug.so"; -#else - string path = core->p->getPath() + "\\plugins\\"; - const char * searchstr = ".plug.dll"; -#endif + Core * core; + PluginManager * plug_mgr; +}; - vector filez; - map plugins; - getdir(path, filez); - const char * (*_PlugName)(void) = 0; - int (*_PlugRun)(Core *) = 0; - for(int i = 0; i < filez.size();i++) - { - if(strstr(filez[i].c_str(),searchstr)) - { - string fullpath = path + filez[i]; - DFLibrary * plug = OpenPlugin(fullpath.c_str()); - if(!plug) - { - dfout << "Can't load plugin " << filez[i] << endl; - continue; - } - _PlugName = (const char * (*)()) LookupPlugin(plug, "plugin_name"); - if(!_PlugName) - { - dfout << "Plugin " << filez[i] << " has no name." << endl; - ClosePlugin(plug); - continue; - } - _PlugRun = (int (*)(Core * c)) LookupPlugin(plug, "plugin_run"); - if(!_PlugRun) - { - dfout << "Plugin " << filez[i] << " has no run function." << endl; - ClosePlugin(plug); - continue; - } - dfout << "Loaded plugin " << filez[i] << endl; - plugins[string(_PlugName())] = _PlugRun; - } - } +int fIOthread(void * iodata) +{ + Core * core = ((IODATA*) iodata)->core; + PluginManager * plug_mgr = ((IODATA*) iodata)->plug_mgr; fprintf(dfout_C,"DFHack is ready. Have a nice day! Type in '?' or 'help' for help.\n"); //dfterm << << endl; int clueless_counter = 0; @@ -132,10 +82,6 @@ int fIOthread(void * _core) if(command=="help" || command == "?") { dfout << "Available commands:" << endl; - for (map ::iterator iter = plugins.begin(); iter != plugins.end(); iter++) - { - dfout << iter->first << endl; - } } else if( command == "" ) { @@ -143,15 +89,26 @@ int fIOthread(void * _core) } else { - map ::iterator iter = plugins.find(command); - if(iter != plugins.end()) + vector parts; + cheap_tokenise(command,parts); + if(parts.size() == 0) { - iter->second(core); + clueless_counter++; } else { - dfout << "Invalid command." << endl; - clueless_counter ++; + string first = parts[0]; + parts.erase(parts.begin()); + command_result res = plug_mgr->InvokeCommand(first, parts); + if(res == CR_NOT_IMPLEMENTED) + { + dfout << "Invalid command." << endl; + clueless_counter ++; + } + else if(res == CR_FAILURE) + { + dfout << "ERROR!" << endl; + } } } if(clueless_counter == 3) @@ -166,6 +123,7 @@ Core::Core() { // init the console. This must be always the first step! con = new Console(); + plug_mgr = 0; // find out what we are... vif = new DFHack::VersionInfoFactory("Memory.xml"); p = new DFHack::Process(vif); @@ -195,9 +153,13 @@ Core::Core() errorstate = false; // lock mutex SDL_mutexP(AccessMutex); + plug_mgr = new PluginManager(this); // look for all plugins, // create IO thread - DFThread * IO = SDL_CreateThread(fIOthread, this); + IODATA temp; + temp.core = this; + temp.plug_mgr = plug_mgr; + DFThread * IO = SDL_CreateThread(fIOthread, (void *) &temp); // and let DF do its thing. }; @@ -230,8 +192,11 @@ int Core::Update() int Core::Shutdown ( void ) { errorstate = 1; - // TODO:shutdown all plugins - + if(plug_mgr) + { + delete plug_mgr; + plug_mgr = 0; + } // invalidate all modules for(unsigned int i = 0 ; i < allModules.size(); i++) { diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp new file mode 100644 index 000000000..bfee5423d --- /dev/null +++ b/library/PluginManager.cpp @@ -0,0 +1,181 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "Internal.h" +#include "dfhack/Core.h" +#include "dfhack/Process.h" +#include "dfhack/PluginManager.h" +#include "dfhack/Console.h" +using namespace DFHack; + +#include +#include +#include +using namespace std; + +#ifdef LINUX_BUILD + #include + #include +#else + #include "wdirent.h" +#endif + +static int getdir (string dir, vector &files) +{ + DIR *dp; + struct dirent *dirp; + if((dp = opendir(dir.c_str())) == NULL) + { + dfout << "Error(" << errno << ") opening " << dir << endl; + return errno; + } + while ((dirp = readdir(dp)) != NULL) { + files.push_back(string(dirp->d_name)); + } + closedir(dp); + return 0; +} + +bool hasEnding (std::string const &fullString, std::string const &ending) +{ + if (fullString.length() > ending.length()) + { + return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending)); + } + else + { + return false; + } +} + +Plugin::Plugin(Core * core, const std::string & file) +{ + filename = file; + plugin_lib = 0; + plugin_init = 0; + plugin_shutdown = 0; + plugin_status = 0; + loaded = false; + DFLibrary * plug = OpenPlugin(file.c_str()); + if(!plug) + { + dfout << "Can't load plugin " << filename << endl; + return; + } + const char * (*_PlugName)() =(const char * (*)()) LookupPlugin(plug, "plugin_name"); + if(!_PlugName) + { + dfout << "Plugin " << filename << " has no name." << endl; + ClosePlugin(plug); + return; + } + plugin_init = (command_result (*)(Core *, std::vector &)) LookupPlugin(plug, "plugin_init"); + if(!plugin_init) + { + dfout << "Plugin " << filename << " has no init function." << endl; + ClosePlugin(plug); + return; + } + plugin_status = (command_result (*)(Core *, std::string &)) LookupPlugin(plug, "plugin_status"); + plugin_shutdown = (command_result (*)(Core *)) LookupPlugin(plug, "plugin_shutdown"); + name = _PlugName(); + plugin_lib = plug; + loaded = true; + dfout << "Found plugin " << name << endl; + if(plugin_init(core,commands) == CR_OK) + { + for(int i = 0; i < commands.size();i++) + { + dfout << commands[i].name << " : " << commands[i].description << std::endl; + } + } + else + { + // horrible! + } +} + +Plugin::~Plugin() +{ + if(loaded) + ClosePlugin(plugin_lib); +} + +bool Plugin::isLoaded() +{ + return loaded; +} + +PluginManager::PluginManager(Core * core) +{ +#ifdef LINUX_BUILD + string path = core->p->getPath() + "/plugins/"; + const string searchstr = ".plug.so"; +#else + string path = core->p->getPath() + "\\plugins\\"; + const string searchstr = ".plug.dll"; +#endif + vector filez; + getdir(path, filez); + for(int i = 0; i < filez.size();i++) + { + if(hasEnding(filez[i],searchstr)) + { + Plugin * p = new Plugin(core, path + filez[i]); + for(int j = 0; j < p->commands.size();j++) + { + commands[p->commands[j].name] = &p->commands[j]; + } + all_plugins.push_back(p); + } + } +} + +PluginManager::~PluginManager() +{ + +} +Plugin *PluginManager::getPluginByName (const std::string & name) +{ + +} +command_result PluginManager::InvokeCommand( std::string & command, std::vector & parameters) +{ + Core * c = &Core::getInstance(); + map ::iterator iter = commands.find(command); + if(iter != commands.end()) + { + return iter->second->function(c,parameters); + } + return CR_NOT_IMPLEMENTED; +} +/* +for (map ::iterator iter = plugins.begin(); iter != plugins.end(); iter++) +{ + dfout << iter->first << endl; +} +*/ +/* + +*/ \ No newline at end of file diff --git a/library/include/dfhack/Console.h b/library/include/dfhack/Console.h index 096cb7db3..4213cb2ac 100644 --- a/library/include/dfhack/Console.h +++ b/library/include/dfhack/Console.h @@ -38,9 +38,15 @@ namespace DFHack public: Console(); ~Console(); + /// Clear the console, along with its scrollback void clear(); + /// Position cursor at x,y. 1,1 = top left corner void gotoxy(int x, int y); + /// Set color (ANSI color number) void color(int index); + /// Enable or disable the caret/cursor void cursor(bool enable = true); + /// Waits given number of milliseconds before continuing. + void msleep(unsigned int msec); }; } \ No newline at end of file diff --git a/library/include/dfhack/Core.h b/library/include/dfhack/Core.h index 0f51ec571..f4923bbaf 100644 --- a/library/include/dfhack/Core.h +++ b/library/include/dfhack/Core.h @@ -48,6 +48,7 @@ namespace DFHack class VersionInfo; class VersionInfoFactory; class Console; + class PluginManager; DFLibrary * OpenPlugin (const char * filename); void * LookupPlugin (DFLibrary * plugin ,const char * function); @@ -97,10 +98,10 @@ namespace DFHack Buildings * getBuildings(); /// get the constructions module Constructions * getConstructions(); - + DFHack::Process * p; DFHack::VersionInfo * vinfo; - Console * con; + DFHack::Console * con; private: Core(); int Update (void); @@ -130,5 +131,6 @@ namespace DFHack Constructions * pConstructions; } s_mods; std::vector allModules; + DFHack::PluginManager * plug_mgr; }; } \ No newline at end of file diff --git a/library/include/dfhack/Module.h b/library/include/dfhack/Module.h index c0b203fca..170a68312 100644 --- a/library/include/dfhack/Module.h +++ b/library/include/dfhack/Module.h @@ -30,7 +30,6 @@ distribution. #include "dfhack/Export.h" namespace DFHack { - class Context; /** * The parent class for all DFHack modules * \ingroup grp_modules diff --git a/library/include/dfhack/PluginManager.h b/library/include/dfhack/PluginManager.h new file mode 100644 index 000000000..76059a361 --- /dev/null +++ b/library/include/dfhack/PluginManager.h @@ -0,0 +1,98 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2011 Petr Mrázek (peterix@gmail.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#pragma once + +#include "dfhack/Export.h" +#include +#include +#include +struct DFLibrary; +namespace DFHack +{ + class Core; + class PluginManager; + enum command_result + { + CR_NOT_IMPLEMENTED = -1, + CR_FAILURE = 0, + CR_OK = 1 + }; + struct PluginCommand + { + PluginCommand(const char * _name, + const char * _description, + command_result (*function_)(Core *, std::vector &) + ) + { + name = _name; + description = _description; + function = function_; + } + PluginCommand (const PluginCommand & rhs) + { + name = rhs.name; + description = rhs.description; + function = rhs.function; + } + std::string name; + std::string description; + command_result (*function)(Core *, std::vector &); + }; + class Plugin + { + friend class PluginManager; + public: + Plugin(DFHack::Core* core, const std::string& file); + ~Plugin(); + bool isLoaded (); + /* + bool Load (); + bool Unload (); + std::string Status (); + */ + private: + std::vector commands; + std::string filename; + std::string name; + DFLibrary * plugin_lib; + bool loaded; + command_result (*plugin_init)(Core *, std::vector &); + command_result (*plugin_status)(Core *, std::string &); + command_result (*plugin_shutdown)(Core *); + }; + class DFHACK_EXPORT PluginManager + { + public: + PluginManager(Core * core); + ~PluginManager(); + Plugin *getPluginByName (const std::string & name); + command_result InvokeCommand( std::string & command, std::vector & parameters); + private: + std::map commands; + std::vector all_plugins; + std::string plugin_path; + }; +} + diff --git a/library/include/dfhack/extra/rlutil.h b/library/include/dfhack/extra/rlutil.h deleted file mode 100644 index 7368e3a6d..000000000 --- a/library/include/dfhack/extra/rlutil.h +++ /dev/null @@ -1,536 +0,0 @@ -#pragma once -/** - * File: rlutil.h - * - * About: Description - * This file provides some useful utilities for console mode - * roguelike game development with C and C++. It is aimed to - * be cross-platform (at least Windows and Linux). - * - * About: Copyright - * (C) 2011 The united church of gotoxy. - * - */ - - -/// Define: RLUTIL_USE_ANSI -/// Define this to use ANSI escape sequences also on Windows -/// (defaults to using WinAPI instead). -#if 0 -#define RLUTIL_USE_ANSI -#endif - -/// Define: RLUTIL_STRING_T -/// Define/typedef this to your preference to override rlutil's string type. -/// -/// Defaults to std::string with C++ and char* with C. -#if 0 -#define RLUTIL_STRING_T char* -#endif - -#ifdef __cplusplus - /// Common C++ headers - #include - #include - #include - /// Namespace forward declarations - namespace rlutil - { - void locate(int x, int y); - } -#endif // __cplusplus - -#ifdef WIN32 - #include // for WinAPI and Sleep() - #include // for getch() and kbhit() -#else - #ifdef __cplusplus - #include // for getch() - #else // __cplusplus - #include // for getch() - #endif // __cplusplus - #include // for getch() and kbhit() - #include // for getch(), kbhit() and (u)sleep() - #include // for getkey() - #include // for kbhit() - #include // for kbhit() - -/// Function: getch -/// Get character without waiting for Return to be pressed. -/// Windows has this in conio.h -int getch() -{ - // Here be magic. - struct termios oldt, newt; - int ch; - tcgetattr(STDIN_FILENO, &oldt); - newt = oldt; - newt.c_lflag &= ~(ICANON | ECHO); - tcsetattr(STDIN_FILENO, TCSANOW, &newt); - ch = getchar(); - tcsetattr(STDIN_FILENO, TCSANOW, &oldt); - return ch; -} - -/// Function: kbhit -/// Determines if keyboard has been hit. -/// Windows has this in conio.h -int kbhit() -{ - // Here be dragons. - static struct termios oldt, newt; - int cnt = 0; - tcgetattr(STDIN_FILENO, &oldt); - newt = oldt; - newt.c_lflag &= ~(ICANON | ECHO); - newt.c_iflag = 0; // input mode - newt.c_oflag = 0; // output mode - newt.c_cc[VMIN] = 1; // minimum time to wait - newt.c_cc[VTIME] = 1; // minimum characters to wait for - tcsetattr(STDIN_FILENO, TCSANOW, &newt); - ioctl(0, FIONREAD, &cnt); // Read count - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 100; - select(STDIN_FILENO+1, NULL, NULL, NULL, &tv); // A small time delay - tcsetattr(STDIN_FILENO, TCSANOW, &oldt); - return cnt; // Return number of characters -} -#endif // WIN32 - -#ifndef gotoxy -/// Function: gotoxy -/// Same as . -void inline gotoxy(int x, int y) { - #ifdef __cplusplus - rlutil:: - #endif - locate(x,y); -} -#endif // gotoxy - -#ifdef __cplusplus -/// Namespace: rlutil -/// In C++ all functions except , and are arranged -/// under namespace rlutil. That is because some platforms have them defined -/// outside of rlutil. -namespace rlutil { -#endif - -/** - * Defs: Internal typedefs and macros - * RLUTIL_STRING_T - String type depending on which one of C or C++ is used - * RLUTIL_PRINT(str) - Printing macro independent of C/C++ - */ - -#ifdef __cplusplus - #ifndef RLUTIL_STRING_T - typedef std::string RLUTIL_STRING_T; - #endif // RLUTIL_STRING_T - - void inline RLUTIL_PRINT(RLUTIL_STRING_T st) { std::cout << st; } - -#else // __cplusplus - #ifndef RLUTIL_STRING_T - typedef char* RLUTIL_STRING_T; - #endif // RLUTIL_STRING_T - - #define RLUTIL_PRINT(st) printf("%s", st) -#endif // __cplusplus - -/** - * Enums: Color codes - * - * BLACK - Black - * RED - Red - * GREEN - Green - * BROWN - Brown / dark yellow - * BLUE - Blue - * MAGENTA - Magenta / purple - * CYAN - Cyan - * GREY - Grey / dark white - * DARKGREY - Dark grey / light black - * LIGHTRED - Light red - * LIGHTGREEN - Light green - * YELLOW - Yellow (bright) - * LIGHTBLUE - Light blue - * LIGHTMAGENTA - Light magenta / light purple - * LIGHTCYAN - Light cyan - * WHITE - White (bright) - */ -enum { - BLACK, - RED, - GREEN, - BROWN, - BLUE, - MAGENTA, - CYAN, - GREY, - DARKGREY, - LIGHTRED, - LIGHTGREEN, - YELLOW, - LIGHTBLUE, - LIGHTMAGENTA, - LIGHTCYAN, - WHITE -}; - -/** - * Consts: ANSI color strings - * - * ANSI_CLS - Clears screen - * ANSI_BLACK - Black - * ANSI_RED - Red - * ANSI_GREEN - Green - * ANSI_BROWN - Brown / dark yellow - * ANSI_BLUE - Blue - * ANSI_MAGENTA - Magenta / purple - * ANSI_CYAN - Cyan - * ANSI_GREY - Grey / dark white - * ANSI_DARKGREY - Dark grey / light black - * ANSI_LIGHTRED - Light red - * ANSI_LIGHTGREEN - Light green - * ANSI_YELLOW - Yellow (bright) - * ANSI_LIGHTBLUE - Light blue - * ANSI_LIGHTMAGENTA - Light magenta / light purple - * ANSI_LIGHTCYAN - Light cyan - * ANSI_WHITE - White (bright) - */ -const RLUTIL_STRING_T ANSI_CLS = "\033[2J"; -const RLUTIL_STRING_T ANSI_BLACK = "\033[22;30m"; -const RLUTIL_STRING_T ANSI_RED = "\033[22;31m"; -const RLUTIL_STRING_T ANSI_GREEN = "\033[22;32m"; -const RLUTIL_STRING_T ANSI_BROWN = "\033[22;33m"; -const RLUTIL_STRING_T ANSI_BLUE = "\033[22;34m"; -const RLUTIL_STRING_T ANSI_MAGENTA = "\033[22;35m"; -const RLUTIL_STRING_T ANSI_CYAN = "\033[22;36m"; -const RLUTIL_STRING_T ANSI_GREY = "\033[22;37m"; -const RLUTIL_STRING_T ANSI_DARKGREY = "\033[01;30m"; -const RLUTIL_STRING_T ANSI_LIGHTRED = "\033[01;31m"; -const RLUTIL_STRING_T ANSI_LIGHTGREEN = "\033[01;32m"; -const RLUTIL_STRING_T ANSI_YELLOW = "\033[01;33m"; -const RLUTIL_STRING_T ANSI_LIGHTBLUE = "\033[01;34m"; -const RLUTIL_STRING_T ANSI_LIGHTMAGENTA = "\033[01;35m"; -const RLUTIL_STRING_T ANSI_LIGHTCYAN = "\033[01;36m"; -const RLUTIL_STRING_T ANSI_WHITE = "\033[01;37m"; - -/** - * Consts: Key codes for keyhit() - * - * KEY_ESCAPE - Escape - * KEY_ENTER - Enter - * KEY_SPACE - Space - * KEY_INSERT - Insert - * KEY_HOME - Home - * KEY_END - End - * KEY_DELETE - Delete - * KEY_PGUP - PageUp - * KEY_PGDOWN - PageDown - * KEY_UP - Up arrow - * KEY_DOWN - Down arrow - * KEY_LEFT - Left arrow - * KEY_RIGHT - Right arrow - * KEY_F1 - F1 - * KEY_F2 - F2 - * KEY_F3 - F3 - * KEY_F4 - F4 - * KEY_F5 - F5 - * KEY_F6 - F6 - * KEY_F7 - F7 - * KEY_F8 - F8 - * KEY_F9 - F9 - * KEY_F10 - F10 - * KEY_F11 - F11 - * KEY_F12 - F12 - * KEY_NUMDEL - Numpad del - * KEY_NUMPAD0 - Numpad 0 - * KEY_NUMPAD1 - Numpad 1 - * KEY_NUMPAD2 - Numpad 2 - * KEY_NUMPAD3 - Numpad 3 - * KEY_NUMPAD4 - Numpad 4 - * KEY_NUMPAD5 - Numpad 5 - * KEY_NUMPAD6 - Numpad 6 - * KEY_NUMPAD7 - Numpad 7 - * KEY_NUMPAD8 - Numpad 8 - * KEY_NUMPAD9 - Numpad 9 - */ -const int KEY_ESCAPE = 0; -const int KEY_ENTER = 1; -const int KEY_SPACE = 32; - -const int KEY_INSERT = 2; -const int KEY_HOME = 3; -const int KEY_PGUP = 4; -const int KEY_DELETE = 5; -const int KEY_END = 6; -const int KEY_PGDOWN = 7; - -const int KEY_UP = 14; -const int KEY_DOWN = 15; -const int KEY_LEFT = 16; -const int KEY_RIGHT = 17; - -const int KEY_F1 = 18; -const int KEY_F2 = 19; -const int KEY_F3 = 20; -const int KEY_F4 = 21; -const int KEY_F5 = 22; -const int KEY_F6 = 23; -const int KEY_F7 = 24; -const int KEY_F8 = 25; -const int KEY_F9 = 26; -const int KEY_F10 = 27; -const int KEY_F11 = 28; -const int KEY_F12 = 29; - -const int KEY_NUMDEL = 30; -const int KEY_NUMPAD0 = 31; -const int KEY_NUMPAD1 = 127; -const int KEY_NUMPAD2 = 128; -const int KEY_NUMPAD3 = 129; -const int KEY_NUMPAD4 = 130; -const int KEY_NUMPAD5 = 131; -const int KEY_NUMPAD6 = 132; -const int KEY_NUMPAD7 = 133; -const int KEY_NUMPAD8 = 134; -const int KEY_NUMPAD9 = 135; - -/// Function: getkey -/// Reads a key press (blocking) and returns a key code. -/// -/// See -/// -/// Note: -/// Only Arrows, Esc, Enter and Space are currently working properly. -int getkey(void) -{ - #ifndef WIN32 - int cnt = kbhit(); // for ANSI escapes processing - #endif - int k = getch(); - switch(k) - { - case 0: - { - int kk; - switch (kk = getch()) - { - case 71: return KEY_NUMPAD7; - case 72: return KEY_NUMPAD8; - case 73: return KEY_NUMPAD9; - case 75: return KEY_NUMPAD4; - case 77: return KEY_NUMPAD6; - case 79: return KEY_NUMPAD1; - case 80: return KEY_NUMPAD4; - case 81: return KEY_NUMPAD3; - case 82: return KEY_NUMPAD0; - case 83: return KEY_NUMDEL; - default: return kk-59+KEY_F1; // Function keys - } - } - case 224: - { - int kk; - switch (kk = getch()) - { - case 71: return KEY_HOME; - case 72: return KEY_UP; - case 73: return KEY_PGUP; - case 75: return KEY_LEFT; - case 77: return KEY_RIGHT; - case 79: return KEY_END; - case 80: return KEY_DOWN; - case 81: return KEY_PGDOWN; - case 82: return KEY_INSERT; - case 83: return KEY_DELETE; - default: return kk-123+KEY_F1; // Function keys - } - } - case 13: return KEY_ENTER; -#ifdef WIN32 - case 27: return KEY_ESCAPE; -#else // WIN32 - case 155: // single-character CSI - case 27: - { - // Process ANSI escape sequences - if (cnt >= 3 && getch() == '[') - { - switch (k = getch()) - { - case 'A': return KEY_UP; - case 'B': return KEY_DOWN; - case 'C': return KEY_RIGHT; - case 'D': return KEY_LEFT; - } - } else return KEY_ESCAPE; - } -#endif // WIN32 - default: return k; - } -} - -/// Function: nb_getch -/// Non-blocking getch(). Returns 0 if no key was pressed. -int inline nb_getch() { - if (kbhit()) return getch(); - else return 0; -} - -/// Function: getANSIColor -/// Return ANSI color escape sequence for specified number 0-15. -/// -/// See -RLUTIL_STRING_T getANSIColor(const int c) { - switch (c) { - case 0 : return ANSI_BLACK; - case 1 : return ANSI_BLUE; // non-ANSI - case 2 : return ANSI_GREEN; - case 3 : return ANSI_CYAN; // non-ANSI - case 4 : return ANSI_RED; // non-ANSI - case 5 : return ANSI_MAGENTA; - case 6 : return ANSI_BROWN; - case 7 : return ANSI_GREY; - case 8 : return ANSI_DARKGREY; - case 9 : return ANSI_LIGHTBLUE; // non-ANSI - case 10: return ANSI_LIGHTGREEN; - case 11: return ANSI_LIGHTCYAN; // non-ANSI; - case 12: return ANSI_LIGHTRED; // non-ANSI; - case 13: return ANSI_LIGHTMAGENTA; - case 14: return ANSI_YELLOW; // non-ANSI - case 15: return ANSI_WHITE; - default: return ""; - } -} - -/// Function: setColor -/// Change color specified by number (Windows / QBasic colors). -/// -/// See -void inline setColor(int c) { -#if defined(WIN32) && !defined(RLUTIL_USE_ANSI) - HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(hConsole, c); -#else - RLUTIL_PRINT(getANSIColor(c)); -#endif -} - -/// Function: cls -/// Clears screen and moves cursor home. -void inline cls() { -#if defined(WIN32) && !defined(RLUTIL_USE_ANSI) - // TODO: This is cheating... - system("cls"); -#else - RLUTIL_PRINT("\033[2J\033[H"); -#endif -} - -/// Function: locate -/// Sets the cursor position to 1-based x,y. -void locate(int x, int y) -{ -#if defined(WIN32) && !defined(RLUTIL_USE_ANSI) - COORD coord = {x-1, y-1}; // Windows uses 0-based coordinates - SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); -#else // WIN32 || USE_ANSI - #ifdef __cplusplus - std::ostringstream oss; - oss << "\033[" << y << ";" << x << "H"; - RLUTIL_PRINT(oss.str()); - #else // __cplusplus - char buf[32]; - sprintf(buf, "\033[%d;%df", y, x); - RLUTIL_PRINT(buf); - #endif // __cplusplus -#endif // WIN32 || USE_ANSI -} - -/// Function: hidecursor -/// Hides the cursor. -void inline hidecursor() -{ -#if defined(WIN32) && !defined(RLUTIL_USE_ANSI) - HANDLE hConsoleOutput; - CONSOLE_CURSOR_INFO structCursorInfo; - hConsoleOutput = GetStdHandle( STD_OUTPUT_HANDLE ); - GetConsoleCursorInfo( hConsoleOutput, &structCursorInfo ); // Get current cursor size - structCursorInfo.bVisible = FALSE; - SetConsoleCursorInfo( hConsoleOutput, &structCursorInfo ); -#else // WIN32 || USE_ANSI - RLUTIL_PRINT("\033[?25l"); -#endif // WIN32 || USE_ANSI -} - -/// Function: showcursor -/// Shows the cursor. -void inline showcursor() { -#if defined(WIN32) && !defined(RLUTIL_USE_ANSI) - HANDLE hConsoleOutput; - CONSOLE_CURSOR_INFO structCursorInfo; - hConsoleOutput = GetStdHandle( STD_OUTPUT_HANDLE ); - GetConsoleCursorInfo( hConsoleOutput, &structCursorInfo ); // Get current cursor size - structCursorInfo.bVisible = TRUE; - SetConsoleCursorInfo( hConsoleOutput, &structCursorInfo ); -#else // WIN32 || USE_ANSI - RLUTIL_PRINT("\033[?25h"); -#endif // WIN32 || USE_ANSI -} - -/// Function: msleep -/// Waits given number of milliseconds before continuing. -void inline msleep(unsigned int ms) { -#ifdef WIN32 - Sleep(ms); -#else - // usleep argument must be under 1 000 000 - if (ms > 1000) sleep(ms/1000000); - usleep((ms % 1000000) * 1000); -#endif -} - -// TODO: Allow optional message for anykey()? - -/// Function: anykey -/// Waits until a key is pressed. -void inline anykey() { - getch(); -} - -#ifndef min -/// Function: min -/// Returns the lesser of the two arguments. -#ifdef __cplusplus -template const T& min ( const T& a, const T& b ) { return (a const T& max ( const T& a, const T& b ) { return (b. -/// Hides the cursor and shows it again -/// when the object goes out of scope. -struct CursorHider { - CursorHider() { hidecursor(); } - ~CursorHider() { showcursor(); } -}; - -} // namespace rlutil -#endif diff --git a/plugins/kittens.cpp b/plugins/kittens.cpp index 3cb9c2b24..0261bc73a 100644 --- a/plugins/kittens.cpp +++ b/plugins/kittens.cpp @@ -1,16 +1,46 @@ #include #include #include -#include +#include +#include +#include + +using std::vector; +using std::string; +using namespace DFHack; +//FIXME: possible race conditions with calling kittens from the IO thread and shutdown from Core. +bool shutdown_flag = false; +bool final_flag = true; + +DFhackCExport command_result kittens (Core * c, vector & parameters); DFhackCExport const char * plugin_name ( void ) { return "kittens"; } -DFhackCExport int plugin_run (DFHack::Core * c) +DFhackCExport command_result plugin_init ( Core * c, std::vector &commands) +{ + commands.clear(); + commands.push_back(PluginCommand("kittens","Rainbow kittens. What else?",kittens)); + commands.push_back(PluginCommand("kittanz","Guess what. More rainbow kittenz.",kittens)); + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( Core * c ) +{ + shutdown_flag = true; + while(!final_flag) + { + c->con->msleep(60); + } + return CR_OK; +} + +DFhackCExport command_result kittens (Core * c, vector & parameters) { - DFHack::Console * con = c->con; + final_flag = false; + Console * con = c->con; const char * kittenz1 []= { " ____", @@ -28,6 +58,11 @@ DFhackCExport int plugin_run (DFHack::Core * c) int color = 1; while(1) { + if(shutdown_flag) + { + final_flag = true; + return CR_OK; + } con->color(color); int index = 0; const char * kit = kittenz1[index]; @@ -41,11 +76,9 @@ DFhackCExport int plugin_run (DFHack::Core * c) kit = kittenz1[index]; } dfout.flush(); - rlutil::msleep(60); // FIXME: replace! - con->clear(); + con->msleep(60); color ++; if(color > 15) color = 1; } - return 0; } diff --git a/plugins/reveal.cpp b/plugins/reveal.cpp index 1734bfb07..cd5ddd5c2 100644 --- a/plugins/reveal.cpp +++ b/plugins/reveal.cpp @@ -5,13 +5,11 @@ #include #include #include +#include #include #include -DFhackCExport const char * plugin_name ( void ) -{ - return "reveal"; -} +using namespace DFHack; struct hideblock { @@ -21,27 +19,58 @@ struct hideblock uint8_t hiddens [16][16]; }; -DFhackCExport int plugin_run (DFHack::Core * c) +// the saved data. we keep map size to check if things still match +uint32_t x_max, y_max, z_max; +std::vector hidesaved; +bool revealed = false; + +DFhackCExport command_result reveal(DFHack::Core * c, std::vector & params); +DFhackCExport command_result unreveal(DFHack::Core * c, std::vector & params); +DFhackCExport command_result revealtoggle(DFHack::Core * c, std::vector & params); +//DFhackCExport command_result revealclear(DFHack::Core * c, std::vector & params); + +DFhackCExport const char * plugin_name ( void ) +{ + return "reveal"; +} + +DFhackCExport command_result plugin_init ( Core * c, std::vector &commands) +{ + commands.clear(); + commands.push_back(PluginCommand("reveal","Reveal the map.",reveal)); + commands.push_back(PluginCommand("unreveal","Revert the map to its previous state.",unreveal)); + commands.push_back(PluginCommand("revealtoggle","Reveal/unreveal depending on state.",revealtoggle)); + //commands.push_back(PluginCommand("revealclear","Reset the reveal tool.",revealclear)); + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( Core * c ) +{ + return CR_OK; +} + +DFhackCExport command_result reveal(DFHack::Core * c, std::vector & params) { + DFHack::designations40d designations; + if(revealed) + { + dfout << "Map is already revealed or this is a different map." << std::endl; + return CR_FAILURE; + } c->Suspend(); DFHack::Maps *Maps =c->getMaps(); DFHack::World *World =c->getWorld(); - // init the map if(!Maps->Start()) { dfout << "Can't init map." << std::endl; c->Resume(); - return 1; + return CR_FAILURE; } dfout << "Revealing, please wait..." << std::endl; - - uint32_t x_max, y_max, z_max; - DFHack::designations40d designations; Maps->getSize(x_max,y_max,z_max); - std::vector hidesaved; - + hidesaved.reserve(x_max * y_max * z_max); for(uint32_t x = 0; x< x_max;x++) { for(uint32_t y = 0; y< y_max;y++) @@ -70,17 +99,47 @@ DFhackCExport int plugin_run (DFHack::Core * c) } } World->SetPauseState(true); + revealed = true; c->Resume(); dfout << "Map revealed. The game has been paused for you." << std::endl; dfout << "Unpausing can unleash the forces of hell!" << std::endl; dfout << "Saving will make this state permanent. Don't do it." << std::endl << std::endl; - dfout << "Press any key to unreveal." << std::endl; - std::cin.ignore(); - dfout << "Unrevealing... please wait." << std::endl; - // FIXME: do some consistency checks here! + dfout << "Run 'reveal' again to revert to previous state." << std::endl; + return CR_OK; +} + +DFhackCExport command_result unreveal(DFHack::Core * c, std::vector & params) +{ + DFHack::designations40d designations; + if(!revealed) + { + dfout << "There's nothing to revert!" << std::endl; + return CR_FAILURE; + } c->Suspend(); + DFHack::Maps *Maps =c->getMaps(); + DFHack::World *World =c->getWorld(); Maps = c->getMaps(); - Maps->Start(); + if(!Maps->Start()) + { + dfout << "Can't init map." << std::endl; + c->Resume(); + return CR_FAILURE; + } + + // Sanity check: map size + uint32_t x_max_b, y_max_b, z_max_b; + Maps->getSize(x_max_b,y_max_b,z_max_b); + if(x_max != x_max_b || y_max != y_max_b || z_max != z_max_b) + { + dfout << "The map is not of the same size..." << std::endl; + c->Resume(); + return CR_FAILURE; + } + + // FIXME: add more sanity checks / MAP ID + + dfout << "Unrevealing... please wait." << std::endl; for(size_t i = 0; i < hidesaved.size();i++) { hideblock & hb = hidesaved[i]; @@ -91,6 +150,21 @@ DFhackCExport int plugin_run (DFHack::Core * c) } Maps->WriteDesignations(hb.x,hb.y,hb.z, &designations); } + // give back memory. + hidesaved.clear(); + revealed = false; c->Resume(); - return 0; + return CR_OK; +} + +DFhackCExport command_result revealtoggle (DFHack::Core * c, std::vector & params) +{ + if(revealed) + { + return unreveal(c,params); + } + else + { + return reveal(c,params); + } }