Plugin manager, reworked kittens and reveal.

develop
Petr Mrázek 2011-06-25 05:35:29 +02:00
parent 0bb097296a
commit 6fd7d42f00
13 changed files with 484 additions and 641 deletions

@ -49,6 +49,7 @@ include/dfhack/modules/World.h
SET(PROJECT_SRCS SET(PROJECT_SRCS
Core.cpp Core.cpp
PluginManager.cpp
VersionInfo.cpp VersionInfo.cpp
VersionInfoFactory.cpp VersionInfoFactory.cpp
TileTypes.cpp TileTypes.cpp

@ -117,3 +117,9 @@ void Console::cursor(bool enable)
dfout <<"\033[?25l"; dfout <<"\033[?25l";
} }
} }
void Console::msleep (unsigned int msec)
{
if (msec > 1000) sleep(msec/1000000);
usleep((msec % 1000000) * 1000);
}

@ -96,14 +96,17 @@ Console::Console()
clear(); clear();
// result is a terminal controlled by the parasitic code! // result is a terminal controlled by the parasitic code!
} }
Console::~Console() Console::~Console()
{ {
FreeConsole(); FreeConsole();
} }
void Console::clear() void Console::clear()
{ {
system("cls"); system("cls");
} }
void Console::gotoxy(int x, int y) void Console::gotoxy(int x, int y)
{ {
COORD coord = {x-1, y-1}; // Windows uses 0-based coordinates COORD coord = {x-1, y-1}; // Windows uses 0-based coordinates
@ -137,3 +140,8 @@ void Console::cursor(bool enable)
SetConsoleCursorInfo( hConsoleOutput, &structCursorInfo ); SetConsoleCursorInfo( hConsoleOutput, &structCursorInfo );
} }
} }
void Console::msleep (unsigned int msec)
{
Sleep(msec);
}

