Merged branch develop into develop

develop
Japa 2016-10-29 00:28:51 +05:30
commit 0edce2b3ae
13 changed files with 225 additions and 50 deletions

@ -72,6 +72,8 @@ Fixes
Misc Improvements
-----------------
- Documented all default keybindings (from :file:`dfhack.init-example`) in the
docs for the relevant commands; updates enforced by build system.
- `lua` and `gui/gm-editor` now support the same aliases (``scr``, ``unit``, etc.)
- `remotefortressreader`: Added support for

@ -15,15 +15,70 @@ serve to show the default.
# pylint:disable=redefined-builtin
import fnmatch
from io import open
from itertools import starmap
import os
import re
import shlex # pylint:disable=unused-import
import sys
# -- Support :dfhack-keybind:`command` ------------------------------------
# this is a custom directive that pulls info from dfhack.init-example
from docutils import nodes
from docutils.parsers.rst import roles
def get_keybinds():
"""Get the implemented keybinds, and return a dict of
{tool: [(full_command, keybinding, context), ...]}.
"""
with open('dfhack.init-example') as f:
lines = [l.replace('keybinding add', '').strip() for l in f.readlines()
if l.startswith('keybinding add')]
keybindings = dict()
for k in lines:
first, command = k.split(' ', 1)
bind, context = (first.split('@') + [''])[:2]
if ' ' not in command:
command = command.replace('"', '')
tool = command.split(' ')[0].replace('"', '')
keybindings[tool] = keybindings.get(tool, []) + [
(command, bind.split('-'), context)]
return keybindings
KEYBINDS = get_keybinds()
# pylint:disable=unused-argument,dangerous-default-value,too-many-arguments
def dfhack_keybind_role_func(role, rawtext, text, lineno, inliner,
options={}, content=[]):
"""Custom role parser for DFHack default keybinds."""
roles.set_classes(options)
if text not in KEYBINDS:
msg = inliner.reporter.error(
'no keybinding for {} in dfhack.init-example'.format(text),
line=lineno)
prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg]
newnode = nodes.paragraph()
for cmd, key, ctx in KEYBINDS[text]:
n = nodes.paragraph()
newnode += n
n += nodes.strong('Keybinding: ', 'Keybinding: ')
for k in key:
n += nodes.inline(k, k, classes=['kbd'])
if cmd != text:
n += nodes.inline(' -> ', ' -> ')
n += nodes.literal(cmd, cmd, classes=['guilabel'])
if ctx:
n += nodes.inline(' in ', ' in ')
n += nodes.literal(ctx, ctx)
return [newnode], []
roles.register_canonical_role('dfhack-keybind', dfhack_keybind_role_func)
# -- Autodoc for DFhack scripts -------------------------------------------
def doc_dir(dirname, files):
@ -46,28 +101,41 @@ def doc_dir(dirname, files):
command = line
def doc_all_dirs():
"""Collect the commands and paths to include in our docs."""
scripts = []
for root, _, files in os.walk('scripts'):
scripts.extend(doc_dir(root, files))
return tuple(scripts)
DOC_ALL_DIRS = doc_all_dirs()
def document_scripts():
"""Autodoc for files with the magic script documentation marker strings.
Returns a dict of script-kinds to lists of .rst include directives.
"""
# First, we collect the commands and paths to include in our docs
scripts = []
for root, _, files in os.walk('scripts'):
scripts.extend(doc_dir(root, files))
# Next we split by type and create include directives sorted by command
kinds = {'base': [], 'devel': [], 'fix': [], 'gui': [], 'modtools': []}
for s in scripts:
for s in DOC_ALL_DIRS:
k_fname = s[0].split('/', 1)
if len(k_fname) == 1:
kinds['base'].append(s)
else:
kinds[k_fname[0]].append(s)
template = '.. _{}:\n\n.. include:: /{}\n' +\
' :start-after: {}\n :end-before: {}\n'
return {key: '\n\n'.join(starmap(template.format, sorted(value)))
def template(arg):
tmp = '.. _{}:\n\n.. include:: /{}\n' +\
' :start-after: {}\n :end-before: {}\n'
if arg[0] in KEYBINDS:
tmp += '\n:dfhack-keybind:`{}`\n'.format(arg[0])
return tmp.format(*arg)
return {key: '\n\n'.join(map(template, sorted(value)))
for key, value in kinds.items()}
def write_script_docs():
"""
Creates a file for eack kind of script (base/devel/fix/gui/modtools)
@ -97,10 +165,23 @@ def write_script_docs():
outfile.write(kinds[k])
# Actually call the docs generator
write_script_docs()
def all_keybinds_documented():
"""Check that all keybindings are documented with the :dfhack-keybind:
directive somewhere."""
configured_binds = set(KEYBINDS)
script_commands = set(i[0] for i in DOC_ALL_DIRS)
with open('./docs/Plugins.rst') as f:
plugin_binds = set(re.findall(':dfhack-keybind:`(.*?)`', f.read()))
undocumented_binds = configured_binds - script_commands - plugin_binds
if undocumented_binds:
raise ValueError('The following DFHack commands have undocumented'
'keybindings: {}'.format(sorted(undocumented_binds)))
# Actually call the docs generator and run test
write_script_docs()
all_keybinds_documented()
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.

@ -221,7 +221,8 @@ DFHack 0.40.11-r1
=================
Internals
- Plugins on OS X now use ``.plug.dylib` as an extension instead of ``.plug.so``
---------
- Plugins on OS X now use ``.plug.dylib`` as an extension instead of ``.plug.so``
Fixes
-----

@ -353,7 +353,8 @@ This plugin adds an option to the :kbd:`q` menu when `enabled <enable>`.
command-prompt
==============
An in-game DFHack terminal, where you can enter other commands.
Best used from a keybinding; by default :kbd:`Ctrl`:kbd:`Shift`:kbd:`P`.
:dfhack-keybind:`command-prompt`
Usage: ``command-prompt [entry]``
@ -372,13 +373,11 @@ Otherwise somewhat similar to `gui/quickcmd`.
hotkeys
=======
Opens an in-game screen showing which DFHack keybindings are
active in the current context.
active in the current context. See also `hotkey-notes`.
.. image:: images/hotkeys.png
Type ``hotkeys`` into the DFHack console to open the screen,
or bind the command to a globally active hotkey. The default
keybinding is :kbd:`Ctrl`:kbd:`F1`. See also `hotkey-notes`.
:dfhack-keybind:`hotkeys`
.. _rb:
@ -407,7 +406,7 @@ military and social skills.
.. image:: images/manipulator2.png
Press :kbd:`t` to toggle between Profession and Squad view.
Press :kbd:`t` to toggle between Profession, Squad, and Job views.
.. image:: images/manipulator3.png
@ -442,6 +441,20 @@ The following mouse shortcuts are also available:
Pressing :kbd:`Esc` normally returns to the unit screen, but :kbd:`Shift`:kbd:`Esc` would exit
directly to the main dwarf mode screen.
Professions
-----------
The manipulator plugin supports saving Professions: a named set of Labors labors that can be
quickly applied to one or multiple Dwarves.
To save a Profession highlight a Dwarf and press :kbd:`P`. The Profession will be saved using
the Custom Profession Name of the Dwarf, or the default for that Dwarf if no Custom Profession
Name has been set.
To apply a Profession either highlight a single Dwarf, or select multiple with :kbd:`x`, and press
:kbd:`p` to select the Profession to apply. All labors for the selected Dwarves will be reset to
the labors of the chosen Profession.
.. comment - the link target "search" is reserved for the Sphinx search page
.. _search-plugin:
@ -659,12 +672,16 @@ Unit order examples::
The orderings are defined in ``hack/lua/plugins/sort/*.lua``
:dfhack-keybind:`sort-units`
.. _stocks:
stocks
======
Replaces the DF stocks screen with an improved version.
:dfhack-keybind:`stocks`
.. _stocksettings:
.. _stockpiles:
@ -676,6 +693,7 @@ See `gui/stockpiles` for an in-game interface.
:copystock: Copies the parameters of the currently highlighted stockpile to the custom
stockpile settings and switches to custom stockpile placement mode, effectively
allowing you to copy/paste stockpiles easily.
:dfhack-keybind:`copystock`
:savestock: Saves the currently highlighted stockpile's settings to a file in your Dwarf
Fortress folder. This file can be used to copy settings between game saves or
@ -874,7 +892,7 @@ Invoked as::
job-material <inorganic-token>
Intended to be used as a keybinding:
:dfhack-keybind:`job-material`
* In :kbd:`q` mode, when a job is highlighted within a workshop or furnace,
changes the material of the job. Only inorganic materials can be used
@ -887,6 +905,8 @@ job-duplicate
In :kbd:`q` mode, when a job is highlighted within a workshop or furnace
building, calling ``job-duplicate`` instantly duplicates the job.
:dfhack-keybind:`job-duplicate`
.. _autogems:
autogems
@ -1076,6 +1096,8 @@ spotclean
Works like ``clean map snow mud``, but only for the tile under the cursor. Ideal
if you want to keep that bloody entrance ``clean map`` would clean up.
:dfhack-keybind:`spotclean`
.. _autodump:
autodump
@ -1098,10 +1120,16 @@ Options:
:destroy-here: As ``destroy``, but only the selected item in the :kbd:`k` list,
or inside a container.
Alias ``autodump-destroy-here``, for keybindings.
:dfhack-keybind:`autodump-destroy-here`
:visible: Only process items that are not hidden.
:hidden: Only process hidden items.
:forbidden: Only process forbidden items (default: only unforbidden).
``autodump-destroy-item`` destroys the selected item, which may be selected
in the :kbd:`k` list, or inside a container. If called again before the game
is resumed, cancels destruction of the item.
:dfhack-keybind:`autodump-destroy-item`
cleanowned
==========
@ -1139,6 +1167,8 @@ Options:
:prefs: Show dwarf preferences summary
:reload: Reload configuration file (``dfhack-config/dwarfmonitor.json``)
:dfhack-keybind:`dwarfmonitor`
Widget configuration:
The following types of widgets (defined in :file:`hack/lua/plugins/dwarfmonitor.lua`)
@ -1226,7 +1256,16 @@ Options:
workNow
=======
Force all dwarves to look for a job immediately, or as soon as the game is unpaused.
Don't allow dwarves to idle if any jobs are available.
When workNow is active, every time the game pauses, DF will make dwarves
perform any appropriate available jobs. This includes when you one step
through the game using the pause menu. Usage:
:workNow: print workNow status
:workNow 0: deactivate workNow
:workNow 1: activate workNow (look for jobs on pause, and only then)
:workNow 2: make dwarves look for jobs whenever a job completes
.. _seedwatch:
@ -1264,6 +1303,8 @@ zone
====
Helps a bit with managing activity zones (pens, pastures and pits) and cages.
:dfhack-keybind:`zone`
Options:
:set: Set zone or cage under cursor as default for future assigns.
@ -1738,6 +1779,8 @@ Basic commands:
to remove designations, for if you accidentally set 50 levels at once.
:diglx: Also cross z-levels, digging stairs as needed. Alias for ``digl x``.
:dfhack-keybind:`digv`
.. _digexp:
digexp
@ -2209,6 +2252,8 @@ Usage:
* When viewing unit details, body-swaps into that unit.
* In the main adventure mode screen, reverts transient swap.
:dfhack-keybind:`adv-bodyswap`
.. _createitem:
createitem

@ -217,18 +217,37 @@ FILE(GLOB PROJECT_PROTOS ${CMAKE_CURRENT_SOURCE_DIR}/proto/*.proto)
STRING(REPLACE ".proto" ".pb.cc" PROJECT_PROTO_SRCS "${PROJECT_PROTOS}")
STRING(REPLACE ".proto" ".pb.h" PROJECT_PROTO_HDRS "${PROJECT_PROTOS}")
STRING(REPLACE "/proto/" "/proto/tmp/" PROJECT_PROTO_TMP_FILES "${PROJECT_PROTO_SRCS};${PROJECT_PROTO_HDRS}")
SET_SOURCE_FILES_PROPERTIES(${PROJECT_PROTO_SRCS} ${PROJECT_PROTO_HDRS}
PROPERTIES GENERATED TRUE)
# Force a re-gen if any *.pb.* files are missing
# (only runs when cmake is run, but better than nothing)
FOREACH(file IN LISTS PROJECT_PROTO_SRCS PROJECT_PROTO_HDRS)
IF(NOT EXISTS ${file})
# MESSAGE("Resetting generate_proto_core because '${file}' is missing")
FILE(REMOVE ${PROJECT_PROTO_TMP_FILES})
BREAK()
ENDIF()
ENDFOREACH()
LIST(APPEND PROJECT_HEADERS ${PROJECT_PROTO_HDRS})
LIST(APPEND PROJECT_SOURCES ${PROJECT_PROTO_SRCS})
ADD_CUSTOM_COMMAND(
OUTPUT ${PROJECT_PROTO_SRCS} ${PROJECT_PROTO_HDRS}
OUTPUT ${PROJECT_PROTO_TMP_FILES}
COMMAND protoc-bin -I=${CMAKE_CURRENT_SOURCE_DIR}/proto/
--cpp_out=dllexport_decl=DFHACK_EXPORT:${CMAKE_CURRENT_SOURCE_DIR}/proto/
--cpp_out=dllexport_decl=DFHACK_EXPORT:${CMAKE_CURRENT_SOURCE_DIR}/proto/tmp/
${PROJECT_PROTOS}
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${PROJECT_PROTO_TMP_FILES}
${CMAKE_CURRENT_SOURCE_DIR}/proto/
COMMENT "Generating core protobufs"
DEPENDS protoc-bin ${PROJECT_PROTOS}
)
ADD_CUSTOM_TARGET(generate_proto_core DEPENDS ${PROJECT_PROTO_TMP_FILES})
# Merge headers into sources
SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HEADERS} PROPERTIES HEADER_FILE_ONLY TRUE )
LIST(APPEND PROJECT_SOURCES ${PROJECT_HEADERS})
@ -299,7 +318,7 @@ ADD_CUSTOM_TARGET(git-describe
ADD_DEPENDENCIES(dfhack-version git-describe)
ADD_LIBRARY(dfhack SHARED ${PROJECT_SOURCES})
ADD_DEPENDENCIES(dfhack generate_headers)
ADD_DEPENDENCIES(dfhack generate_headers generate_proto_core)
ADD_LIBRARY(dfhack-client SHARED RemoteClient.cpp ColorText.cpp MiscUtils.cpp ${PROJECT_PROTO_SRCS})
ADD_DEPENDENCIES(dfhack-client dfhack)

@ -0,0 +1 @@
*.pb.*

@ -59,7 +59,7 @@ fi
# Now run
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"./hack/libs":"./hack"
export LD_LIBRARY_PATH="./hack/libs:./hack:$LD_LIBRARY_PATH"
PRELOAD_LIB="${PRELOAD_LIB:+$PRELOAD_LIB:}./hack/libdfhack.so"

@ -44,16 +44,33 @@ FILE(GLOB PROJECT_PROTOS ${CMAKE_CURRENT_SOURCE_DIR}/proto/*.proto)
STRING(REPLACE ".proto" ".pb.cc" PROJECT_PROTO_SRCS "${PROJECT_PROTOS}")
STRING(REPLACE ".proto" ".pb.h" PROJECT_PROTO_HDRS "${PROJECT_PROTOS}")
STRING(REPLACE "/proto/" "/proto/tmp/" PROJECT_PROTO_TMP_FILES "${PROJECT_PROTO_SRCS};${PROJECT_PROTO_HDRS}")
SET_SOURCE_FILES_PROPERTIES(${PROJECT_PROTO_SRCS} ${PROJECT_PROTO_HDRS}
PROPERTIES GENERATED TRUE)
# Force a re-gen if any *.pb.* files are missing
# (only runs when cmake is run, but better than nothing)
FOREACH(file IN LISTS PROJECT_PROTO_SRCS PROJECT_PROTO_HDRS)
IF(NOT EXISTS ${file})
# MESSAGE("Resetting generate_proto because '${file}' is missing")
FILE(REMOVE ${PROJECT_PROTO_TMP_FILES})
BREAK()
ENDIF()
ENDFOREACH()
ADD_CUSTOM_COMMAND(
OUTPUT ${PROJECT_PROTO_SRCS} ${PROJECT_PROTO_HDRS}
OUTPUT ${PROJECT_PROTO_TMP_FILES}
COMMAND protoc-bin -I=${dfhack_SOURCE_DIR}/library/proto/
-I=${CMAKE_CURRENT_SOURCE_DIR}/proto/
--cpp_out=${CMAKE_CURRENT_SOURCE_DIR}/proto/
--cpp_out=${CMAKE_CURRENT_SOURCE_DIR}/proto/tmp/
${PROJECT_PROTOS}
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${PROJECT_PROTO_TMP_FILES}
${CMAKE_CURRENT_SOURCE_DIR}/proto/
COMMENT "Generating plugin protobufs"
DEPENDS protoc-bin ${PROJECT_PROTOS}
)
add_custom_target(generate_proto DEPENDS ${PROJECT_PROTO_SRCS} ${PROJECT_PROTO_HDRS})
ADD_CUSTOM_TARGET(generate_proto DEPENDS ${PROJECT_PROTO_TMP_FILES})
SET_SOURCE_FILES_PROPERTIES( Brushes.h PROPERTIES HEADER_FILE_ONLY TRUE )

@ -69,7 +69,7 @@ void outputHex(uint8_t *buf,uint8_t *lbuf,size_t len,size_t start,color_ostream
for(size_t i=0;i<len;i+=page_size)
{
//con.gotoxy(1,i/page_size+1);
con.print("0x%08X ",i+start);
con.print("0x%08lX ",i+start);
for(size_t j=0;(j<page_size) && (i+j<len);j++)
{
if(j%sizeof(void*)==0)

@ -0,0 +1 @@
*.pb.*

@ -577,10 +577,14 @@ sub render_global_objects {
# define friendlier accessors, eg df.world -> DFHack::GlobalObjects.new._at(0).world
indent_rb {
push @lines_rb, "Global = GlobalObjects.new._at(0)";
for my $obj (@global_objects)
for my $oname (@global_objects)
{
push @lines_rb, "def self.$obj ; Global.$obj ; end";
push @lines_rb, "def self.$obj=(v) ; Global.$obj = v ; end";
push @lines_rb, "if DFHack.get_global_address('$oname') != 0";
indent_rb {
push @lines_rb, "def self.$oname ; Global.$oname ; end";
push @lines_rb, "def self.$oname=(v) ; Global.$oname = v ; end";
};
push @lines_rb, "end";
}
};
}
@ -743,7 +747,7 @@ sub sizeof {
} elsif ($subtype eq 'df-linked-list') {
return 3 * $SIZEOF_PTR;
} elsif ($subtype eq 'df-flagarray') {
return 4 + $SIZEOF_PTR;
return 2 * $SIZEOF_PTR; # XXX length may be 4 on windows?
} elsif ($subtype eq 'df-static-flagarray') {
return $field->getAttribute('count');
} elsif ($subtype eq 'df-array') {

@ -214,15 +214,13 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
if (!onupdate_active)
return CR_OK;
if (df::global::cur_year && (*df::global::cur_year < onupdate_minyear))
if (df::global::cur_year && *df::global::cur_year < onupdate_minyear)
return CR_OK;
if (df::global::cur_year_tick && onupdate_minyeartick >= 0 &&
(*df::global::cur_year == onupdate_minyear &&
*df::global::cur_year_tick < onupdate_minyeartick))
*df::global::cur_year_tick < onupdate_minyeartick)
return CR_OK;
if (df::global::cur_year_tick_advmode && onupdate_minyeartickadv >= 0 &&
(*df::global::cur_year == onupdate_minyear &&
*df::global::cur_year_tick_advmode < onupdate_minyeartickadv))
*df::global::cur_year_tick_advmode < onupdate_minyeartickadv)
return CR_OK;
return plugin_eval_ruby(out, "DFHack.onupdate");
@ -281,15 +279,13 @@ static command_result df_rubyeval(color_ostream &out, std::vector <std::string>
// - ruby.h with gcc -m32 on linux 64 is broken
// so we dynamically load libruby with dlopen/LoadLibrary
// lib path is hardcoded here, and by default downloaded by cmake
// this code should work with ruby1.9, but ruby1.9 doesn't like running
// in a dedicated non-main thread, so use ruby1.8 binaries only for now
typedef uintptr_t VALUE;
typedef uintptr_t ID;
#define Qfalse ((VALUE)0)
#define Qtrue ((VALUE)2)
#define Qnil ((VALUE)4)
static VALUE Qfalse = 0;
static VALUE Qtrue = 2;
static VALUE Qnil = 4;
#define INT2FIX(i) ((VALUE)((((intptr_t)i) << 1) | 1))
#define FIX2INT(i) (((intptr_t)i) >> 1)
@ -359,6 +355,7 @@ static int df_loadruby(void)
rbloadsym(rb_uint2inum);
rbloadsym(rb_num2ulong);
#undef rbloadsym
// rb_float_new_in_heap in ruby 2
if (!((rb_float_new = (decltype(rb_float_new))(LookupPlugin(libruby_handle, "rb_float_new"))) ||
(rb_float_new = (decltype(rb_float_new))(LookupPlugin(libruby_handle, "rb_float_new_in_heap")))))
return 0;
@ -436,6 +433,11 @@ static void df_rubythread(void *p)
r_result = CR_OK;
r_type = RB_IDLE;
// initialize ruby constants (may depend on libruby compilation flags/version)
Qnil = rb_eval_string_protect("nil", &state);
Qtrue = rb_eval_string_protect("true", &state);
Qfalse = rb_eval_string_protect("false", &state);
// load the default ruby-level definitions in the background
state=0;
rb_eval_string_protect("require './hack/ruby/ruby'", &state);
@ -1056,8 +1058,8 @@ static VALUE rb_dfmemory_set_clear(VALUE self, VALUE set)
/* call an arbitrary object virtual method */
#if defined(_WIN32) && !defined(_WIN64)
__declspec(naked) static int raw_vcall(void *that, void *fptr, unsigned long a0,
unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5)
__declspec(naked) static intptr_t raw_vcall(void *that, void *fptr, uintptr_t a0,
uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5)
{
// __thiscall requires that the callee cleans up the stack
// here we dont know how many arguments it will take, so
@ -1083,10 +1085,11 @@ __declspec(naked) static int raw_vcall(void *that, void *fptr, unsigned long a0,
}
}
#else
static int raw_vcall(void *that, void *fptr, unsigned long a0,
unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5)
static intptr_t raw_vcall(void *that, void *fptr, uintptr_t a0,
uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5)
{
int (*t_fptr)(void *me, int, int, int, int, int, int);
intptr_t (*t_fptr)(void *me, uintptr_t, uintptr_t, uintptr_t,
uintptr_t, uintptr_t, uintptr_t);
t_fptr = (decltype(t_fptr))fptr;
return t_fptr(that, a0, a1, a2, a3, a4, a5);
}

@ -109,13 +109,14 @@ module DFHack
def onupdate
@onupdate_list ||= []
y = cur_year
y = yt = 0
y = cur_year rescue 0
ytmax = TICKS_PER_YEAR
if df.gamemode == :ADVENTURE and df.respond_to?(:cur_year_tick_advmode)
yt = cur_year_tick_advmode
ytmax *= 144
else
yt = cur_year_tick
yt = cur_year_tick rescue 0
end
@onupdate_list.each { |o|