@ -50,7 +50,13 @@ namespace DFHack
{ {
DFLibrary * OpenPlugin (const char * filename) 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) void * LookupPlugin (DFLibrary * plugin ,const char * function)
{ {

@ -30,6 +30,8 @@ distribution.
#include <set> #include <set>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <iterator>
#include <sstream>
using namespace std; using namespace std;
#include "dfhack/Error.h" #include "dfhack/Error.h"
@ -37,85 +39,33 @@ using namespace std;
#include "dfhack/Core.h" #include "dfhack/Core.h"
#include "dfhack/Console.h" #include "dfhack/Console.h"
#include "dfhack/VersionInfoFactory.h" #include "dfhack/VersionInfoFactory.h"
#include "dfhack/PluginManager.h"
#include "ModuleFactory.h" #include "ModuleFactory.h"
#include "dfhack/modules/Gui.h" #include "dfhack/modules/Gui.h"
#include "dfhack/modules/Vegetation.h" #include "dfhack/modules/Vegetation.h"
#include "dfhack/modules/Maps.h" #include "dfhack/modules/Maps.h"
#include "dfhack/modules/World.h" #include "dfhack/modules/World.h"
#include "dfhack/extra/rlutil.h"
#include <stdio.h> #include <stdio.h>
#ifdef LINUX_BUILD
#include <dirent.h>
#include <errno.h>
#else
#include "wdirent.h"
#endif
using namespace DFHack; using namespace DFHack;
static int getdir (string dir, vector<string> &files) void cheap_tokenise(string const& input, vector<string> &output)
{ {
DIR *dp; istringstream str(input);
struct dirent *dirp; istream_iterator<string> cur(str), end;
if((dp = opendir(dir.c_str())) == NULL) output.assign(cur, end);
{
dfout << "Error(" << errno << ") opening " << dir << endl;
return errno;
}
while ((dirp = readdir(dp)) != NULL) {
files.push_back(string(dirp->d_name));
}
closedir(dp);
return 0;
} }
struct IODATA
int fIOthread(void * _core)
{ {
Core * core = (Core *) _core; Core * core;
PluginManager * plug_mgr;
#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
vector <string> filez; int fIOthread(void * iodata)
map <string, int (*)(Core *)> plugins; {
getdir(path, filez); Core * core = ((IODATA*) iodata)->core;
const char * (*_PlugName)(void) = 0; PluginManager * plug_mgr = ((IODATA*) iodata)->plug_mgr;
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;
}
}
fprintf(dfout_C,"DFHack is ready. Have a nice day! Type in '?' or 'help' for help.\n"); fprintf(dfout_C,"DFHack is ready. Have a nice day! Type in '?' or 'help' for help.\n");
//dfterm << << endl; //dfterm << << endl;
int clueless_counter = 0; int clueless_counter = 0;
@ -132,10 +82,6 @@ int fIOthread(void * _core)
if(command=="help" || command == "?") if(command=="help" || command == "?")
{ {
dfout << "Available commands:" << endl; dfout << "Available commands:" << endl;
for (map <string, int (*)(Core *)>::iterator iter = plugins.begin(); iter != plugins.end(); iter++)
{
dfout << iter->first << endl;
}
} }
else if( command == "" ) else if( command == "" )
{ {
@ -143,16 +89,27 @@ int fIOthread(void * _core)
} }
else else
{ {
map <string, int (*)(Core *)>::iterator iter = plugins.find(command); vector <string> parts;
if(iter != plugins.end()) cheap_tokenise(command,parts);
if(parts.size() == 0)
{ {
iter->second(core); clueless_counter++;
} }
else else
{
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; dfout << "Invalid command." << endl;
clueless_counter ++; clueless_counter ++;
} }
else if(res == CR_FAILURE)
{
dfout << "ERROR!" << endl;
}
}
} }
if(clueless_counter == 3) if(clueless_counter == 3)
{ {
@ -166,6 +123,7 @@ Core::Core()
{ {
// init the console. This must be always the first step! // init the console. This must be always the first step!
con = new Console(); con = new Console();
plug_mgr = 0;
// find out what we are... // find out what we are...
vif = new DFHack::VersionInfoFactory("Memory.xml"); vif = new DFHack::VersionInfoFactory("Memory.xml");
p = new DFHack::Process(vif); p = new DFHack::Process(vif);
@ -195,9 +153,13 @@ Core::Core()
errorstate = false; errorstate = false;
// lock mutex // lock mutex
SDL_mutexP(AccessMutex); SDL_mutexP(AccessMutex);
plug_mgr = new PluginManager(this);
// look for all plugins, // look for all plugins,
// create IO thread // 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. // and let DF do its thing.
}; };
@ -230,8 +192,11 @@ int Core::Update()
int Core::Shutdown ( void ) int Core::Shutdown ( void )
{ {
errorstate = 1; errorstate = 1;
// TODO:shutdown all plugins if(plug_mgr)
{
delete plug_mgr;
plug_mgr = 0;
}
// invalidate all modules // invalidate all modules
for(unsigned int i = 0 ; i < allModules.size(); i++) for(unsigned int i = 0 ; i < allModules.size(); i++)
{ {

@ -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 <string>
#include <vector>
#include <map>
using namespace std;
#ifdef LINUX_BUILD
#include <dirent.h>
#include <errno.h>
#else
#include "wdirent.h"
#endif
static int getdir (string dir, vector<string> &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 <PluginCommand> &)) 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 <string> 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 <std::string> & parameters)
{
Core * c = &Core::getInstance();
map <string, PluginCommand *>::iterator iter = commands.find(command);
if(iter != commands.end())
{
return iter->second->function(c,parameters);
}
return CR_NOT_IMPLEMENTED;
}
/*
for (map <string, int (*)(Core *)>::iterator iter = plugins.begin(); iter != plugins.end(); iter++)
{
dfout << iter->first << endl;
}
*/
/*
*/

@ -38,9 +38,15 @@ namespace DFHack
public: public:
Console(); Console();
~Console(); ~Console();
/// Clear the console, along with its scrollback
void clear(); void clear();
/// Position cursor at x,y. 1,1 = top left corner
void gotoxy(int x, int y); void gotoxy(int x, int y);
/// Set color (ANSI color number)
void color(int index); void color(int index);
/// Enable or disable the caret/cursor
void cursor(bool enable = true); void cursor(bool enable = true);
/// Waits given number of milliseconds before continuing.
void msleep(unsigned int msec);
}; };
} }

@ -48,6 +48,7 @@ namespace DFHack
class VersionInfo; class VersionInfo;
class VersionInfoFactory; class VersionInfoFactory;
class Console; class Console;
class PluginManager;
DFLibrary * OpenPlugin (const char * filename); DFLibrary * OpenPlugin (const char * filename);
void * LookupPlugin (DFLibrary * plugin ,const char * function); void * LookupPlugin (DFLibrary * plugin ,const char * function);
@ -100,7 +101,7 @@ namespace DFHack
DFHack::Process * p; DFHack::Process * p;
DFHack::VersionInfo * vinfo; DFHack::VersionInfo * vinfo;
Console * con; DFHack::Console * con;
private: private:
Core(); Core();
int Update (void); int Update (void);
@ -130,5 +131,6 @@ namespace DFHack
Constructions * pConstructions; Constructions * pConstructions;
} s_mods; } s_mods;
std::vector <Module *> allModules; std::vector <Module *> allModules;
DFHack::PluginManager * plug_mgr;
}; };
} }

@ -30,7 +30,6 @@ distribution.
#include "dfhack/Export.h" #include "dfhack/Export.h"
namespace DFHack namespace DFHack
{ {
class Context;
/** /**
* The parent class for all DFHack modules * The parent class for all DFHack modules
* \ingroup grp_modules * \ingroup grp_modules

@ -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 <map>
#include <string>
#include <vector>
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 <std::string> &)
)
{
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 <std::string> &);
};
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 <PluginCommand> commands;
std::string filename;
std::string name;
DFLibrary * plugin_lib;
bool loaded;
command_result (*plugin_init)(Core *, std::vector <PluginCommand> &);
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 <std::string> & parameters);
private:
std::map <std::string, PluginCommand *> commands;
std::vector <Plugin *> all_plugins;
std::string plugin_path;
};
}

@ -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 <iostream>
#include <string>
#include <sstream>
/// Namespace forward declarations
namespace rlutil
{
void locate(int x, int y);
}
#endif // __cplusplus
#ifdef WIN32
#include <windows.h> // for WinAPI and Sleep()
#include <conio.h> // for getch() and kbhit()
#else
#ifdef __cplusplus
#include <cstdio> // for getch()
#else // __cplusplus
#include <stdio.h> // for getch()
#endif // __cplusplus
#include <termios.h> // for getch() and kbhit()
#include <unistd.h> // for getch(), kbhit() and (u)sleep()
#include <sys/ioctl.h> // for getkey()
#include <sys/types.h> // for kbhit()
#include <sys/time.h> // 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 <rlutil.locate>.
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 <getch>, <kbhit> and <gotoxy> 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 <Key codes for keyhit()>
///
/// 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 <Color Codes>
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 <Color Codes>
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 <class T> const T& min ( const T& a, const T& b ) { return (a<b)?a:b; }
#else
#define min(a,b) (((a)<(b))?(a):(b))
#endif // __cplusplus
#endif // min
#ifndef max
/// Function: max
/// Returns the greater of the two arguments.
#ifdef __cplusplus
template <class T> const T& max ( const T& a, const T& b ) { return (b<a)?a:b; }
#else
#define max(a,b) (((b)<(a))?(a):(b))
#endif // __cplusplus
#endif // max
// Classes are here at the end so that documentation is pretty.
#ifdef __cplusplus
/// Class: CursorHider
/// RAII OOP wrapper for <rlutil.hidecursor>.
/// Hides the cursor and shows it again
/// when the object goes out of scope.
struct CursorHider {
CursorHider() { hidecursor(); }
~CursorHider() { showcursor(); }
};
} // namespace rlutil
#endif

@ -1,16 +1,46 @@
#include <dfhack/Core.h> #include <dfhack/Core.h>
#include <dfhack/Console.h> #include <dfhack/Console.h>
#include <dfhack/Export.h> #include <dfhack/Export.h>
#include <dfhack/extra/rlutil.h> #include <dfhack/PluginManager.h>
#include <vector>
#include <string>
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 <string> & parameters);
DFhackCExport const char * plugin_name ( void ) DFhackCExport const char * plugin_name ( void )
{ {
return "kittens"; return "kittens";
} }
DFhackCExport int plugin_run (DFHack::Core * c) DFhackCExport command_result plugin_init ( Core * c, std::vector <PluginCommand> &commands)
{ {
DFHack::Console * con = c->con; 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 <string> & parameters)
{
final_flag = false;
Console * con = c->con;
const char * kittenz1 []= const char * kittenz1 []=
{ {
" ____", " ____",
@ -28,6 +58,11 @@ DFhackCExport int plugin_run (DFHack::Core * c)
int color = 1; int color = 1;
while(1) while(1)
{ {
if(shutdown_flag)
{
final_flag = true;
return CR_OK;
}
con->color(color); con->color(color);
int index = 0; int index = 0;
const char * kit = kittenz1[index]; const char * kit = kittenz1[index];
@ -41,11 +76,9 @@ DFhackCExport int plugin_run (DFHack::Core * c)
kit = kittenz1[index]; kit = kittenz1[index];
} }
dfout.flush(); dfout.flush();
rlutil::msleep(60); // FIXME: replace! con->msleep(60);
con->clear();
color ++; color ++;
if(color > 15) if(color > 15)
color = 1; color = 1;
} }
return 0;
} }

@ -5,13 +5,11 @@
#include <dfhack/Core.h> #include <dfhack/Core.h>
#include <dfhack/Console.h> #include <dfhack/Console.h>
#include <dfhack/Export.h> #include <dfhack/Export.h>
#include <dfhack/PluginManager.h>
#include <dfhack/modules/Maps.h> #include <dfhack/modules/Maps.h>
#include <dfhack/modules/World.h> #include <dfhack/modules/World.h>
DFhackCExport const char * plugin_name ( void ) using namespace DFHack;
{
return "reveal";
}
struct hideblock struct hideblock
{ {
@ -21,27 +19,58 @@ struct hideblock
uint8_t hiddens [16][16]; 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 <hideblock> hidesaved;
bool revealed = false;
DFhackCExport command_result reveal(DFHack::Core * c, std::vector<std::string> & params);
DFhackCExport command_result unreveal(DFHack::Core * c, std::vector<std::string> & params);
DFhackCExport command_result revealtoggle(DFHack::Core * c, std::vector<std::string> & params);
//DFhackCExport command_result revealclear(DFHack::Core * c, std::vector<std::string> & params);
DFhackCExport const char * plugin_name ( void )
{
return "reveal";
}
DFhackCExport command_result plugin_init ( Core * c, std::vector <PluginCommand> &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<std::string> & params)
{ {
DFHack::designations40d designations;
if(revealed)
{
dfout << "Map is already revealed or this is a different map." << std::endl;
return CR_FAILURE;
}
c->Suspend(); c->Suspend();
DFHack::Maps *Maps =c->getMaps(); DFHack::Maps *Maps =c->getMaps();
DFHack::World *World =c->getWorld(); DFHack::World *World =c->getWorld();
// init the map // init the map
if(!Maps->Start()) if(!Maps->Start())
{ {
dfout << "Can't init map." << std::endl; dfout << "Can't init map." << std::endl;
c->Resume(); c->Resume();
return 1; return CR_FAILURE;
} }
dfout << "Revealing, please wait..." << std::endl; 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); Maps->getSize(x_max,y_max,z_max);
std::vector <hideblock> hidesaved; hidesaved.reserve(x_max * y_max * z_max);
for(uint32_t x = 0; x< x_max;x++) for(uint32_t x = 0; x< x_max;x++)
{ {
for(uint32_t y = 0; y< y_max;y++) for(uint32_t y = 0; y< y_max;y++)
@ -70,17 +99,47 @@ DFhackCExport int plugin_run (DFHack::Core * c)
} }
} }
World->SetPauseState(true); World->SetPauseState(true);
revealed = true;
c->Resume(); c->Resume();
dfout << "Map revealed. The game has been paused for you." << std::endl; dfout << "Map revealed. The game has been paused for you." << std::endl;
dfout << "Unpausing can unleash the forces of hell!" << 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 << "Saving will make this state permanent. Don't do it." << std::endl << std::endl;
dfout << "Press any key to unreveal." << std::endl; dfout << "Run 'reveal' again to revert to previous state." << std::endl;
std::cin.ignore(); return CR_OK;
dfout << "Unrevealing... please wait." << std::endl; }
// FIXME: do some consistency checks here!
DFhackCExport command_result unreveal(DFHack::Core * c, std::vector<std::string> & params)
{
DFHack::designations40d designations;
if(!revealed)
{
dfout << "There's nothing to revert!" << std::endl;
return CR_FAILURE;
}
c->Suspend(); c->Suspend();
DFHack::Maps *Maps =c->getMaps();
DFHack::World *World =c->getWorld();
Maps = c->getMaps(); 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++) for(size_t i = 0; i < hidesaved.size();i++)
{ {
hideblock & hb = hidesaved[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); Maps->WriteDesignations(hb.x,hb.y,hb.z, &designations);
} }
// give back memory.
hidesaved.clear();
revealed = false;
c->Resume(); c->Resume();
return 0; return CR_OK;
}
DFhackCExport command_result revealtoggle (DFHack::Core * c, std::vector<std::string> & params)
{
if(revealed)
{
return unreveal(c,params);
}
else
{
return reveal(c,params);
}
} }