Merge remote-tracking branch 'dfhack/develop' into remote_reader

develop
JapaMala 2018-07-31 19:46:31 +05:30
commit 5e95bd9cf2
73 changed files with 1727 additions and 400 deletions

4
.gitignore vendored

@ -56,3 +56,7 @@ tags
#VS is annoying about this one.
/build/win64/DF_PATH.txt
/build/win32/DF_PATH.txt
/.vs
# custom plugins
/plugins/CMakeLists.custom.txt

@ -13,6 +13,7 @@ addons:
- libsdl1.2-dev
- libxml-libxml-perl
- libxml-libxslt-perl
- ninja-build
- zlib1g-dev
matrix:
include:
@ -32,6 +33,7 @@ before_install:
- sh travis/build-lua.sh
- sh travis/download-df.sh
- echo "export DFHACK_HEADLESS=1" >> "$HOME/.dfhackrc"
- echo "export DFHACK_DISABLE_CONSOLE=1" >> "$HOME/.dfhackrc"
script:
- export PATH="$PATH:$HOME/lua53/bin"
- git tag tmp-travis-build
@ -45,12 +47,18 @@ script:
- python travis/script-syntax.py --ext=rb --cmd="ruby -c"
- mkdir build-travis
- cd build-travis
- cmake .. -DCMAKE_C_COMPILER=gcc-$GCC_VERSION -DCMAKE_CXX_COMPILER=g++-$GCC_VERSION -DDFHACK_BUILD_ARCH=64 -DBUILD_DOCS:BOOL=ON -DCMAKE_INSTALL_PREFIX="$DF_FOLDER"
- make -j3 install
- cmake .. -G Ninja -DCMAKE_C_COMPILER=gcc-$GCC_VERSION -DCMAKE_CXX_COMPILER=g++-$GCC_VERSION -DDFHACK_BUILD_ARCH=64 -DBUILD_DOCS:BOOL=ON -DCMAKE_INSTALL_PREFIX="$DF_FOLDER"
- ninja -j3 install
- mv "$DF_FOLDER"/dfhack.init-example "$DF_FOLDER"/dfhack.init
- cd ..
- cp travis/dfhack_travis.init "$DF_FOLDER"/
- python travis/run-tests.py "$DF_FOLDER"
- python travis/check-rpc.py "$DF_FOLDER/dfhack-rpc.txt"
before_cache:
- cat "$DF_FOLDER/stderr.log"
- rm -rf "$DF_FOLDER/hack"
- rm -rf "$DF_FOLDER/dfhack-config"
- rm -f "$DF_FOLDER"/*.log
notifications:
email: false
irc:

@ -2,12 +2,16 @@
# Set up build types
if(CMAKE_CONFIGURATION_TYPES)
SET(CMAKE_CONFIGURATION_TYPES Release RelWithDebInfo)
SET(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}" CACHE STRING "List of supported configuration types" FORCE)
SET(CMAKE_CONFIGURATION_TYPES "Release;RelWithDebInfo" CACHE STRING "List of supported configuration types" FORCE)
else(CMAKE_CONFIGURATION_TYPES)
set(DFHACK_TYPE_HELP "Choose the type of build, options are: Release and RelWithDebInfo")
# Prevent cmake C module attempts to overwrite our help string
if (NOT CMAKE_BUILD_TYPE)
SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Release RelWithDebInfo.")
SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "${DFHACK_TYPE_HELP}")
else (NOT CMAKE_BUILD_TYPE)
SET(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING "${DFHACK_TYPE_HELP}")
endif (NOT CMAKE_BUILD_TYPE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Release;RelWithDebInfo")
endif(CMAKE_CONFIGURATION_TYPES)
OPTION(BUILD_DOCS "Choose whether to build the documentation (requires python and Sphinx)." OFF)
@ -44,8 +48,8 @@ if(UNIX)
endif()
if(WIN32)
if((NOT MSVC) OR (NOT MSVC_VERSION STREQUAL 1900))
message(SEND_ERROR "MSVC 2015 is required")
if((NOT MSVC) OR (MSVC_VERSION LESS 1900) OR (MSVC_VERSION GREATER 1919))
message(SEND_ERROR "MSVC 2015 or 2017 is required")
endif()
endif()
@ -83,7 +87,7 @@ IF(MSVC AND NOT DEFINED DFHACK_BUILD_ARCH)
SET(DFHACK_BUILD_ARCH "32")
ENDIF()
ELSE()
SET(DFHACK_BUILD_ARCH "32" CACHE STRING "Architecture to build ('32' or '64')")
SET(DFHACK_BUILD_ARCH "64" CACHE STRING "Architecture to build ('32' or '64')")
ENDIF()
IF("${DFHACK_BUILD_ARCH}" STREQUAL "32")
@ -164,13 +168,15 @@ endif()
# set up versioning.
set(DF_VERSION "0.44.12")
set(DFHACK_RELEASE "alpha0")
set(DFHACK_PRERELEASE TRUE)
set(DFHACK_RELEASE "r1")
set(DFHACK_PRERELEASE FALSE)
set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}")
set(DFHACK_ABI_VERSION 1)
set(DFHACK_BUILD_ID "" CACHE STRING "Build ID (should be specified on command line)")
## where to install things (after the build is done, classic 'make install' or package structure)
# the dfhack libraries will be installed here:
IF(UNIX)
@ -400,6 +406,7 @@ file(WRITE "${CMAKE_BINARY_DIR}/dfhack_setarch.txt" ${DFHACK_SETARCH})
install(FILES "${CMAKE_BINARY_DIR}/dfhack_setarch.txt" DESTINATION "${DFHACK_DATA_DESTINATION}")
install(DIRECTORY dfhack-config/ DESTINATION dfhack-config/default)
install(DIRECTORY test DESTINATION "${DFHACK_DATA_DESTINATION}")
#build the plugins
IF(BUILD_PLUGINS)
@ -477,9 +484,54 @@ IF(APPLE)
ELSE()
set(DFHACK_PACKAGE_PLATFORM_NAME ${CMAKE_SYSTEM_NAME})
ENDIF()
set(CPACK_PACKAGE_FILE_NAME "dfhack-${DFHACK_VERSION}-${DFHACK_PACKAGE_PLATFORM_NAME}-${DFHACK_BUILD_ARCH}${DFHACK_PACKAGE_SUFFIX}")
# set on command line
if(DFHACK_BUILD_ID STREQUAL "")
set(DFHACK_BUILD_ID_PACKAGE "")
else()
set(DFHACK_BUILD_ID_PACKAGE "${DFHACK_BUILD_ID}-")
endif()
set(CPACK_PACKAGE_FILE_NAME "dfhack-${DFHACK_VERSION}-${DFHACK_BUILD_ID_PACKAGE}${DFHACK_PACKAGE_PLATFORM_NAME}-${DFHACK_BUILD_ARCH}${DFHACK_PACKAGE_SUFFIX}")
INCLUDE(CPack)
OPTION(DFHACK_INCLUDE_CORE "Download and include Dwarf Fortress core files in DFHack. Useful for local testing, but should not be used in releases." OFF)
IF(DFHACK_INCLUDE_CORE)
STRING(REPLACE "." "_" DF_CORE_FILENAME "${DF_VERSION}")
STRING(REGEX REPLACE "^0_" "df_" DF_CORE_FILENAME "${DF_CORE_FILENAME}")
IF(UNIX)
IF(APPLE)
STRING(APPEND DF_CORE_FILENAME "_osx")
ELSE()
STRING(APPEND DF_CORE_FILENAME "_linux")
ENDIF()
IF(DFHACK_BUILD_32)
STRING(APPEND DF_CORE_FILENAME "32")
ENDIF()
STRING(APPEND DF_CORE_FILENAME ".tar.bz2")
ELSE()
STRING(APPEND DF_CORE_FILENAME "_win")
IF(DFHACK_BUILD_32)
STRING(APPEND DF_CORE_FILENAME "32")
ENDIF()
STRING(APPEND DF_CORE_FILENAME ".zip")
ENDIF()
SET(DF_CORE_URL "http://bay12games.com/dwarves/${DF_CORE_FILENAME}")
IF (NOT EXISTS "${CMAKE_BINARY_DIR}/${DF_CORE_FILENAME}")
FILE(DOWNLOAD "${DF_CORE_URL}" "${CMAKE_BINARY_DIR}/${DF_CORE_FILENAME}" SHOW_PROGRESS)
ENDIF()
FILE(REMOVE_RECURSE "${CMAKE_BINARY_DIR}/df-core")
FILE(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/df-core")
IF(UNIX)
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xjf "../${DF_CORE_FILENAME}" --strip-components=1
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/df-core")
ELSE()
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "../${DF_CORE_FILENAME}" --format=zip
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/df-core")
FILE(REMOVE "${CMAKE_BINARY_DIR}/df-core/SDL.dll")
ENDIF()
INSTALL(DIRECTORY "${CMAKE_BINARY_DIR}/df-core/"
DESTINATION .)
ENDIF()
#INCLUDE(FindSphinx.cmake)
# Store old build arch

@ -0,0 +1,350 @@
{
"environments": [
{
"environment": "msvc_2015_x86",
"PATH": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\BIN\\amd64_x86;${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\BIN\\amd64;${env.ProgramFiles(x86)}\\Windows Kits\\10\\bin\\x86;${env.ProgramFiles(x86)}\\Windows Kits\\8.1\\bin\\x86;${env.PATH}",
"VS140COMNTOOLS": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\Common7\\Tools\\",
"VCINSTALLDIR": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\",
"WindowsSdkDir": "${env.ProgramFiles(x86)}\\Windows Kits\\10\\",
"UCRTVersion": "10.0.10240.0",
"UniversalCRTSdkDir": "${env.ProgramFiles(x86)}\\Windows Kits\\10\\",
"LIB": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\LIB;${env.ProgramFiles(x86)}\\Windows Kits\\10\\lib\\10.0.10240.0\\ucrt\\x86;${env.ProgramFiles(x86)}\\Windows Kits\\10\\lib\\um\\x86;${env.ProgramFiles(x86)}\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\x86",
"INCLUDE": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\INCLUDE;${env.ProgramFiles(x86)}\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt;${env.ProgramFiles(x86)}\\Windows Kits\\10\\include\\shared;${env.ProgramFiles(x86)}\\Windows Kits\\10\\include\\um;${env.ProgramFiles(x86)}\\Windows Kits\\10\\include\\winrt;${env.ProgramFiles(x86)}\\Windows Kits\\8.1\\Include\\um;${env.ProgramFiles(x86)}\\Windows Kits\\8.1\\Include\\shared",
"LIBPATH": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\LIB"
},
{
"environment": "msvc_2015_x64",
"PATH": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\BIN\\amd64;${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\BIN;${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\BIN\\1033;${env.ProgramFiles(x86)}\\Windows Kits\\bin\\x64;${env.ProgramFiles(x86)}\\Windows Kits\\8.1\\bin\\x64;${env.PATH}",
"VS140COMNTOOLS": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\Common7\\Tools\\",
"VCINSTALLDIR": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\",
"WindowsSdkDir": "${env.ProgramFiles(x86)}\\Windows Kits\\10\\",
"UCRTVersion": "10.0.10240.0",
"UniversalCRTSdkDir": "${env.ProgramFiles(x86)}\\Windows Kits\\10\\",
"LIB": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\LIB\\amd64;${env.ProgramFiles(x86)}\\Windows Kits\\10\\lib\\10.0.10240.0\\ucrt\\x64;${env.ProgramFiles(x86)}\\Windows Kits\\10\\lib\\um\\x64;${env.ProgramFiles(x86)}\\Windows Kits\\8.1\\Lib\\winv6.3\\um\\x64",
"INCLUDE": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\INCLUDE;${env.ProgramFiles(x86)}\\Windows Kits\\10\\include\\10.0.10240.0\\ucrt;${env.ProgramFiles(x86)}\\Windows Kits\\10\\include\\shared;${env.ProgramFiles(x86)}\\Windows Kits\\10\\include\\um;${env.ProgramFiles(x86)}\\Windows Kits\\10\\include\\winrt;${env.ProgramFiles(x86)}\\Windows Kits\\8.1\\Include\\um;${env.ProgramFiles(x86)}\\Windows Kits\\8.1\\Include\\shared",
"LIBPATH": "${env.ProgramFiles(x86)}\\Microsoft Visual Studio 14.0\\VC\\LIB\\amd64"
}
],
"configurations": [
{
"name": "MSVC 32 Debug",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"inheritEnvironments": [ "msvc_x86_x64", "msvc_2015_x86" ],
"variables": [
{
"name": "DFHACK_BUILD_ARCH",
"value": "32"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
},
{
"name": "REMOVE_SYMBOLS_FROM_DF_STUBS",
"value": "0"
},
{
"name": "DFHACK_INCLUDE_CORE",
"value": "1"
}
]
},
{
"name": "MSVC 32 Release",
"generator": "Ninja",
"configurationType": "Release",
"inheritEnvironments": [ "msvc_x86_x64", "msvc_2015_x86" ],
"variables": [
{
"name": "DFHACK_BUILD_ARCH",
"value": "32"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
}
]
},
{
"name": "MSVC 64 Debug",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"inheritEnvironments": [ "msvc_x64_x64", "msvc_2015_x64" ],
"variables": [
{
"name": "DFHACK_BUILD_ARCH",
"value": "64"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
},
{
"name": "REMOVE_SYMBOLS_FROM_DF_STUBS",
"value": "0"
},
{
"name": "DFHACK_INCLUDE_CORE",
"value": "1"
}
]
},
{
"name": "MSVC 64 Release",
"generator": "Ninja",
"configurationType": "Release",
"inheritEnvironments": [ "msvc_x64_x64", "msvc_2015_x64" ],
"variables": [
{
"name": "DFHACK_BUILD_ARCH",
"value": "64"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
}
]
},
{
"name": "GCC 32 Debug",
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/tmp/dfhack-${workspaceHash}/src",
"remoteBuildRoot": "/tmp/dfhack-${workspaceHash}/debug-32",
"rsyncCommandArgs": "--exclude=build --include=.git",
"remoteCopySources": true,
"cmakeExecutable": "$(which cmake)/..",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"intelliSenseMode": "linux-gcc-x86",
"variables": [
{
"name": "DFHACK_BUILD_ARCH",
"value": "32"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
},
{
"name": "REMOVE_SYMBOLS_FROM_DF_STUBS",
"value": "0"
},
{
"name": "DFHACK_INCLUDE_CORE",
"value": "1"
}
]
},
{
"name": "GCC 32 Release",
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/tmp/dfhack-${workspaceHash}/src",
"remoteBuildRoot": "/tmp/dfhack-${workspaceHash}/release-32",
"rsyncCommandArgs": "--exclude=build --include=.git",
"remoteCopySources": true,
"cmakeExecutable": "$(which cmake)/..",
"generator": "Ninja",
"configurationType": "Release",
"intelliSenseMode": "linux-gcc-x86",
"variables": [
{
"name": "DFHACK_BUILD_ARCH",
"value": "32"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
}
]
},
{
"name": "GCC 64 Debug",
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/tmp/dfhack-${workspaceHash}/src",
"remoteBuildRoot": "/tmp/dfhack-${workspaceHash}/debug-64",
"rsyncCommandArgs": "--exclude=build --include=.git",
"remoteCopySources": true,
"cmakeExecutable": "$(which cmake)/..",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"intelliSenseMode": "linux-gcc-x64",
"variables": [
{
"name": "DFHACK_BUILD_ARCH",
"value": "64"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
},
{
"name": "REMOVE_SYMBOLS_FROM_DF_STUBS",
"value": "0"
},
{
"name": "DFHACK_INCLUDE_CORE",
"value": "1"
}
]
},
{
"name": "GCC 64 Release",
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/tmp/dfhack-${workspaceHash}/src",
"remoteBuildRoot": "/tmp/dfhack-${workspaceHash}/release-64",
"rsyncCommandArgs": "--exclude=build --include=.git",
"remoteCopySources": true,
"cmakeExecutable": "$(which cmake)/..",
"generator": "Ninja",
"configurationType": "Release",
"intelliSenseMode": "linux-gcc-x64",
"variables": [
{
"name": "DFHACK_BUILD_ARCH",
"value": "64"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
}
]
},
{
"name": "GCC 4.8 32 Debug",
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/tmp/dfhack-${workspaceHash}/src",
"remoteBuildRoot": "/tmp/dfhack-${workspaceHash}/debug-32-48",
"rsyncCommandArgs": "--exclude=build --include=.git",
"remoteCopySources": true,
"cmakeExecutable": "$(which cmake)/..",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"intelliSenseMode": "linux-gcc-x86",
"variables": [
{
"name": "CMAKE_C_COMPILER",
"value": "gcc-4.8"
},
{
"name": "CMAKE_CXX_COMPILER",
"value": "g++-4.8"
},
{
"name": "DFHACK_BUILD_ARCH",
"value": "32"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
},
{
"name": "REMOVE_SYMBOLS_FROM_DF_STUBS",
"value": "0"
},
{
"name": "DFHACK_INCLUDE_CORE",
"value": "1"
}
]
},
{
"name": "GCC 4.8 32 Release",
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/tmp/dfhack-${workspaceHash}/src",
"remoteBuildRoot": "/tmp/dfhack-${workspaceHash}/release-32-48",
"rsyncCommandArgs": "--exclude=build --include=.git",
"remoteCopySources": true,
"cmakeExecutable": "$(which cmake)/..",
"generator": "Ninja",
"configurationType": "Release",
"intelliSenseMode": "linux-gcc-x86",
"variables": [
{
"name": "CMAKE_C_COMPILER",
"value": "gcc-4.8"
},
{
"name": "CMAKE_CXX_COMPILER",
"value": "g++-4.8"
},
{
"name": "DFHACK_BUILD_ARCH",
"value": "32"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
}
]
},
{
"name": "GCC 4.8 64 Debug",
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/tmp/dfhack-${workspaceHash}/src",
"remoteBuildRoot": "/tmp/dfhack-${workspaceHash}/debug-64-48",
"rsyncCommandArgs": "--exclude=build --include=.git",
"remoteCopySources": true,
"cmakeExecutable": "$(which cmake)/..",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"intelliSenseMode": "linux-gcc-x64",
"variables": [
{
"name": "CMAKE_C_COMPILER",
"value": "gcc-4.8"
},
{
"name": "CMAKE_CXX_COMPILER",
"value": "g++-4.8"
},
{
"name": "DFHACK_BUILD_ARCH",
"value": "64"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
},
{
"name": "REMOVE_SYMBOLS_FROM_DF_STUBS",
"value": "0"
},
{
"name": "DFHACK_INCLUDE_CORE",
"value": "1"
}
]
},
{
"name": "GCC 4.8 64 Release",
"remoteMachineName": "${defaultRemoteMachineName}",
"remoteCMakeListsRoot": "/tmp/dfhack-${workspaceHash}/src",
"remoteBuildRoot": "/tmp/dfhack-${workspaceHash}/release-64-48",
"rsyncCommandArgs": "--exclude=build --include=.git",
"remoteCopySources": true,
"cmakeExecutable": "$(which cmake)/..",
"generator": "Ninja",
"configurationType": "Release",
"intelliSenseMode": "linux-gcc-x64",
"variables": [
{
"name": "CMAKE_C_COMPILER",
"value": "gcc-4.8"
},
{
"name": "CMAKE_CXX_COMPILER",
"value": "g++-4.8"
},
{
"name": "DFHACK_BUILD_ARCH",
"value": "64"
},
{
"name": "BUILD_STONESENSE",
"value": "1"
}
]
}
]
}

@ -6,7 +6,7 @@ Licenses
DFHack is distributed under the Zlib license, with some MIT-
and BSD-licensed components. These licenses protect your right
to use DFhack for any purpose, distribute copies, and so on.
to use DFHack for any purpose, distribute copies, and so on.
The core, plugins, scripts, and other DFHack code all use the
ZLib license unless noted otherwise. By contributing to DFHack,
@ -26,11 +26,11 @@ jsoncpp_ MIT \(c\) 2007-2010, Baptiste Lepilleur
linenoise_ BSD 2-clause \(c\) 2010, Salvatore Sanfilippo & Pieter Noordhuis
lua_ MIT \(c\) 1994-2008, Lua.org, PUC-Rio.
luafilesystem_ MIT \(c\) 2003-2014, Kepler Project
lua-profiler_ MIT \(c\) 2002,2003,2004 Pepperfish
protobuf_ BSD 3-clause \(c\) 2008, Google Inc.
tinythread_ Zlib \(c\) 2010, Marcus Geelnard
tinyxml_ Zlib \(c\) 2000-2006, Lee Thomason
UTF-8-decoder_ MIT \(c\) 2008-2010, Bjoern Hoehrmann
lua-profiler_ MIT \(c\) 2002,2003,2004 Pepperfish
=============== ============= =================================================
.. _DFHack: https://github.com/DFHack/dfhack

@ -11,7 +11,7 @@ endif()
add_subdirectory(tthread)
OPTION(JSONCPP_WITH_TESTS "Compile and (for jsoncpp_check) run JsonCpp test executables" OFF)
OPTION(JSONCPP_WITH_POST_BUILD_UNITTEST "Automatically run unit-tests as a post build step" OFF)
add_subdirectory(jsoncpp-sub)
add_subdirectory(jsoncpp-sub EXCLUDE_FROM_ALL)
# build clsocket static and only as a dependency. Setting those options here overrides its own default settings.
OPTION(CLSOCKET_SHARED "Build clsocket lib as shared." OFF)
OPTION(CLSOCKET_DEP_ONLY "Build for use inside other CMake projects as dependency." ON)

@ -62,6 +62,7 @@ Jonas Ask
Josh Cooper cppcooper coope
kane-t kane-t
Kelly Kinkade ab9rf
KlonZK KlonZK
Kris Parker kaypy
Kromtec Kromtec
Kurik Amudnil
@ -125,6 +126,7 @@ Seth Woodworth sethwoodworth
simon
Simon Jackson sizeak
stolencatkarma
Stoyan Gaydarov sgayda2
suokko suokko shrieker
sv-esk sv-esk
Tacomagic

@ -33,13 +33,16 @@ To get the latest development code (develop branch), clone as above and then::
Generally, you should only need to clone DFHack once.
**Important note regarding submodule update and changing branches**:
**Important note regarding submodule update after pulling or changing branches**:
You must run ``git submodule update`` every time you change branches,
such as when switching between the master and develop branches or vice versa.
If a submodule only exists on the newer branch, you also need to run
``git submodule update --init``. Failure to do this may result in strange
build errors or "not a known DF version" errors.
You must run ``git submodule update`` every time you change branches, such as
when switching between the master and develop branches or vice versa. You also
must run it after pulling any changes to submodules from the DFHack repo. If a
submodule only exists on the newer branch, or if a commit you just pulled
contains a new submodule, you need to run ``git submodule update --init``.
Failure to do this may result in a variety of errors, including ``fatal: <path>
does not exist`` when using Git, errors when building DFHack, and ``not a known
DF version`` when starting DF.
**More notes**:
@ -65,6 +68,24 @@ and more, please see `contributing-code`.
Build settings
==============
Generator
---------
The ``Ninja`` CMake build generator is the prefered build method on Linux and
macOS, instead of ``Unix Makefiles``, which is the default. You can select Ninja
by passing ``-G Ninja`` to CMake. Incremental builds using Unix Makefiles can be
much slower than Ninja builds.
::
cmake .. -G Ninja
.. warning::
Most other CMake settings can be changed by running ``cmake`` again, but the
generator cannot be changed after ``cmake`` has been run without creating a
new build folder. Do not forget to specify this option.
Build type
----------
@ -72,19 +93,16 @@ Build type
cmake .. -DCMAKE_BUILD_TYPE:string=BUILD_TYPE
Without specifying a build type or 'None', cmake uses the
``CMAKE_CXX_FLAGS`` variable for building.
Valid and useful build types include 'Release', 'Debug' and
'RelWithDebInfo'.
'Debug' is not available on Windows; use 'RelWithDebInfo' instead.
Valid and useful build types include 'Release' and 'RelWithDebInfo'. The default
build type is 'Release'.
Target architecture (32-bit vs. 64-bit)
---------------------------------------
Set DFHACK_BUILD_ARCH to either ``32`` or ``64`` to build a 32-bit or 64-bit
version of DFHack (respectively). The default is currently ``32``, but this may
change, so specifying it explicitly is a good idea.
version of DFHack (respectively). The default is currently ``64``, so you will
need to specify this explicitly for 32-bit builds. Specifying it is a good idea
in any case.
::
@ -138,7 +156,7 @@ Here are some package install commands for various platforms:
* On Ubuntu::
apt-get install gcc cmake git zlib1g-dev libsdl1.2-dev libxml-libxml-perl libxml-libxslt-perl
apt-get install gcc cmake ninja-build git zlib1g-dev libsdl1.2-dev libxml-libxml-perl libxml-libxslt-perl
* Debian and derived distros should have similar requirements to Ubuntu.
@ -171,8 +189,8 @@ Building is fairly straightforward. Enter the ``build`` folder (or create an
empty folder in the DFHack directory to use instead) and start the build like this::
cd build
cmake .. -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=<path to DF>
make install # or make -jX install on multi-core systems to compile with X parallel processes
cmake .. -G Ninja -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=<path to DF>
ninja install # or ninja -jX install to specify the number of cores (X) to use
<path to DF> should be a path to a copy of Dwarf Fortress, of the appropriate
version for the DFHack you are building. This will build the library along
@ -181,8 +199,8 @@ with the normal set of plugins and install them into your DF folder.
Alternatively, you can use ccmake instead of cmake::
cd build
ccmake ..
make install
ccmake .. -G Ninja
ninja install
This will show a curses-based interface that lets you set all of the
extra options. You can also use a cmake-friendly IDE like KDevelop 4
@ -215,7 +233,7 @@ compilation-for-distribution with a GCC newer than 4.8.
Mac OS X
========
DFHack functions similarly on OS X and Linux, and the majority of the
information above regarding the build process (cmake and make) applies here
information above regarding the build process (cmake and ninja) applies here
as well.
DFHack can officially be built on OS X with GCC 4.8 or 7. Anything newer than 7
@ -278,11 +296,12 @@ Dependencies and system set-up
brew tap homebrew/versions
brew install git
brew install cmake
brew install ninja
brew install gcc@7
Using `MacPorts <https://www.macports.org>`_::
sudo port install gcc7 +universal cmake +universal git-core +universal
sudo port install gcc7 +universal cmake +universal git-core +universal ninja +universal
Macports will take some time - maybe hours. At some point it may ask
you to install a Java environment; let it do so.
@ -345,8 +364,8 @@ Building
mkdir build-osx
cd build-osx
cmake .. -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=<path to DF>
make install # or make -j X install on multi-core systems to compile with X parallel processes
cmake .. -G Ninja -DCMAKE_BUILD_TYPE:string=Release -DCMAKE_INSTALL_PREFIX=<path to DF>
ninja install # or ninja -jX install to specify the number of cores (X) to use
<path to DF> should be a path to a copy of Dwarf Fortress, of the appropriate
version for the DFHack you are building.
@ -361,7 +380,7 @@ Dependencies
------------
You will need the following:
* Microsoft Visual Studio 2015, with the C++ language
* Microsoft Visual C++ 2015 or 2017
* Git
* CMake
* Perl with XML::LibXML and XML::LibXSLT
@ -372,12 +391,16 @@ You will need the following:
Microsoft Visual Studio 2015
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DFHack has to be compiled with the Microsoft Visual C++ 2015 toolchain; other
versions won't work against Dwarf Fortress due to ABI and STL incompatibilities.
DFHack has to be compiled with the Microsoft Visual C++ 2015 or 2017 toolchain on Windows;
other versions won't work against Dwarf Fortress due to ABI and STL incompatibilities.
You can install Visual Studio 2015_ or 2017_ Community edition for free, which
include all the features needed by DFHack. You can also download just the
`build tools`_ if you aren't going to use Visual Studio to edit code.
At present, the only way to obtain the MSVC C++ 2015 toolchain is to install a
full copy of Microsoft Visual Studio 2015. The free Community version is
sufficient.
.. _2015: https://visualstudio.microsoft.com/vs/older-downloads/#visual-studio-2015-and-other-products
.. _2017: https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15
.. _build tools: https://visualstudio.microsoft.com/vs/older-downloads/#microsoft-build-tools-2015-update-3
Additional dependencies: installing with the Chocolatey Package Manager
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -724,7 +747,7 @@ files alphabetically, so all the files you need should be next to each other.
It is recommended that you create a build folder and run CMake to verify that
you have downloaded everything at this point, assuming your download machine has
CMake installed. This involves running a "generate" batch script on Windows, or
a command starting with ``cmake ..`` on Linux and OS X, following the
a command starting with ``cmake .. -G Ninja`` on Linux and OS X, following the
instructions in the sections above. CMake should automatically locate files that
you placed in ``CMake/downloads``, and use them instead of attempting to
download them.

@ -822,6 +822,7 @@ can be omitted.
* ``dfhack.getDFHackVersion()``
* ``dfhack.getDFHackRelease()``
* ``dfhack.getDFHackBuildID()``
* ``dfhack.getCompiledDFVersion()``
* ``dfhack.getGitDescription()``
* ``dfhack.getGitCommit()``
@ -1271,6 +1272,18 @@ Units module
Retrieves the profession color for the given race/caste using raws.
* ``dfhack.units.getStressCategory(unit)``
Returns a number from 0-6 indicating stress. 0 is most stressed; 6 is least.
Note that 0 is guaranteed to remain the most stressed but 6 could change in the future.
* ``dfhack.units.getStressCategoryRaw(stress_level)``
Identical to ``getStressCategory`` but takes a raw stress level instead of a unit.
* ``dfhack.units.getStressCutoffs()``
Returns a table of the cutoffs used by the above stress level functions.
Items module
------------

@ -539,7 +539,7 @@ This plugin provides embark site selection help. It has to be run with the
extended (and correct(?)) resource information for the embark rectangle as well
as normally undisplayed sites in the current embark region. It also has a site
selection tool with more options than DF's vanilla search tool. For detailed
help invoke the in game info screen. Requires 46 lines to display properly.
help invoke the in game info screen.
.. _embark-tools:
@ -1933,6 +1933,10 @@ all 'down ramps' that can remain after a cave-in (you don't have to designate
anything for that to happen).
.. _dig:
.. _digv:
.. _digvx:
.. _digl:
.. _diglx:
dig
===

@ -38,11 +38,102 @@ changelog.txt uses a syntax similar to RST, with a few special sequences:
# Future
## Fixes
- `building-hacks`: fixed error when dealing with custom animation tables
- `embark-assistant`: fixed crash when entering finder with a 16x16 embark selected, and added 16 to dimension choices
- `prospector`: (also affected `embark-tools`) - fixed a crash when prospecting an unusable site (ocean, mountains, etc.) with a large default embark size in d_init.txt (e.g. 16x16)
## Misc Improvements
- `devel/export-dt-ini`: added viewscreen offsets for DT 40.1.2
## Internals
- Linux/macOS: changed recommended build backend from Make to Ninja (Make builds will be significantly slower now)
- Added a usable unit test framework for basic tests, and a few basic tests
- Core: various thread safety and memory management improvements
- Fixed cmake build dependencies for generated header files
- Fixed custom ``CMAKE_CXX_FLAGS`` not being passed to plugins
- Changed ``plugins/CMakeLists.custom.txt`` to be ignored by git and created (if needed) at build time instead
## Lua
- ``utils``: new ``OrderedTable`` class
================================================================================
# 0.44.12-r1
## Fixes
-@ Console: fixed crash when entering long commands on Linux/macOS
-@ Removed jsoncpp's ``include`` and ``lib`` folders from DFHack builds/packages
- Fixed special characters in `command-prompt` and other non-console in-game outputs on Linux/macOS (in tools using ``df2console``)
- `die`: fixed Windows crash in exit handling
- `dwarfmonitor`, `manipulator`: fixed stress cutoffs
- `modtools/force`: fixed a bug where the help text would always be displayed and nothing useful would happen
- `ruby`: fixed calling conventions for vmethods that return strings (currently ``enabler.GetKeyDisplay()``)
- `startdwarf`: fixed on 64-bit Linux
## Misc Improvements
- Reduced time for designation jobs from tools like `digv` to be assigned workers
- `embark-assistant`:
- Switched to standard scrolling keys, improved spacing slightly
- Introduced scrolling of Finder search criteria, removing requirement for 46 lines to work properly (Help/Info still formatted for 46 lines).
- Added Freezing search criterion, allowing searches for NA/Frozen/At_Least_Partial/Partial/At_Most_Partial/Never Freezing embarks.
- `rejuvenate`:
- Added ``-all`` argument to apply to all citizens
- Added ``-force`` to include units under 20 years old
- Clarified documentation
## API
- Added to ``Units`` module:
- ``getStressCategory(unit)``
- ``getStressCategoryRaw(level)``
- ``stress_cutoffs`` (Lua: ``getStressCutoffs()``)
## Internals
- Changed default build architecture to 64-bit
- Added documentation for all RPC functions and a build-time check
- Added support for build IDs to development builds
- Use ``dlsym(3)`` to find vtables from libgraphics.so
## Structures
- Added ``start_dwarf_count`` on 64-bit Linux again and fixed scanning script
- ``army_controller``: added new vector from 0.44.11
-@ ``viewscreen_civlistst``: split ``unk_20`` into 3 pointers
- ``belief_system``: new type, few fields identified
- ``mental_picture``: new type, some fields identified
- ``mission``: new type (used in ``viewscreen_civlistst``)
- ``mission_report``:
- new type (renamed, was ``mission`` before)
- identified some fields
- ``spoils_report``: new type, most fields identified
- ``viewscreen_civlistst``:
- identified new pages
- identified new messenger-related fields
- ``viewscreen_image_creatorst``:
- fixed layout
- identified many fields
- ``viewscreen_reportlistst``:
- added new mission and spoils report-related fields (fixed layout)
- ``world``:
- ``belief_systems``: identified
- ``world.languages``: identified (minimal information; whole languages stored elsewhere)
- ``world.status``:
- ``mission_reports``: renamed, was ``missions``
- ``spoils_reports``: identified
- ``world.unk_131ec0``, ``world.unk_131ef0``: researched layout
- ``world.worldgen_status``: identified many fields
================================================================================
# 0.44.12-alpha1
## Fixes
-@ macOS: fixed ``renderer`` vtable address on x64 (fixes `rendermax`)
- `stonesense`: fixed ``PLANT:DESERT_LIME:LEAF`` typo
## API
- Added C++-style linked list interface for DF linked lists
## Structures
- Dropped 0.44.11 support
- ``ui.squads``: Added fields new in 0.44.12
================================================================================
# 0.44.11-beta2.1

@ -0,0 +1 @@
git-describe.cmake

@ -258,15 +258,22 @@ LIST(APPEND PROJECT_SOURCES ${GENERATED_HDRS})
FILE(GLOB GENERATE_INPUT_SCRIPTS ${dfapi_SOURCE_DIR}/xml/*.pm ${dfapi_SOURCE_DIR}/xml/*.xslt)
FILE(GLOB GENERATE_INPUT_XMLS ${dfapi_SOURCE_DIR}/xml/df.*.xml)
set(CODEGEN_OUT ${dfapi_SOURCE_DIR}/include/df/codegen.out.xml)
LIST(APPEND CODEGEN_OUT ${GENERATED_HDRS})
ADD_CUSTOM_COMMAND(
OUTPUT ${dfapi_SOURCE_DIR}/include/df/codegen.out.xml
COMMAND ${PERL_EXECUTABLE} xml/codegen.pl xml include/df
WORKING_DIRECTORY ${dfapi_SOURCE_DIR}
OUTPUT ${CODEGEN_OUT}
COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/xml/codegen.pl
${CMAKE_CURRENT_SOURCE_DIR}/xml
${CMAKE_CURRENT_SOURCE_DIR}/include/df
MAIN_DEPENDENCY ${dfapi_SOURCE_DIR}/xml/codegen.pl
COMMENT "Generating codegen.out.xml and df/headers"
DEPENDS ${GENERATE_INPUT_XMLS} ${GENERATE_INPUT_SCRIPTS}
)
ADD_CUSTOM_TARGET(generate_headers DEPENDS ${dfapi_SOURCE_DIR}/include/df/codegen.out.xml)
ADD_CUSTOM_TARGET(generate_headers
DEPENDS ${dfapi_SOURCE_DIR}/include/df/codegen.out.xml)
IF(REMOVE_SYMBOLS_FROM_DF_STUBS)
IF(UNIX)
# Don't produce debug info for generated stubs
@ -276,6 +283,11 @@ IF(REMOVE_SYMBOLS_FROM_DF_STUBS)
SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp ${STATIC_FIELDS_FILES}
PROPERTIES COMPILE_FLAGS "/O1 /bigobj")
ENDIF()
ELSE()
IF(WIN32)
SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp ${STATIC_FIELDS_FILES}
PROPERTIES COMPILE_FLAGS "/Od /bigobj")
ENDIF()
ENDIF()
# Compilation
@ -297,7 +309,14 @@ ELSE(WIN32)
SET(PROJECT_LIBS psapi dfhack-md5 ${DFHACK_TINYXML} dfhack-tinythread)
ENDIF()
ADD_LIBRARY(dfhack-version STATIC DFHackVersion.cpp)
set(VERSION_SRCS DFHackVersion.cpp)
set(VERSION_HDRS include/git-describe.h)
SET_SOURCE_FILES_PROPERTIES(${VERSION_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE)
LIST(APPEND VERSION_SRCS ${VERSION_HDRS})
ADD_LIBRARY(dfhack-version STATIC ${VERSION_SRCS})
SET_PROPERTY(TARGET dfhack-version APPEND PROPERTY COMPILE_DEFINITIONS
DFHACK_VERSION="${DFHACK_VERSION}"
DF_VERSION="${DF_VERSION}"
@ -310,17 +329,24 @@ IF(DFHACK_PRERELEASE)
)
ENDIF()
ADD_CUSTOM_TARGET(git-describe
COMMAND ${CMAKE_COMMAND}
-D dfhack_SOURCE_DIR:STRING=${dfhack_SOURCE_DIR}
-D GIT_EXECUTABLE:STRING=${GIT_EXECUTABLE}
-P ${CMAKE_CURRENT_SOURCE_DIR}/git-describe.cmake
COMMENT "Obtaining git commit information"
)
ADD_DEPENDENCIES(dfhack-version git-describe)
configure_file(git-describe.cmake.in ${CMAKE_CURRENT_SOURCE_DIR}/git-describe.cmake @ONLY)
if (EXISTS ${CMAKE_SOURCE_DIR}/.git/index AND EXISTS ${CMAKE_SOURCE_DIR}/.git/modules/library/xml/index)
ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/include/git-describe.h
COMMAND ${CMAKE_COMMAND}
-D dfhack_SOURCE_DIR:STRING=${dfhack_SOURCE_DIR}
-D GIT_EXECUTABLE:STRING=${GIT_EXECUTABLE}
-P ${CMAKE_CURRENT_SOURCE_DIR}/git-describe.cmake
COMMENT "Obtaining git commit information"
DEPENDS ${CMAKE_SOURCE_DIR}/.git/index
${CMAKE_SOURCE_DIR}/.git/modules/library/xml/index
${CMAKE_CURRENT_SOURCE_DIR}/git-describe.cmake
include/git-describe.h.in
)
endif ()
ADD_LIBRARY(dfhack SHARED ${PROJECT_SOURCES})
ADD_DEPENDENCIES(dfhack generate_headers generate_proto_core)
ADD_DEPENDENCIES(dfhack generate_proto_core)
ADD_DEPENDENCIES(dfhack generate_headers)
ADD_LIBRARY(dfhack-client SHARED RemoteClient.cpp ColorText.cpp MiscUtils.cpp Error.cpp ${PROJECT_PROTO_SRCS})
ADD_DEPENDENCIES(dfhack-client dfhack)

@ -486,9 +486,10 @@ namespace DFHack
int cooked_cursor = raw_cursor;
if ((plen+cooked_cursor) >= cols)
{
begin = plen+cooked_cursor-cols-1;
len -= plen+cooked_cursor-cols-1;
cooked_cursor -= plen+cooked_cursor-cols-1;
const int text_over = plen + cooked_cursor + 1 - cols;
begin = text_over;
len -= text_over;
cooked_cursor -= text_over;
}
if (plen+len > cols)
len -= plen+len - cols;

@ -54,6 +54,7 @@ using namespace std;
#include "modules/Graphic.h"
#include "modules/Windows.h"
#include "RemoteServer.h"
#include "RemoteTools.h"
#include "LuaTools.h"
#include "DFHackVersion.h"
@ -98,7 +99,8 @@ static bool parseKeySpec(std::string keyspec, int *psym, int *pmod, std::string
size_t loadScriptFiles(Core* core, color_ostream& out, const vector<std::string>& prefix, const std::string& folder);
namespace DFHack {
struct MainThread {
class MainThread {
public:
//! MainThread::suspend keeps the main DF thread suspended from Core::Init to
//! thread exit.
static CoreSuspenderBase& suspend() {
@ -261,6 +263,8 @@ static string dfhack_version_desc()
else
s << "(development build " << Version::git_description() << ")";
s << " on " << (sizeof(void*) == 8 ? "x86_64" : "x86");
if (strlen(Version::dfhack_build_id()))
s << " [build ID: " << Version::dfhack_build_id() << "]";
return s.str();
}
@ -655,6 +659,9 @@ string getBuiltinCommand(std::string cmd)
else if (cmd == "clear")
builtin = "cls";
else if (cmd == "devel/dump-rpc")
builtin = "devel/dump-rpc";
return builtin;
}
@ -1163,7 +1170,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
}
else if (builtin == "die")
{
_exit(666);
std::_Exit(666);
}
else if (builtin == "kill-lua")
{
@ -1301,6 +1308,34 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
return CR_WRONG_USAGE;
}
}
else if (builtin == "devel/dump-rpc")
{
if (parts.size() == 1)
{
std::ofstream file(parts[0]);
CoreService core;
core.dumpMethods(file);
for (auto & it : *plug_mgr)
{
Plugin * plug = it.second;
if (!plug)
continue;
std::unique_ptr<RPCService> svc(plug->rpc_connect(con));
if (!svc)
continue;
file << "// Plugin: " << plug->getName() << endl;
svc->dumpMethods(file);
}
}
else
{
con << "Usage: devel/dump-rpc \"filename\"" << endl;
return CR_WRONG_USAGE;
}
}
else if (RunAlias(con, first, parts, res))
{
return res;
@ -1441,6 +1476,10 @@ void fIOthread(void * iodata)
dfhack_version_desc().c_str());
int clueless_counter = 0;
if (getenv("DFHACK_DISABLE_CONSOLE"))
return;
while (true)
{
string command = "";
@ -1479,17 +1518,7 @@ void fIOthread(void * iodata)
Core::~Core()
{
if (MainThread::suspend().owns_lock())
MainThread::suspend().unlock();
if (d->hotkeythread.joinable()) {
std::lock_guard<std::mutex> lock(HotkeyMutex);
hotkey_set = SHUTDOWN;
HotkeyCond.notify_one();
}
if (d->iothread.joinable())
con.shutdown();
delete d;
// we leak the memory in case ~Core is called after _exit
}
Core::Core() :
@ -1558,8 +1587,6 @@ std::string Core::getHackPath()
#endif
}
void init_screen_module(Core *);
bool Core::Init()
{
if(started)
@ -1682,7 +1709,7 @@ bool Core::Init()
cerr << "Headless mode not supported on Windows" << endl;
#endif
}
if ((is_text_mode && !is_headless) || getenv("DFHACK_DISABLE_CONSOLE"))
if (is_text_mode && !is_headless)
{
con.init(true);
cerr << "Console is not available. Use dfhack-run to send commands.\n";
@ -1706,7 +1733,6 @@ bool Core::Init()
*/
// initialize data defs
virtual_identity::Init(this);
init_screen_module(this);
// copy over default config files if necessary
std::vector<std::string> config_files;
@ -2299,6 +2325,8 @@ int Core::Shutdown ( void )
}
allModules.clear();
memset(&(s_mods), 0, sizeof(s_mods));
delete d;
d = nullptr;
return -1;
}

@ -19,6 +19,10 @@ namespace DFHack {
{
return DFHACK_RELEASE;
}
const char *dfhack_build_id()
{
return DFHACK_BUILD_ID;
}
const char *git_description()
{
return DFHACK_GIT_DESCRIPTION;

@ -26,6 +26,7 @@ distribution.
#include <windows.h>
#include <stdint.h>
#include <mutex>
#include <vector>
#include <string>
#include "Core.h"
@ -40,8 +41,8 @@ distribution.
// we don't know which of the SDL functions will be called first... so we
// just catch the first one and init all our function pointers at that time
bool FirstCall(void);
bool inited = false;
static void InitSDLPointers(void);
static std::once_flag inited;
/// wrappers for SDL 1.2 functions used in 40d16
/***** Condition variables
@ -612,8 +613,7 @@ DFhackCExport int SDL_NumJoysticks(void)
static void (*_SDL_GL_SwapBuffers)(void) = 0;
DFhackCExport void SDL_GL_SwapBuffers(void)
{
if(!inited)
FirstCall();
InitSDLPointers();
_SDL_GL_SwapBuffers();
}
@ -621,16 +621,14 @@ DFhackCExport void SDL_GL_SwapBuffers(void)
static int (*_SDL_Flip)(void * some_ptr) = 0;
DFhackCExport int SDL_Flip(void * some_ptr)
{
if(!inited)
FirstCall();
InitSDLPointers();
return _SDL_Flip(some_ptr);
}
static int (*_SDL_Init)(uint32_t flags) = 0;
DFhackCExport int SDL_Init(uint32_t flags)
{
if(!inited)
FirstCall();
InitSDLPointers();
return _SDL_Init(flags);
}
@ -640,16 +638,14 @@ MORE CRAP
static void * (*_SDL_CreateSemaphore)(uint32_t initial_value) = 0;
DFhackCExport void *SDL_CreateSemaphore(uint32_t initial_value)
{
if(!inited)
FirstCall();
InitSDLPointers();
return _SDL_CreateSemaphore(initial_value);
}
static vPtr (*_SDL_CreateThread)(int (*fn)(void *), void *data) = 0;
DFhackCExport vPtr SDL_CreateThread(int (*fn)(void *), void *data)
{
if(!inited)
FirstCall();
InitSDLPointers();
return _SDL_CreateThread(fn,data);
}
@ -657,93 +653,81 @@ DFhackCExport vPtr SDL_CreateThread(int (*fn)(void *), void *data)
static void (*_SDL_Delay)(uint32_t ms) = 0;
DFhackCExport void SDL_Delay(uint32_t ms)
{
if(!inited)
FirstCall();
InitSDLPointers();
_SDL_Delay(ms);
}
static void (*_SDL_DestroySemaphore)(void *sem) = 0;
DFhackCExport void SDL_DestroySemaphore(void *sem)
{
if(!inited)
FirstCall();
InitSDLPointers();
_SDL_DestroySemaphore(sem);
}
static uint8_t (*_SDL_GetAppState)(void) = 0;
DFhackCExport uint8_t SDL_GetAppState(void)
{
if(!inited)
FirstCall();
InitSDLPointers();
return _SDL_GetAppState();
}
static uint8_t (*_SDL_GetMouseState)(int *, int *) = 0;
DFhackCExport uint8_t SDL_GetMouseState(int *x, int *y)
{
if(!inited)
FirstCall();
InitSDLPointers();
return _SDL_GetMouseState(x,y);
}
static int (*_SDL_InitSubSystem)(uint32_t flags) = 0;
DFhackCExport int SDL_InitSubSystem(uint32_t flags)
{
if(!inited)
FirstCall();
InitSDLPointers();
return _SDL_InitSubSystem(flags);
}
static int (*_SDL_SemPost)(void *sem) = 0;
DFhackCExport int SDL_SemPost(void *sem)
{
if(!inited)
FirstCall();
InitSDLPointers();
return _SDL_SemPost(sem);
}
static int (*_SDL_SemTryWait)(void *sem) = 0;
DFhackCExport int SDL_SemTryWait(void *sem)
{
if(!inited)
FirstCall();
InitSDLPointers();
return _SDL_SemTryWait(sem);
}
static int (*_SDL_SemWait)(void *sem) = 0;
DFhackCExport int SDL_SemWait(void *sem)
{
if(!inited)
FirstCall();
InitSDLPointers();
return _SDL_SemWait(sem);
}
static uint32_t (*_SDL_ThreadID)(void) = 0;
DFhackCExport uint32_t SDL_ThreadID(void)
{
if(!inited)
FirstCall();
InitSDLPointers();
return _SDL_ThreadID();
}
static char* (*_SDL_getenv)(const char *name) = 0;
DFhackCExport char* SDL_getenv(const char *name)
{
if(!inited)
FirstCall();
InitSDLPointers();
return _SDL_getenv(name);
}
static size_t (*_SDL_strlcat)(char *dst, const char *src, size_t maxlen) = 0;
DFhackCExport size_t SDL_strlcat(char *dst, const char *src, size_t maxlen)
{
if(!inited)
FirstCall();
InitSDLPointers();
return _SDL_strlcat(dst, src, maxlen);
}
// FIXME: this has to be thread-safe.
bool FirstCall()
void FirstCall()
{
// reroute stdout and stderr
freopen("stdout.log", "w", stdout);
@ -753,7 +737,7 @@ bool FirstCall()
{
MessageBox(0,"Can't load SDLreal.dll\n","Error", MB_OK);
fprintf(stderr, "Can't load SDLreal.dll\n");
return 0;
return;
}
fprintf(stderr, "FirstCall()\n");
// stuff for DF
@ -836,6 +820,9 @@ bool FirstCall()
_SDL_EnableUNICODE(1);
fprintf(stderr,"Initized HOOKS!\n");
inited = true;
return 1;
}
void InitSDLPointers()
{
std::call_once(inited, [](){ FirstCall(); });
}

@ -1416,7 +1416,7 @@ static bool isMapLoaded() { return Core::getInstance().isMapLoaded(); }
static std::string df2utf(std::string s) { return DF2UTF(s); }
static std::string utf2df(std::string s) { return UTF2DF(s); }
static std::string df2console(std::string s) { return DF2CONSOLE(s); }
static std::string df2console(color_ostream &out, std::string s) { return DF2CONSOLE(out, s); }
#define WRAP_VERSION_FUNC(name, function) WRAPN(name, DFHack::Version::function)
@ -1436,6 +1436,7 @@ static const LuaWrapper::FunctionReg dfhack_module[] = {
WRAP(df2console),
WRAP_VERSION_FUNC(getDFHackVersion, dfhack_version),
WRAP_VERSION_FUNC(getDFHackRelease, dfhack_release),
WRAP_VERSION_FUNC(getDFHackBuildID, dfhack_build_id),
WRAP_VERSION_FUNC(getCompiledDFVersion, df_version),
WRAP_VERSION_FUNC(getGitDescription, git_description),
WRAP_VERSION_FUNC(getGitCommit, git_commit),
@ -1641,6 +1642,8 @@ static const LuaWrapper::FunctionReg dfhack_units_module[] = {
WRAPM(Units, isDomesticated),
WRAPM(Units, getMainSocialActivity),
WRAPM(Units, getMainSocialEvent),
WRAPM(Units, getStressCategory),
WRAPM(Units, getStressCategoryRaw),
{ NULL, NULL }
};
@ -1691,10 +1694,19 @@ static int units_getUnitsInBox(lua_State *state)
return 2;
}
static int units_getStressCutoffs(lua_State *L)
{
lua_newtable(L);
for (size_t i = 0; i < Units::stress_cutoffs.size(); i++)
Lua::TableInsert(L, i, Units::stress_cutoffs[i]);
return 1;
}
static const luaL_Reg dfhack_units_funcs[] = {
{ "getPosition", units_getPosition },
{ "getNoblePositions", units_getNoblePositions },
{ "getUnitsInBox", units_getUnitsInBox },
{ "getStressCutoffs", units_getStressCutoffs },
{ NULL, NULL }
};

@ -25,6 +25,7 @@ distribution.
#include "Internal.h"
#include "Export.h"
#include "MiscUtils.h"
#include "ColorText.h"
#ifndef LINUX_BUILD
// We don't want min and max macros
@ -394,3 +395,8 @@ DFHACK_EXPORT std::string DF2CONSOLE(const std::string &in)
#endif
return is_utf ? DF2UTF(in) : in;
}
DFHACK_EXPORT std::string DF2CONSOLE(DFHack::color_ostream &out, const std::string &in)
{
return out.is_console() ? DF2CONSOLE(in) : in;
}

@ -23,6 +23,7 @@
*/
namespace DFHack
{
DFLibrary* GLOBAL_NAMES = (DFLibrary*)RTLD_DEFAULT;
DFLibrary * OpenPlugin (const char * filename)
{
dlerror();

@ -41,6 +41,7 @@ distribution.
*/
namespace DFHack
{
DFLibrary* GLOBAL_NAMES = (DFLibrary*)GetModuleHandle(nullptr);
DFLibrary * OpenPlugin (const char * filename)
{
return (DFLibrary *) LoadLibrary(filename);

@ -236,7 +236,7 @@ void RemoteClient::disconnect()
}
bool RemoteClient::bind(color_ostream &out, RemoteFunctionBase *function,
const std::string &name, const std::string &proto)
const std::string &name, const std::string &plugin)
{
if (!active || !socket->IsSocketValid())
return false;
@ -247,8 +247,8 @@ bool RemoteClient::bind(color_ostream &out, RemoteFunctionBase *function,
auto in = bind_call.in();
in->set_method(name);
if (!proto.empty())
in->set_plugin(proto);
if (!plugin.empty())
in->set_plugin(plugin);
in->set_input_msg(function->p_in_template->GetTypeName());
in->set_output_msg(function->p_out_template->GetTypeName());
}
@ -326,23 +326,23 @@ void RPCFunctionBase::reset(bool free)
}
bool RemoteFunctionBase::bind(color_ostream &out, RemoteClient *client,
const std::string &name, const std::string &proto)
const std::string &name, const std::string &plugin)
{
if (isValid())
{
if (p_client == client && this->name == name && this->proto == proto)
if (p_client == client && this->name == name && this->plugin == plugin)
return true;
out.printerr("Function already bound to %s::%s\n",
this->proto.c_str(), this->name.c_str());
this->plugin.c_str(), this->name.c_str());
return false;
}
this->name = name;
this->proto = proto;
this->plugin = plugin;
this->p_client = client;
return client->bind(out, this, name, proto);
return client->bind(out, this, name, plugin);
}
bool sendRemoteMessage(CSimpleSocket *socket, int16_t id, const MessageLite *msg, bool size_ready)
@ -371,14 +371,14 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
if (!isValid())
{
out.printerr("Calling an unbound RPC function %s::%s.\n",
this->proto.c_str(), this->name.c_str());
this->plugin.c_str(), this->name.c_str());
return CR_NOT_IMPLEMENTED;
}
if (!p_client->socket->IsSocketValid())
{
out.printerr("In call to %s::%s: invalid socket.\n",
this->proto.c_str(), this->name.c_str());
this->plugin.c_str(), this->name.c_str());
return CR_LINK_FAILURE;
}
@ -387,14 +387,14 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
if (send_size > RPCMessageHeader::MAX_MESSAGE_SIZE)
{
out.printerr("In call to %s::%s: message too large: %d.\n",
this->proto.c_str(), this->name.c_str(), send_size);
this->plugin.c_str(), this->name.c_str(), send_size);
return CR_LINK_FAILURE;
}
if (!sendRemoteMessage(p_client->socket, id, input, true))
{
out.printerr("In call to %s::%s: I/O error in send.\n",
this->proto.c_str(), this->name.c_str());
this->plugin.c_str(), this->name.c_str());
return CR_LINK_FAILURE;
}
@ -409,7 +409,7 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
if (!readFullBuffer(p_client->socket, &header, sizeof(header)))
{
out.printerr("In call to %s::%s: I/O error in receive header.\n",
this->proto.c_str(), this->name.c_str());
this->plugin.c_str(), this->name.c_str());
return CR_LINK_FAILURE;
}
@ -421,7 +421,7 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
if (header.size < 0 || header.size > RPCMessageHeader::MAX_MESSAGE_SIZE)
{
out.printerr("In call to %s::%s: invalid received size %d.\n",
this->proto.c_str(), this->name.c_str(), header.size);
this->plugin.c_str(), this->name.c_str(), header.size);
return CR_LINK_FAILURE;
}
@ -430,7 +430,7 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
if (!readFullBuffer(p_client->socket, buf, header.size))
{
out.printerr("In call to %s::%s: I/O error in receive %d bytes of data.\n",
this->proto.c_str(), this->name.c_str(), header.size);
this->plugin.c_str(), this->name.c_str(), header.size);
return CR_LINK_FAILURE;
}
@ -439,7 +439,7 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
if (!output->ParseFromArray(buf, header.size))
{
out.printerr("In call to %s::%s: error parsing received result.\n",
this->proto.c_str(), this->name.c_str());
this->plugin.c_str(), this->name.c_str());
delete[] buf;
return CR_LINK_FAILURE;
}
@ -453,7 +453,7 @@ command_result RemoteFunctionBase::execute(color_ostream &out,
text_decoder.decode(&text_data);
else
out.printerr("In call to %s::%s: received invalid text data.\n",
this->proto.c_str(), this->name.c_str());
this->plugin.c_str(), this->name.c_str());
break;
default:

@ -109,6 +109,24 @@ void RPCService::finalize(ServerConnection *owner, std::vector<ServerFunctionBas
}
}
void RPCService::dumpMethods(std::ostream & out) const
{
for (auto fn : functions)
{
std::string in_name = fn->p_in_template->GetTypeName();
size_t last_dot = in_name.rfind('.');
if (last_dot != std::string::npos)
in_name = in_name.substr(last_dot + 1);
std::string out_name = fn->p_out_template->GetTypeName();
last_dot = out_name.rfind('.');
if (last_dot != std::string::npos)
out_name = out_name.substr(last_dot + 1);
out << "// RPC " << fn->name << " : " << in_name << " -> " << out_name << endl;
}
}
ServerConnection::ServerConnection(CActiveSocket *socket)
: socket(socket), stream(this)
{

@ -35,6 +35,7 @@ using namespace std;
#include "VersionInfo.h"
#include "Error.h"
#include "Memory.h"
#include "PluginManager.h"
using namespace DFHack;
#include <tinyxml.h>
@ -133,18 +134,30 @@ void VersionInfoFactory::ParseVersion (TiXmlElement* entry, VersionInfo* mem)
if(!cstr_key)
throw Error::SymbolsXmlUnderspecifiedEntry(cstr_name);
const char *cstr_value = pMemEntry->Attribute("value");
if(!cstr_value)
const char *cstr_mangled = pMemEntry->Attribute("mangled");
if(!cstr_value && !cstr_mangled)
{
cerr << "Dummy symbol table entry: " << cstr_key << endl;
continue;
}
if ((is_vtable && no_vtables) || (!is_vtable && no_globals))
continue;
#ifdef DFHACK64
uintptr_t addr = strtoull(cstr_value, 0, 0);
#else
uintptr_t addr = strtol(cstr_value, 0, 0);
#endif
uintptr_t addr;
if (cstr_value) {
if (sizeof(addr) == sizeof(unsigned long))
addr = strtoul(cstr_value, 0, 0);
else
addr = strtoull(cstr_value, 0, 0);
} else {
addr = (uintptr_t)DFHack::LookupPlugin(DFHack::GLOBAL_NAMES, cstr_mangled);
if (!addr)
continue;
const char *cstr_offset = pMemEntry->Attribute("offset");
if (cstr_offset) {
unsigned long offset = strtoul(cstr_offset, 0, 0);
addr += offset;
}
}
if (is_vtable)
mem->setVTable(cstr_key, addr);
else

@ -1,52 +0,0 @@
if(NOT EXISTS ${dfhack_SOURCE_DIR}/.git/index OR NOT EXISTS ${dfhack_SOURCE_DIR}/.git/modules/library/xml/index)
MESSAGE(FATAL_ERROR "Could not find git index file(s)")
endif()
set(git_describe_tmp_h ${dfhack_SOURCE_DIR}/library/include/git-describe.tmp.h)
set(git_describe_h ${dfhack_SOURCE_DIR}/library/include/git-describe.h)
if(EXISTS ${git_describe_tmp_h} AND
NOT(${dfhack_SOURCE_DIR}/.git/index IS_NEWER_THAN ${git_describe_tmp_h}) AND
NOT(${dfhack_SOURCE_DIR}/.git/modules/library/xml/index IS_NEWER_THAN ${git_describe_tmp_h}) AND
NOT(${dfhack_SOURCE_DIR}/library/git-describe.cmake IS_NEWER_THAN ${git_describe_tmp_h}))
return()
endif()
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=8 --long
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
OUTPUT_VARIABLE DFHACK_GIT_DESCRIPTION)
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
OUTPUT_VARIABLE DFHACK_GIT_COMMIT)
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=8 --exact-match
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
RESULT_VARIABLE DFHACK_GIT_TAGGED_RESULT
OUTPUT_QUIET ERROR_QUIET)
# Check library/xml for matching commits
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD:library/xml
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
OUTPUT_VARIABLE DFHACK_GIT_XML_EXPECTED_COMMIT)
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}/library/xml"
OUTPUT_VARIABLE DFHACK_GIT_XML_COMMIT)
file(WRITE ${git_describe_tmp_h} "")
macro(git_describe_definition var)
string(STRIP "${${var}}" GIT_DEF_VALUE)
file(APPEND ${git_describe_tmp_h} "#define ${var} \"${GIT_DEF_VALUE}\"\n")
endmacro()
git_describe_definition(DFHACK_GIT_DESCRIPTION)
git_describe_definition(DFHACK_GIT_COMMIT)
git_describe_definition(DFHACK_GIT_XML_EXPECTED_COMMIT)
git_describe_definition(DFHACK_GIT_XML_COMMIT)
if(${DFHACK_GIT_TAGGED_RESULT} EQUAL 0)
file(APPEND ${git_describe_tmp_h} "#define DFHACK_GIT_TAGGED\n")
endif()
if(${DFHACK_GIT_XML_COMMIT} STREQUAL ${DFHACK_GIT_XML_EXPECTED_COMMIT})
file(APPEND ${git_describe_tmp_h} "#define DFHACK_GIT_XML_MATCH\n")
endif()
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
${git_describe_tmp_h} ${git_describe_h})

@ -0,0 +1,39 @@
# Generated from git-describe.cmake.in
set(DFHACK_BUILD_ID "@DFHACK_BUILD_ID@")
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=8 --long
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
OUTPUT_VARIABLE DFHACK_GIT_DESCRIPTION
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
OUTPUT_VARIABLE DFHACK_GIT_COMMIT
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=8 --exact-match
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
RESULT_VARIABLE DFHACK_GIT_TAGGED_RESULT
OUTPUT_QUIET ERROR_QUIET)
# Check library/xml for matching commits
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD:library/xml
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}"
OUTPUT_VARIABLE DFHACK_GIT_XML_EXPECTED_COMMIT
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
WORKING_DIRECTORY "${dfhack_SOURCE_DIR}/library/xml"
OUTPUT_VARIABLE DFHACK_GIT_XML_COMMIT
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(${DFHACK_GIT_TAGGED_RESULT} EQUAL 0)
set(DFHACK_GIT_TAGGED TRUE)
else()
set(DFHACK_GIT_TAGGED FALSE)
endif()
if(${DFHACK_GIT_XML_COMMIT} STREQUAL ${DFHACK_GIT_XML_EXPECTED_COMMIT})
set(DFHACK_GIT_XML_MATCH TRUE)
else()
set(DFHACK_GIT_XML_MATCH FALSE)
endif()
configure_file(${dfhack_SOURCE_DIR}/library/include/git-describe.h.in ${dfhack_SOURCE_DIR}/library/include/git-describe.h)

@ -1 +1 @@
git-describe.*
git-describe.h

@ -1,15 +1,18 @@
#pragma once
namespace DFHack {
namespace Version {
const char *dfhack_version();
const char *df_version();
const char *dfhack_version();
const char *dfhack_release();
const char *dfhack_build_id();
int dfhack_abi_version();
const char *git_description();
const char *git_commit();
const char *git_xml_commit();
const char *git_xml_expected_commit();
bool git_xml_match();
bool is_release();
bool is_prerelease();
}
@ -17,14 +20,17 @@ namespace DFHack {
#ifndef NO_DFHACK_VERSION_MACROS
#define DF_VERSION (DFHack::Version::df_version())
#define DFHACK_RELEASE (DFHack::Version::dfhack_release())
#define DFHACK_VERSION (DFHack::Version::dfhack_version())
#define DFHACK_RELEASE (DFHack::Version::dfhack_release())
#define DFHACK_BUILD_ID (DFHack::Version::dfhack_build_id())
#define DFHACK_ABI_VERSION (DFHack::Version::dfhack_abi_version())
#define DFHACK_GIT_DESCRIPTION (DFHack::Version::git_description())
#define DFHACK_GIT_COMMIT (DFHack::Version::git_commit())
#define DFHACK_GIT_XML_COMMIT (DFHack::Version::git_xml_commit())
#define DFHACK_GIT_XML_EXPECTED_COMMIT (DFHack::Version::git_xml_expected_commit())
#define DFHACK_GIT_XML_MATCH (DFHack::Version::git_xml_match())
#define DFHACK_IS_RELEASE (DFHack::Version::is_release())
#define DFHACK_IS_PRERELEASE (DFHack::Version::is_prerelease())
#endif

@ -45,6 +45,10 @@ using std::endl;
#define DFHACK_FUNCTION_SIG __func__
#endif
namespace DFHack {
class color_ostream;
}
/*! \namespace dts
* std.reverse() == dts, The namespace that include forward compatible helpers
* which can be used from newer standards. The preprocessor check prefers
@ -384,3 +388,4 @@ DFHACK_EXPORT std::string stl_vsprintf(const char *fmt, va_list args) Wformat(pr
DFHACK_EXPORT std::string UTF2DF(const std::string &in);
DFHACK_EXPORT std::string DF2UTF(const std::string &in);
DFHACK_EXPORT std::string DF2CONSOLE(const std::string &in);
DFHACK_EXPORT std::string DF2CONSOLE(DFHack::color_ostream &out, const std::string &in);

@ -67,6 +67,8 @@ namespace DFHack
// anon type, pretty much
struct DFLibrary;
// DFLibrary* that can be used to resolve global names
extern DFLibrary* GLOBAL_NAMES;
// Open a plugin library
DFHACK_EXPORT DFLibrary * OpenPlugin (const char * filename);
// find a symbol inside plugin

@ -150,10 +150,10 @@ namespace DFHack
class DFHACK_EXPORT RemoteFunctionBase : public RPCFunctionBase {
public:
bool bind(RemoteClient *client, const std::string &name,
const std::string &proto = std::string());
const std::string &plugin = std::string());
bool bind(color_ostream &out,
RemoteClient *client, const std::string &name,
const std::string &proto = std::string());
const std::string &plugin = std::string());
bool isValid() { return (id >= 0); }
@ -167,7 +167,7 @@ namespace DFHack
inline color_ostream &default_ostream();
command_result execute(color_ostream &out, const message_type *input, message_type *output);
std::string name, proto;
std::string name, plugin;
RemoteClient *p_client;
int16_t id;
};
@ -227,7 +227,7 @@ namespace DFHack
friend class RemoteFunctionBase;
bool bind(color_ostream &out, RemoteFunctionBase *function,
const std::string &name, const std::string &proto);
const std::string &name, const std::string &plugin);
public:
RemoteClient(color_ostream *default_output = NULL);
@ -269,8 +269,8 @@ namespace DFHack
}
inline bool RemoteFunctionBase::bind(RemoteClient *client, const std::string &name,
const std::string &proto) {
return bind(client->default_output(), client, name, proto);
const std::string &plugin) {
return bind(client->default_output(), client, name, plugin);
}
class RemoteSuspender {

@ -151,6 +151,7 @@ namespace DFHack
class DFHACK_EXPORT RPCService {
friend class ServerConnection;
friend class Plugin;
friend class Core;
std::vector<ServerFunctionBase*> functions;
std::map<std::string, ServerFunctionBase*> lookup;
@ -208,6 +209,8 @@ namespace DFHack
assert(!owner);
functions.push_back(new VoidServerMethod<Svc,In>(this, name, flags, fptr));
}
void dumpMethods(std::ostream & out) const;
};
class ServerConnection {

@ -0,0 +1,7 @@
#cmakedefine DFHACK_GIT_DESCRIPTION "@DFHACK_GIT_DESCRIPTION@"
#cmakedefine DFHACK_GIT_COMMIT "@DFHACK_GIT_COMMIT@"
#cmakedefine DFHACK_GIT_XML_EXPECTED_COMMIT "@DFHACK_GIT_XML_EXPECTED_COMMIT@"
#cmakedefine DFHACK_GIT_XML_COMMIT "@DFHACK_GIT_XML_COMMIT@"
#define DFHACK_BUILD_ID "@DFHACK_BUILD_ID@"
#cmakedefine DFHACK_GIT_TAGGED
#cmakedefine DFHACK_GIT_XML_MATCH

@ -55,7 +55,7 @@ class Block;
struct BiomeInfo {
// Determined by the 4-bit index in the designation bitfield
static const unsigned MAX_LAYERS = 16;
static constexpr unsigned MAX_LAYERS = 16;
df::coord2d pos;
int default_soil, default_stone, lava_stone;

@ -347,6 +347,8 @@ namespace DFHack
virtual df::job *getSelectedJob() { return nullptr; }
virtual df::building *getSelectedBuilding() { return nullptr; }
virtual df::plant *getSelectedPlant() { return nullptr; }
static virtual_identity _identity;
};
class DFHACK_EXPORT dfhack_lua_viewscreen : public dfhack_viewscreen {
@ -387,6 +389,8 @@ namespace DFHack
virtual df::job *getSelectedJob();
virtual df::building *getSelectedBuilding();
virtual df::plant *getSelectedPlant();
static virtual_identity _identity;
};
}

@ -184,5 +184,10 @@ DFHACK_EXPORT std::string getSquadName(df::unit *unit);
DFHACK_EXPORT df::activity_entry *getMainSocialActivity(df::unit *unit);
DFHACK_EXPORT df::activity_event *getMainSocialEvent(df::unit *unit);
// stress categories - 0 is highest stress
DFHACK_EXPORT extern const std::vector<int32_t> stress_cutoffs;
DFHACK_EXPORT int getStressCategory(df::unit *unit);
DFHACK_EXPORT int getStressCategoryRaw(int32_t stress_level);
}
}

@ -682,4 +682,40 @@ function addressof(obj)
return select(2, obj:sizeof())
end
function OrderedTable()
-- store values in a separate table to ensure that __index and __newindex
-- run on every table index operation
local t = {}
local key_to_index = {}
local index_to_key = {}
local mt = {}
function mt:__index(k)
return t[k]
end
function mt:__newindex(k, v)
if not key_to_index[k] then
table.insert(index_to_key, k)
key_to_index[k] = #index_to_key
end
t[k] = v
end
function mt:__pairs()
return function(_, k)
if k then
k = index_to_key[key_to_index[k] + 1]
else
k = index_to_key[1]
end
if k then
return k, t[k]
end
end, nil, nil
end
local self = {}
setmetatable(self, mt)
return self
end
return _ENV

@ -79,10 +79,6 @@ using df::global::world;
extern bool GetLocalFeature(t_feature &feature, df::coord2d rgn_pos, int32_t index);
#ifdef LINUX_BUILD
const unsigned MapExtras::BiomeInfo::MAX_LAYERS;
#endif
const BiomeInfo MapCache::biome_stub = {
df::coord2d(),
-1, -1, -1, -1,
@ -742,6 +738,7 @@ bool MapExtras::Block::Write ()
{
COPY(block->designation, designation);
block->flags.bits.designated = true;
block->dsgn_check_cooldown = 0;
dirty_designations = false;
}
if(dirty_tiles || dirty_veins)

@ -412,58 +412,6 @@ void Hide::merge(df::viewscreen* a)
}
} }
#ifdef _LINUX
class DFHACK_EXPORT renderer {
unsigned char *screen;
long *screentexpos;
char *screentexpos_addcolor;
unsigned char *screentexpos_grayscale;
unsigned char *screentexpos_cf;
unsigned char *screentexpos_cbr;
// For partial printing:
unsigned char *screen_old;
long *screentexpos_old;
char *screentexpos_addcolor_old;
unsigned char *screentexpos_grayscale_old;
unsigned char *screentexpos_cf_old;
unsigned char *screentexpos_cbr_old;
public:
virtual void update_tile(int x, int y) {};
virtual void update_all() {};
virtual void render() {};
virtual void set_fullscreen();
virtual void zoom(df::zoom_commands cmd);
virtual void resize(int w, int h) {};
virtual void grid_resize(int w, int h) {};
renderer() {
screen = NULL;
screentexpos = NULL;
screentexpos_addcolor = NULL;
screentexpos_grayscale = NULL;
screentexpos_cf = NULL;
screentexpos_cbr = NULL;
screen_old = NULL;
screentexpos_old = NULL;
screentexpos_addcolor_old = NULL;
screentexpos_grayscale_old = NULL;
screentexpos_cf_old = NULL;
screentexpos_cbr_old = NULL;
}
virtual ~renderer();
virtual bool get_mouse_coords(int &x, int &y) { return false; }
virtual bool uses_opengl();
};
#endif
void init_screen_module(Core *core)
{
#ifdef _LINUX
renderer tmp;
if (!strict_virtual_cast<df::renderer>((virtual_ptr)&tmp))
cerr << "Could not fetch the renderer vtable." << std::endl;
#endif
}
string Screen::getKeyDisplay(df::interface_key key)
{
if (enabler)
@ -970,3 +918,31 @@ df::plant *dfhack_lua_viewscreen::getSelectedPlant()
safe_call_lua(do_notify, 1, 1);
return Lua::GetDFObject<df::plant>(Lua::Core::State, -1);
}
#define STATIC_FIELDS_GROUP
#include "../DataStaticsFields.cpp"
using df::identity_traits;
#define CUR_STRUCT dfhack_viewscreen
static const struct_field_info dfhack_viewscreen_fields[] = {
{ METHOD(OBJ_METHOD, is_lua_screen), 0, 0 },
{ METHOD(OBJ_METHOD, getFocusString), 0, 0 },
{ METHOD(OBJ_METHOD, onShow), 0, 0 },
{ METHOD(OBJ_METHOD, onDismiss), 0, 0 },
{ METHOD(OBJ_METHOD, getSelectedUnit), 0, 0 },
{ METHOD(OBJ_METHOD, getSelectedItem), 0, 0 },
{ METHOD(OBJ_METHOD, getSelectedJob), 0, 0 },
{ METHOD(OBJ_METHOD, getSelectedBuilding), 0, 0 },
{ METHOD(OBJ_METHOD, getSelectedPlant), 0, 0 },
{ FLD_END }
};
#undef CUR_STRUCT
virtual_identity dfhack_viewscreen::_identity(sizeof(dfhack_viewscreen), nullptr, "dfhack_viewscreen", nullptr, &df::viewscreen::_identity, dfhack_viewscreen_fields);
#define CUR_STRUCT dfhack_lua_viewscreen
static const struct_field_info dfhack_lua_viewscreen_fields[] = {
{ FLD_END }
};
#undef CUR_STRUCT
virtual_identity dfhack_lua_viewscreen::_identity(sizeof(dfhack_lua_viewscreen), nullptr, "dfhack_lua_viewscreen", nullptr, &dfhack_viewscreen::_identity, dfhack_lua_viewscreen_fields);

@ -1626,3 +1626,30 @@ bool Units::isDomesticated(df::unit* unit)
}
return tame;
}
// 50000 and up is level 0, 25000 and up is level 1, etc.
const vector<int32_t> Units::stress_cutoffs {50000, 25000, 10000, -10000, -25000, -50000, -100000};
int Units::getStressCategory(df::unit *unit)
{
CHECK_NULL_POINTER(unit);
if (!unit->status.current_soul)
return int(stress_cutoffs.size()) / 2;
return getStressCategoryRaw(unit->status.current_soul->personality.stress_level);
}
int Units::getStressCategoryRaw(int32_t stress_level)
{
int max_level = int(stress_cutoffs.size()) - 1;
int level = max_level;
for (int i = max_level; i >= 0; i--)
{
if (stress_level >= stress_cutoffs[i])
{
level = i;
}
}
return level;
}

@ -1 +1 @@
Subproject commit 372993c58851153320f3390119cdd2d97caef9db
Subproject commit 57928311390bb67fe0cd5deec14e2d4621b13a58

@ -1,3 +0,0 @@
# You can add custom plugins here to avoid touching plugins/CMakeLists.txt,
# This can be useful if you've made modifications to that file and try to
# switch between branches that have also made modifications to it.

@ -178,4 +178,12 @@ if(BUILD_SKELETON)
add_subdirectory(skeleton)
endif()
if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.custom.txt")
FILE(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.custom.txt" "# You can add custom plugins here to avoid touching plugins/CMakeLists.txt,
# This can be useful if you've made modifications to that file and try to
# switch between branches that have also made modifications to it.
")
endif()
INCLUDE(CMakeLists.custom.txt)

@ -1,15 +1,4 @@
IF(UNIX)
add_definitions(-DLINUX_BUILD)
SET(CMAKE_CXX_FLAGS_DEBUG "-g -Wall")
SET(CMAKE_CXX_FLAGS "-fvisibility=hidden")
SET(CMAKE_C_FLAGS "-fvisibility=hidden")
IF(DFHACK_BUILD_64)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m64 -mno-avx")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m64 -mno-avx")
ELSE()
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32")
ENDIF()
IF(NOT APPLE)
# Linux: Check for unresolved symbols at link time
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,-z,defs")

@ -101,23 +101,10 @@ static color_value monitor_colors[] =
static int get_happiness_cat(df::unit *unit)
{
if (!unit || !unit->status.current_soul)
return 3;
int stress = unit->status.current_soul->personality.stress_level;
if (stress >= 500000)
return 0;
else if (stress >= 250000)
return 1;
else if (stress >= 100000)
return 2;
else if (stress >= 60000)
return 3;
else if (stress >= 30000)
return 4;
else if (stress >= 0)
return 5;
else
return 6;
int level = Units::getStressCategory(unit);
if (level < 0) level = 0;
if (level > 6) level = 6;
return level;
}
static int get_max_history()

@ -58,6 +58,8 @@ namespace embark_assist {
int16_t biome_index[10]; // Indexed through biome_offset; -1 = null, Index of region, [0] not used
int16_t biome[10]; // Indexed through biome_offset; -1 = null, df::biome_type, [0] not used
uint8_t biome_count;
int16_t min_temperature[10]; // Indexed through biome_offset; -30000 = null, Urists - 10000, [0] not used
int16_t max_temperature[10]; // Indexed through biome_offset; -30000 = null, Urists - 10000, [0] not used
bool blood_rain[10];
bool blood_rain_possible;
bool blood_rain_full;
@ -229,16 +231,14 @@ namespace embark_assist {
None
};
/* // Future possible enhancement
enum class freezing_ranges : int8_t {
NA = -1,
Permanent,
At_Least_Partial,
Partial,
At_Most_Partial,
Never
NA = -1,
Permanent,
At_Least_Partial,
Partial,
At_Most_Partial,
Never
};
*/
struct finders {
uint16_t x_dim;
@ -256,7 +256,7 @@ namespace embark_assist {
soil_ranges soil_min;
all_present_ranges soil_min_everywhere;
soil_ranges soil_max;
/*freezing_ranges freezing;*/
freezing_ranges freezing;
yes_no_ranges blood_rain; // Will probably blow up with the magic release arcs...
syndrome_rain_ranges syndrome_rain;
reanimation_ranges reanimation;

@ -12,6 +12,7 @@
#include "df/material_flags.h"
#include "df/viewscreen_choose_start_sitest.h"
#include "df/world.h"
#include "df/world_data.h"
#include "df/world_region_type.h"
#include "df/world_raws.h"
@ -46,6 +47,7 @@ namespace embark_assist {
soil_min,
soil_min_everywhere,
soil_max,
freezing,
blood_rain,
syndrome_rain,
reanimation,
@ -314,14 +316,14 @@ namespace embark_assist {
switch (i) {
case fields::x_dim:
for (int16_t k = 1; k < 16; k++) {
for (int16_t k = 1; k <= 16; k++) {
element->list.push_back({ std::to_string(k), k });
}
break;
case fields::y_dim:
for (int16_t k = 1; k < 16; k++) {
for (int16_t k = 1; k <= 16; k++) {
element->list.push_back({ std::to_string(k), k });
}
@ -574,6 +576,52 @@ namespace embark_assist {
break;
case fields::freezing:
{
embark_assist::defs::freezing_ranges k = embark_assist::defs::freezing_ranges::NA;
while (true) {
switch (k) {
case embark_assist::defs::freezing_ranges::NA:
element->list.push_back({ "N/A", static_cast<int8_t>(k) });
break;
case embark_assist::defs::freezing_ranges::Permanent:
element->list.push_back({ "Permanent", static_cast<int8_t>(k) });
break;
case embark_assist::defs::freezing_ranges::At_Least_Partial:
element->list.push_back({ "At Least Partially Frozen", static_cast<int8_t>(k) });
break;
case embark_assist::defs::freezing_ranges::Partial:
element->list.push_back({ "Partially Frozen", static_cast<int8_t>(k) });
break;
case embark_assist::defs::freezing_ranges::At_Most_Partial:
element->list.push_back({ "At Most Partially Frozen", static_cast<int8_t>(k) });
break;
case embark_assist::defs::freezing_ranges::Never:
element->list.push_back({ "Never Frozen", static_cast<int8_t>(k) });
break;
}
if (k == embark_assist::defs::freezing_ranges::Never ||
(world->world_data->world_height != 17 && // Can't handle temperature in non standard height worlds.
world->world_data->world_height != 33 &&
world->world_data->world_height != 65 &&
world->world_data->world_height != 129 &&
world->world_data->world_height != 257)) {
break;
}
k = static_cast <embark_assist::defs::freezing_ranges>(static_cast<int8_t>(k) + 1);
}
}
break;
case fields::syndrome_rain:
{
embark_assist::defs::syndrome_rain_ranges k = embark_assist::defs::syndrome_rain_ranges::NA;
@ -917,6 +965,10 @@ namespace embark_assist {
state->finder_list.push_back({ "Min Soil Everywhere", static_cast<int8_t>(i) });
break;
case fields::freezing:
state->finder_list.push_back({ "Freezing", static_cast<int8_t>(i) });
break;
case fields::blood_rain:
state->finder_list.push_back({ "Blood Rain", static_cast<int8_t>(i) });
break;
@ -1141,6 +1193,11 @@ namespace embark_assist {
static_cast<embark_assist::defs::all_present_ranges>(state->ui[static_cast<uint8_t>(i)]->current_value);
break;
case fields::freezing:
finder.freezing =
static_cast<embark_assist::defs::freezing_ranges>(state->ui[static_cast<uint8_t>(i)]->current_value);
break;
case fields::blood_rain:
finder.blood_rain =
static_cast<embark_assist::defs::yes_no_ranges>(state->ui[static_cast<uint8_t>(i)]->current_value);
@ -1303,11 +1360,11 @@ namespace embark_assist {
Screen::dismiss(this);
return;
} else if (input->count(df::interface_key::CURSOR_LEFT) ||
input->count(df::interface_key::CURSOR_RIGHT)) {
} else if (input->count(df::interface_key::STANDARDSCROLL_LEFT) ||
input->count(df::interface_key::STANDARDSCROLL_RIGHT)) {
state->finder_list_active = !state->finder_list_active;
} else if (input->count(df::interface_key::CURSOR_UP)) {
} else if (input->count(df::interface_key::STANDARDSCROLL_UP)) {
if (state->finder_list_active) {
if (state->finder_list_focus > 0) {
state->finder_list_focus--;
@ -1324,7 +1381,7 @@ namespace embark_assist {
}
}
} else if (input->count(df::interface_key::CURSOR_DOWN)) {
} else if (input->count(df::interface_key::STANDARDSCROLL_DOWN)) {
if (state->finder_list_active) {
if (state->finder_list_focus < static_cast<uint16_t>(last_fields)) {
state->finder_list_focus++;
@ -1366,59 +1423,76 @@ namespace embark_assist {
void ViewscreenFindUi::render() {
// color_ostream_proxy out(Core::getInstance().getConsole());
auto screen_size = DFHack::Screen::getWindowSize();
const int list_column = 53;
uint16_t top_row = 2;
uint16_t list_column = 53;
uint16_t offset = 0;
Screen::clear();
Screen::drawBorder("Embark Assistant Site Finder");
Screen::drawBorder(" Embark Assistant Site Finder ");
embark_assist::screen::paintString(lr_pen, 1, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CURSOR_LEFT).c_str());
embark_assist::screen::paintString(lr_pen, 1, 1, DFHack::Screen::getKeyDisplay(df::interface_key::STANDARDSCROLL_LEFT).c_str());
embark_assist::screen::paintString(white_pen, 2, 1, "/");
embark_assist::screen::paintString(lr_pen, 3, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CURSOR_RIGHT).c_str());
embark_assist::screen::paintString(white_pen, 4, 1, ":\x1b/\x1a");
embark_assist::screen::paintString(lr_pen, 9, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CURSOR_UP).c_str());
embark_assist::screen::paintString(white_pen, 10, 1, "/");
embark_assist::screen::paintString(lr_pen, 11, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CURSOR_DOWN).c_str());
embark_assist::screen::paintString(white_pen, 12, 1, ":Up/Down");
embark_assist::screen::paintString(lr_pen, 21, 1, DFHack::Screen::getKeyDisplay(df::interface_key::SELECT).c_str());
embark_assist::screen::paintString(white_pen, 26, 1, ":Select");
embark_assist::screen::paintString(lr_pen, 34, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_F).c_str());
embark_assist::screen::paintString(white_pen, 35, 1, ":Find");
embark_assist::screen::paintString(lr_pen, 3, 1, DFHack::Screen::getKeyDisplay(df::interface_key::STANDARDSCROLL_RIGHT).c_str());
embark_assist::screen::paintString(white_pen, 4, 1, ": \x1b/\x1a");
embark_assist::screen::paintString(lr_pen, 10, 1, DFHack::Screen::getKeyDisplay(df::interface_key::STANDARDSCROLL_UP).c_str());
embark_assist::screen::paintString(white_pen, 11, 1, "/");
embark_assist::screen::paintString(lr_pen, 12, 1, DFHack::Screen::getKeyDisplay(df::interface_key::STANDARDSCROLL_DOWN).c_str());
embark_assist::screen::paintString(white_pen, 13, 1, ": \x18/\x19");
embark_assist::screen::paintString(lr_pen, 19, 1, DFHack::Screen::getKeyDisplay(df::interface_key::SELECT).c_str());
embark_assist::screen::paintString(white_pen, 24, 1, ": Select");
embark_assist::screen::paintString(lr_pen, 33, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_F).c_str());
embark_assist::screen::paintString(white_pen, 34, 1, ": Find");
embark_assist::screen::paintString(lr_pen, 41, 1, DFHack::Screen::getKeyDisplay(df::interface_key::LEAVESCREEN).c_str());
embark_assist::screen::paintString(white_pen, 44, 1, ":Abort");
embark_assist::screen::paintString(lr_pen, 51, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_S).c_str());
embark_assist::screen::paintString(white_pen, 52, 1, ":Save");
embark_assist::screen::paintString(lr_pen, 58, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_L).c_str());
embark_assist::screen::paintString(white_pen, 59, 1, ":Load");
embark_assist::screen::paintString(white_pen, 44, 1, ": Abort");
embark_assist::screen::paintString(lr_pen, 52, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_S).c_str());
embark_assist::screen::paintString(white_pen, 53, 1, ": Save");
embark_assist::screen::paintString(lr_pen, 60, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_L).c_str());
embark_assist::screen::paintString(white_pen, 61, 1, ": Load");
// Implement scrolling lists if they don't fit on the screen.
if (int32_t(state->finder_list.size()) > screen_size.y - top_row - 1) {
offset = (screen_size.y - top_row - 1) / 2;
if (state->finder_list_focus < offset) {
offset = 0;
}
else {
offset = state->finder_list_focus - offset;
}
for (uint16_t i = 0; i < state->finder_list.size(); i++) {
if (int32_t(state->finder_list.size() - offset) < screen_size.y - top_row - 1) {
offset = static_cast<uint16_t>(state->finder_list.size()) - (screen_size.y - top_row - 1);
}
}
for (uint16_t i = offset; i < state->finder_list.size(); i++) {
if (i == state->finder_list_focus) {
if (state->finder_list_active) {
embark_assist::screen::paintString(active_pen, 1, 2 + i, state->finder_list[i].text);
embark_assist::screen::paintString(active_pen, 1, top_row + i - offset, state->finder_list[i].text);
}
else {
embark_assist::screen::paintString(passive_pen, 1, 2 + i, state->finder_list[i].text);
embark_assist::screen::paintString(passive_pen, 1, top_row + i - offset, state->finder_list[i].text);
}
embark_assist::screen::paintString(active_pen,
21,
2 + i,
top_row + i - offset,
state->ui[i]->list[state->ui[i]->current_display_value].text);
}
else {
embark_assist::screen::paintString(normal_pen, 1, 2 + i, state->finder_list[i].text);
embark_assist::screen::paintString(normal_pen, 1, top_row + i - offset, state->finder_list[i].text);
embark_assist::screen::paintString(white_pen,
21,
2 + i,
top_row + i - offset,
state->ui[i]->list[state->ui[i]->current_display_value].text);
}
}
// Implement scrolling lists if they don't fit on the screen.
if (int32_t(state->ui[state->finder_list_focus]->list.size()) > screen_size.y - 3) {
offset = (screen_size.y - 3) / 2;
offset = 0;
if (int32_t(state->ui[state->finder_list_focus]->list.size()) > screen_size.y - top_row - 1) {
offset = (screen_size.y - top_row - 1) / 2;
if (state->ui[state->finder_list_focus]->current_index < offset) {
offset = 0;
}
@ -1426,22 +1500,22 @@ namespace embark_assist {
offset = state->ui[state->finder_list_focus]->current_index - offset;
}
if (int32_t(state->ui[state->finder_list_focus]->list.size() - offset) < screen_size.y - 3) {
offset = static_cast<uint16_t>(state->ui[state->finder_list_focus]->list.size()) - (screen_size.y - 3);
if (int32_t(state->ui[state->finder_list_focus]->list.size() - offset) < screen_size.y - top_row - 1) {
offset = static_cast<uint16_t>(state->ui[state->finder_list_focus]->list.size()) - (screen_size.y - top_row - 1);
}
}
for (uint16_t i = 0; i < state->ui[state->finder_list_focus]->list.size(); i++) {
for (uint16_t i = offset; i < state->ui[state->finder_list_focus]->list.size(); i++) {
if (i == state->ui[state->finder_list_focus]->current_index) {
if (!state->finder_list_active) { // Negated expression to get the display lines in the same order as above.
embark_assist::screen::paintString(active_pen, list_column, 2 + i - offset, state->ui[state->finder_list_focus]->list[i].text);
embark_assist::screen::paintString(active_pen, list_column, top_row + i - offset, state->ui[state->finder_list_focus]->list[i].text);
}
else {
embark_assist::screen::paintString(passive_pen, list_column, 2 + i - offset, state->ui[state->finder_list_focus]->list[i].text);
embark_assist::screen::paintString(passive_pen, list_column, top_row + i - offset, state->ui[state->finder_list_focus]->list[i].text);
}
}
else {
embark_assist::screen::paintString(normal_pen, list_column, 2 + i - offset, state->ui[state->finder_list_focus]->list[i].text);
embark_assist::screen::paintString(normal_pen, list_column, top_row + i - offset, state->ui[state->finder_list_focus]->list[i].text);
}
}

@ -99,7 +99,7 @@ namespace embark_assist{
switch (current_page) {
case pages::Intro:
Screen::drawBorder("Embark Assistant Help/Info Introduction Page");
Screen::drawBorder(" Embark Assistant Help/Info Introduction Page ");
help_text.push_back("Embark Assistant is used on the embark selection screen to provide information");
help_text.push_back("to help selecting a suitable embark site. It provides three services:");
@ -108,10 +108,10 @@ namespace embark_assist{
help_text.push_back("- Site find search. Richer set of selection criteria than the vanilla");
help_text.push_back(" DF Find that Embark Assistant suppresses (by using the same key).");
help_text.push_back("");
help_text.push_back("The functionality requires a screen height of at least 46 lines to display");
help_text.push_back("correctly (that's the height of the Finder screen), as fitting everything");
help_text.push_back("onto a standard 80*25 screen would be too challenging. The help is adjusted");
help_text.push_back("to fit into onto an 80*46 screen as well.");
help_text.push_back("The functionality requires a screen height larger than the default 80*25,");
help_text.push_back("and while the Finder screen provides for scrolling, the embark resources");
help_text.push_back("list will spill over the bottom if many resources are present and the");
help_text.push_back("screen isn't deep enough. This help info is adapted to fit onto 80*46.");
help_text.push_back("This help/info is split over several screens, and you can move between them");
help_text.push_back("using the TAB/Shift-TAB keys, and leave the help from any screen using ESC.");
help_text.push_back("");
@ -139,7 +139,7 @@ namespace embark_assist{
break;
case pages::General:
Screen::drawBorder("Embark Assistant Help/Info General Page");
Screen::drawBorder(" Embark Assistant Help/Info General Page ");
help_text.push_back("The Embark Assistant overlays the region map with characters indicating sites");
help_text.push_back("normally not displayed by DF. The following key is used:");
@ -178,7 +178,7 @@ namespace embark_assist{
break;
case pages::Finder:
Screen::drawBorder("Embark Assistant Help/Info Find Page");
Screen::drawBorder(" Embark Assistant Help/Info Find Page ");
help_text.push_back("The Embark Assist Finder page is brought up with the f command key.");
help_text.push_back("The top of the Finder page lists the command keys available on the page:");
@ -209,6 +209,9 @@ namespace embark_assist{
help_text.push_back("Min/Max soil uses the same terminology as DF for 1-4. The Min Soil");
help_text.push_back("Everywhere toggles the Min Soil parameter between acting as All and");
help_text.push_back("and Present.");
help_text.push_back("Freezing allows you to select embarks to select/avoid various freezing");
help_text.push_back("conditions. Note that the minimum temperature is held for only 10 ticks");
help_text.push_back("in many embarks.");
help_text.push_back("Syndrome Rain allows you to search for Permanent and Temporary syndromes,");
help_text.push_back("where Permanent allows for Temporary ones as well, but not the reverse, as");
help_text.push_back("Not Permanent matches everything except Permanent syndromes.");
@ -222,7 +225,7 @@ namespace embark_assist{
break;
case pages::Caveats:
Screen::drawBorder("Embark Assistant Help/Info Caveats Page");
Screen::drawBorder(" Embark Assistant Help/Info Caveats Page ");
help_text.push_back("Find searching first does a sanity check (e.g. max < min) and then a rough");
help_text.push_back("world tile match to find tiles that may have matching embarks. This results");
@ -261,7 +264,7 @@ namespace embark_assist{
help_text.push_back(" reaching caverns that have been removed at world gen to fail to be");
help_text.push_back(" generated at all. It's likely this bug also affects magma pools.");
help_text.push_back(" This plugin does not address this but scripts can correct it.");
help_text.push_back("Version 0.4 2018-06-21");
help_text.push_back("Version 0.5 2018-07-13");
break;
}
@ -270,9 +273,9 @@ namespace embark_assist{
embark_assist::screen::paintString(pen_lr, 1, 1, DFHack::Screen::getKeyDisplay(df::interface_key::CHANGETAB).c_str());
embark_assist::screen::paintString(pen, 4, 1, "/");
embark_assist::screen::paintString(pen_lr, 5, 1, DFHack::Screen::getKeyDisplay(df::interface_key::SEC_CHANGETAB).c_str());
embark_assist::screen::paintString(pen, 14, 1, ":Next/Previous Page");
embark_assist::screen::paintString(pen_lr, 34, 1, DFHack::Screen::getKeyDisplay(df::interface_key::LEAVESCREEN).c_str());
embark_assist::screen::paintString(pen, 37, 1, ":Leave Info/Help");
embark_assist::screen::paintString(pen, 14, 1, ": Next/Previous Page");
embark_assist::screen::paintString(pen_lr, 35, 1, DFHack::Screen::getKeyDisplay(df::interface_key::LEAVESCREEN).c_str());
embark_assist::screen::paintString(pen, 38, 1, ": Leave Info/Help");
for (uint16_t i = 0; i < help_text.size(); i++) {
embark_assist::screen::paintString(pen, 1, 2 + i, help_text[i]);
@ -298,10 +301,10 @@ namespace embark_assist{
break;
case pages::Finder:
embark_assist::screen::paintString(pen_lr, 1, 4, DFHack::Screen::getKeyDisplay(df::interface_key::CURSOR_LEFT).c_str());
embark_assist::screen::paintString(pen_lr, 3, 4, DFHack::Screen::getKeyDisplay(df::interface_key::CURSOR_RIGHT).c_str());
embark_assist::screen::paintString(pen_lr, 1, 5, DFHack::Screen::getKeyDisplay(df::interface_key::CURSOR_UP).c_str());
embark_assist::screen::paintString(pen_lr, 3, 5, DFHack::Screen::getKeyDisplay(df::interface_key::CURSOR_DOWN).c_str());
embark_assist::screen::paintString(pen_lr, 1, 4, DFHack::Screen::getKeyDisplay(df::interface_key::STANDARDSCROLL_LEFT).c_str());
embark_assist::screen::paintString(pen_lr, 3, 4, DFHack::Screen::getKeyDisplay(df::interface_key::STANDARDSCROLL_RIGHT).c_str());
embark_assist::screen::paintString(pen_lr, 1, 5, DFHack::Screen::getKeyDisplay(df::interface_key::STANDARDSCROLL_UP).c_str());
embark_assist::screen::paintString(pen_lr, 3, 5, DFHack::Screen::getKeyDisplay(df::interface_key::STANDARDSCROLL_DOWN).c_str());
embark_assist::screen::paintString(pen_lr, 1, 6, DFHack::Screen::getKeyDisplay(df::interface_key::SELECT).c_str());
embark_assist::screen::paintString(pen_lr, 1, 7, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_F).c_str());
embark_assist::screen::paintString(pen_lr, 1, 8, DFHack::Screen::getKeyDisplay(df::interface_key::LEAVESCREEN).c_str());

@ -48,6 +48,8 @@ namespace embark_assist {
bool flux_found = false;
uint8_t max_soil = 0;
bool uneven = false;
int16_t min_temperature = survey_results->at(x).at(y).min_temperature[mlt->at(start_x).at(start_y).biome_offset];
int16_t max_temperature = survey_results->at(x).at(y).max_temperature[mlt->at(start_x).at(start_y).biome_offset];
bool blood_rain_found = false;
bool permanent_syndrome_rain_found = false;
bool temporary_syndrome_rain_found = false;
@ -185,6 +187,21 @@ namespace embark_assist {
if (finder->soil_max != embark_assist::defs::soil_ranges::NA &&
mlt->at(i).at(k).soil_depth > static_cast<uint16_t>(finder->soil_max)) return false;
// Freezing
if (min_temperature > survey_results->at(x).at(y).min_temperature[mlt->at(i).at(k).biome_offset]) {
min_temperature = survey_results->at(x).at(y).min_temperature[mlt->at(i).at(k).biome_offset];
}
if (max_temperature < survey_results->at(x).at(y).max_temperature[mlt->at(i).at(k).biome_offset]) {
max_temperature = survey_results->at(x).at(y).max_temperature[mlt->at(i).at(k).biome_offset];
}
if (min_temperature <= 0 &&
finder->freezing == embark_assist::defs::freezing_ranges::Never) return false;
if (max_temperature > 0 &&
finder->freezing == embark_assist::defs::freezing_ranges::Permanent) return false;
// Blood Rain
if (survey_results->at(x).at(y).blood_rain[mlt->at(i).at(k).biome_offset]) {
if (finder->blood_rain == embark_assist::defs::yes_no_ranges::No) return false;
@ -323,6 +340,17 @@ namespace embark_assist {
finder->soil_min_everywhere == embark_assist::defs::all_present_ranges::Present &&
max_soil < static_cast<uint8_t>(finder->soil_min)) return false;
// Freezing
if (finder->freezing == embark_assist::defs::freezing_ranges::At_Least_Partial &&
min_temperature > 0) return false;
if (finder->freezing == embark_assist::defs::freezing_ranges::Partial &&
(min_temperature > 0 ||
max_temperature <= 0)) return false;
if (finder->freezing == embark_assist::defs::freezing_ranges::At_Most_Partial &&
max_temperature <= 0) return false;
// Blood Rain
if (finder->blood_rain == embark_assist::defs::yes_no_ranges::Yes && !blood_rain_found) return false;
@ -415,7 +443,7 @@ namespace embark_assist {
uint16_t y,
embark_assist::defs::finders *finder) {
// color_ostream_proxy out(Core::getInstance().getConsole());
color_ostream_proxy out(Core::getInstance().getConsole());
df::world_data *world_data = world->world_data;
embark_assist::defs::region_tile_datum *tile = &survey_results->at(x).at(y);
const uint16_t embark_size = finder->x_dim * finder->y_dim;
@ -622,6 +650,61 @@ namespace embark_assist {
break;
}
// Freezing
if (finder->freezing != embark_assist::defs::freezing_ranges::NA)
{
int16_t max_max_temperature = tile->max_temperature[5];
int16_t min_max_temperature = tile->max_temperature[5];
int16_t max_min_temperature = tile->min_temperature[5];
int16_t min_min_temperature = tile->min_temperature[5];
for (uint8_t i = 1; i < 10; i++) {
if (tile->max_temperature[i] > max_max_temperature) {
max_max_temperature = tile->max_temperature[i];
}
if (tile->max_temperature[i] != - 30000 &&
tile->max_temperature[i] < min_max_temperature) {
min_max_temperature = tile->max_temperature[i];
}
if (tile->min_temperature[i] != -30000 &&
tile->min_temperature[i] < min_min_temperature) {
min_min_temperature = tile->min_temperature[i];
}
if (tile->min_temperature[i] > max_min_temperature) {
max_min_temperature = tile->min_temperature[i];
}
}
switch (finder->freezing) {
case embark_assist::defs::freezing_ranges::NA:
break; // Excluded above, but the Travis complains if it's not here.
case embark_assist::defs::freezing_ranges::Permanent:
if (min_max_temperature > 0) return false;
break;
case embark_assist::defs::freezing_ranges::At_Least_Partial:
if (min_min_temperature > 0) return false;
break;
case embark_assist::defs::freezing_ranges::Partial:
if (min_min_temperature > 0 ||
max_max_temperature <= 0) return false;
break;
case embark_assist::defs::freezing_ranges::At_Most_Partial:
if (max_max_temperature <= 0) return false;
break;
case embark_assist::defs::freezing_ranges::Never:
if (max_min_temperature <= 0) return false;
break;
}
}
// Blood Rain
switch (finder->blood_rain) {
case embark_assist::defs::yes_no_ranges::NA:

@ -72,7 +72,7 @@ namespace embark_assist {
if ((map_size - result * factor) * 2 != factor) {
result = (map_size + factor / 2) / factor;
}
if (result <= available_screen) {
return {result, factor};
}
@ -151,21 +151,21 @@ namespace embark_assist {
if (state->matching) state->show = true;
Screen::drawBorder("Embark Assistant");
Screen::drawBorder(" Embark Assistant ");
Screen::Pen pen_lr(' ', COLOR_LIGHTRED);
Screen::Pen pen_w(' ', COLOR_WHITE);
Screen::paintString(pen_lr, width - 28, 20, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_I).c_str(), false);
Screen::paintString(pen_w, width - 27, 20, ":Embark Assistant Info", false);
Screen::paintString(pen_w, width - 27, 20, ": Embark Assistant Info", false);
Screen::paintString(pen_lr, width - 28, 21, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_F).c_str(), false);
Screen::paintString(pen_w, width - 27, 21, ":Find Embark ", false);
Screen::paintString(pen_w, width - 27, 21, ": Find Embark ", false);
Screen::paintString(pen_lr, width - 28, 22, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_C).c_str(), false);
Screen::paintString(pen_w, width - 27, 22, ":Cancel/Clear Find", false);
Screen::paintString(pen_w, width - 27, 22, ": Cancel/Clear Find", false);
Screen::paintString(pen_lr, width - 28, 23, DFHack::Screen::getKeyDisplay(df::interface_key::CUSTOM_Q).c_str(), false);
Screen::paintString(pen_w, width - 27, 23, ":Quit Embark Assistant", false);
Screen::paintString(pen_w, width - 28, 25, "Matching World Tiles", false);
Screen::paintString(empty_pen, width - 7, 25, to_string(state->match_count), false);
Screen::paintString(pen_w, width - 27, 23, ": Quit Embark Assistant", false);
Screen::paintString(pen_w, width - 28, 25, "Matching World Tiles:", false);
Screen::paintString(empty_pen, width - 6, 25, to_string(state->match_count), false);
if (height > 25) { // Mask the vanilla DF find help as it's overridden.
Screen::paintString(pen_w, 50, height - 2, " ", false);
@ -192,10 +192,10 @@ namespace embark_assist {
if (left_x < 0) { left_x = 0; }
if (top_y < 0) { top_y = 0; }
right_x = left_x + width / 2 - 7 - 18;
bottom_y = top_y + height - 8 - 2;
if (right_x >= world->worldgen.worldgen_parms.dim_x) {
right_x = world->worldgen.worldgen_parms.dim_x - 1;
left_x = right_x - (width / 2 - 7 - 18);
@ -226,7 +226,7 @@ namespace embark_assist {
}
}
}
/* // Stuff for trying to replicate the DF right world map sizing logic. Close, but not there.
Screen::Pen pen(' ', COLOR_YELLOW);
// Boundaries of the top level world map
@ -234,7 +234,7 @@ namespace embark_assist {
// Screen::paintString(pen, width - 30, 2, "X", false); // Marks UR corner of right world map area.
// Screen::paintString(pen, width / 2 - 5, height - 8, "X", false); // BL corner of right world map area.
// Screen::paintString(pen, width - 30, height - 8, "X", false); // BR corner of right world map area.
uint16_t l_width = width - 30 - (width / 2 - 5) + 1; // Horizontal space available for right world map.
uint16_t l_height = height - 8 - 2 + 1; // Vertical space available for right world map.
df::coord2d size_factor_x = world_dimension_size(l_width, world->worldgen.worldgen_parms.dim_x);
@ -360,7 +360,7 @@ void embark_assist::overlay::set_embark(embark_assist::defs::site_infos *site_in
if (site_info->aquifer) {
if (site_info->aquifer_full) {
state->embark_info.push_back({ Screen::Pen(' ', COLOR_BLUE), "Aquifer" });
}
else {
state->embark_info.push_back({ Screen::Pen(' ', COLOR_LIGHTBLUE), "Aquifer" });

@ -1,3 +1,4 @@
#include <math.h>
#include <vector>
#include "Core.h"
@ -399,6 +400,67 @@ namespace embark_assist {
}
}
}
//=================================================================================
int16_t min_temperature(int16_t max_temperature, uint16_t latitude) {
uint16_t divisor;
uint16_t steps;
uint16_t lat;
if (world->world_data->flip_latitude == df::world_data::T_flip_latitude::None) {
return max_temperature;
}
else if (world->world_data->flip_latitude == df::world_data::T_flip_latitude::North ||
world->world_data->flip_latitude == df::world_data::T_flip_latitude::South) {
steps = world->world_data->world_height / 2;
if (latitude > steps) {
lat = world->world_data->world_height - 1 - latitude;
}
else
{
lat = latitude;
}
}
else { // Both
steps = world->world_data->world_height / 4;
if (latitude < steps) {
lat = latitude;
}
else if (latitude <= steps * 2) {
lat = steps * 2 - latitude;
}
else if (latitude <= steps * 3) {
lat = latitude - steps * 2;
}
else {
lat = world->world_data->world_height - latitude;
}
}
if (world->world_data->world_height == 17) {
divisor = (57 / steps * lat + 0.4);
}
else if (world->world_data->world_height == 33) {
divisor = (61 / steps * lat + 0.1);
}
else if (world->world_data->world_height == 65) {
divisor = (63 / steps * lat);
}
else if (world->world_data->world_height == 129 ||
world->world_data->world_height == 257) {
divisor = (64 / steps * lat);
}
else {
return max_temperature; // Not any standard world height. No formula available
}
return max_temperature - ceil(divisor * 3 / 4);
}
}
}
@ -450,7 +512,10 @@ void embark_assist::survey::clear_results(embark_assist::defs::match_results *ma
void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_data *geo_summary,
embark_assist::defs::world_tile_data *survey_results) {
// color_ostream_proxy out(Core::getInstance().getConsole());
// color_ostream_proxy out(Core::getInstance().getConsole());
int16_t temperature;
bool negative;
embark_assist::survey::geo_survey(geo_summary);
for (uint16_t i = 0; i < world->worldgen.worldgen_parms.dim_x; i++) {
@ -488,6 +553,23 @@ void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_dat
results.biome_index[l] = world_data->region_map[adjusted.x][adjusted.y].region_id;
results.biome[l] = get_biome_type(adjusted.x, adjusted.y, k);
temperature = world_data->region_map[adjusted.x][adjusted.y].temperature;
negative = temperature < 0;
if (negative) {
temperature = -temperature;
}
results.max_temperature[l] = (temperature / 4) * 3;
if (temperature % 4 > 1) {
results.max_temperature[l] = results.max_temperature[l] + temperature % 4 - 1;
}
if (negative) {
results.max_temperature[l] = -results.max_temperature[l];
}
results.min_temperature[l] = min_temperature(results.max_temperature[l], adjusted.y);
geo_index = world_data->region_map[adjusted.x][adjusted.y].geo_index;
if (!geo_summary->at(geo_index).aquifer_absent) results.aquifer_count++;
@ -518,6 +600,8 @@ void embark_assist::survey::high_level_world_survey(embark_assist::defs::geo_dat
else {
results.biome_index[l] = -1;
results.biome[l] = -1;
results.max_temperature[l] = -30000;
results.min_temperature[l] = -30000;
}
}
@ -1109,7 +1193,6 @@ void embark_assist::survey::survey_embark(embark_assist::defs::mid_level_tiles *
if (i == state->local_min_x && k == state->local_min_y) {
elevation = mlt->at(i).at(k).elevation;
}
else if (elevation != mlt->at(i).at(k).elevation) {
site_info->flat = false;

@ -147,13 +147,13 @@ function registerBuilding(args)
end
--animations and connections next:
local gears
local frames
local frameLength
local animate=args.animate
if not auto_gears then
gears=args.gears or {}
frameLength=1
local frames
if animate~=nil then
frameLength=animate.frameLength
if animate.isMechanical then

@ -1932,20 +1932,20 @@ void viewscreen_unitlaborsst::render()
int8_t fg = 15, bg = 0;
int stress_lvl = unit->status.current_soul ? unit->status.current_soul->personality.stress_level : 0;
const vector<UIColor> stress_colors {
13, // 5:1
12, // 4:1
14, // 6:1
2, // 2:0
10 // 2:1 (default)
};
fg = vector_get(stress_colors, Units::getStressCategoryRaw(stress_lvl), stress_colors.back());
// cap at 6 digits
if (stress_lvl < -99999) stress_lvl = -99999;
if (stress_lvl > 999999) stress_lvl = 999999;
string stress = stl_sprintf("%6i", stress_lvl);
if (stress_lvl >= 500000)
fg = 13; // 5:1
else if (stress_lvl >= 250000)
fg = 12; // 4:1
else if (stress_lvl >= 100000)
fg = 14; // 6:1
else if (stress_lvl >= 0)
fg = 2; // 2:0
else
fg = 10; // 2:1
Screen::paintString(Screen::Pen(' ', fg, bg), col_offsets[DISP_COLUMN_STRESS], 4 + row, stress);
Screen::paintTile(

@ -255,6 +255,10 @@ static df::world_region_details *get_details(df::world_data *data, df::coord2d p
bool estimate_underground(color_ostream &out, EmbarkTileLayout &tile, df::world_region_details *details, int x, int y)
{
if (x < 0 || y < 0 || x > 15 || y > 15) {
out.printerr("Invalid embark coordinates: x=%i, y=%i\n", x, y);
return false;
}
// Find actual biome
int bv = clip_range(details->biome[x][y] & 15, 1, 9);
tile.biome_off = biome_delta[bv-1];
@ -519,9 +523,9 @@ static command_result embark_prospector(color_ostream &out, df::viewscreen_choos
biomes[screen->biome_rgn[screen->biome_idx]]++;
}*/
for (int x = screen->location.embark_pos_min.x; x <= screen->location.embark_pos_max.x; x++)
for (int x = screen->location.embark_pos_min.x; x <= 15 && x <= screen->location.embark_pos_max.x; x++)
{
for (int y = screen->location.embark_pos_min.y; y <= screen->location.embark_pos_max.y; y++)
for (int y = screen->location.embark_pos_min.y; y <= 15 && y <= screen->location.embark_pos_max.y; y++)
{
EmbarkTileLayout tile;
if (!estimate_underground(out, tile, cur_details, x, y) ||

@ -5,6 +5,8 @@ option optimize_for = LITE_RUNTIME;
import "RemoteFortressReader.proto";
// Plugin: RemoteFortressReader
enum AdvmodeMenu
{
Default = 0;
@ -102,4 +104,4 @@ message MenuContents
message MiscMoveParams
{
optional MiscMoveType type = 1;
}
}

@ -3,6 +3,8 @@ package ItemdefInstrument;
//Attempts to provide a complete framework for reading everything from a fortress needed for vizualization
option optimize_for = LITE_RUNTIME;
// Plugin: RemoteFortressReader
message InstrumentFlags
{
optional bool indefinite_pitch = 1;
@ -101,4 +103,4 @@ message InstrumentDef
repeated string tuning_parm = 17;
repeated InstrumentRegister registers = 18;
optional string description = 19;
}
}

@ -3,8 +3,46 @@ package RemoteFortressReader;
//Attempts to provide a complete framework for reading everything from a fortress needed for vizualization
option optimize_for = LITE_RUNTIME;
// Plugin: RemoteFortressReader
import "ItemdefInstrument.proto";
// RPC GetMaterialList : EmptyMessage -> MaterialList
// RPC GetGrowthList : EmptyMessage -> MaterialList
// RPC GetBlockList : BlockRequest -> BlockList
// RPC CheckHashes : EmptyMessage -> EmptyMessage
// RPC GetTiletypeList : EmptyMessage -> TiletypeList
// RPC GetPlantList : BlockRequest -> PlantList
// RPC GetUnitList : EmptyMessage -> UnitList
// RPC GetUnitListInside : BlockRequest -> UnitList
// RPC GetViewInfo : EmptyMessage -> ViewInfo
// RPC GetMapInfo : EmptyMessage -> MapInfo
// RPC ResetMapHashes : EmptyMessage -> EmptyMessage
// RPC GetItemList : EmptyMessage -> MaterialList
// RPC GetBuildingDefList : EmptyMessage -> BuildingList
// RPC GetWorldMap : EmptyMessage -> WorldMap
// RPC GetWorldMapNew : EmptyMessage -> WorldMap
// RPC GetRegionMaps : EmptyMessage -> RegionMaps
// RPC GetRegionMapsNew : EmptyMessage -> RegionMaps
// RPC GetCreatureRaws : EmptyMessage -> CreatureRawList
// RPC GetPartialCreatureRaws : ListRequest -> CreatureRawList
// RPC GetWorldMapCenter : EmptyMessage -> WorldMap
// RPC GetPlantRaws : EmptyMessage -> PlantRawList
// RPC GetPartialPlantRaws : ListRequest -> PlantRawList
// RPC CopyScreen : EmptyMessage -> ScreenCapture
// RPC PassKeyboardEvent : KeyboardEvent -> EmptyMessage
// RPC SendDigCommand : DigCommand -> EmptyMessage
// RPC SetPauseState : SingleBool -> EmptyMessage
// RPC GetPauseState : EmptyMessage -> SingleBool
// RPC GetVersionInfo : EmptyMessage -> VersionInfo
// RPC GetReports : EmptyMessage -> Status
// RPC MoveCommand : MoveCommandParams -> EmptyMessage
// RPC JumpCommand : MoveCommandParams -> EmptyMessage
// RPC MenuQuery : EmptyMessage -> MenuContents
// RPC MovementSelectCommand : IntMessage -> EmptyMessage
// RPC MiscMoveCommand : MiscMoveParams -> EmptyMessage
// RPC GetLanguage : EmptyMessage -> Language
//We use shapes, etc, because the actual tiletypes may differ between DF versions.
enum TiletypeShape
{
@ -1069,4 +1107,4 @@ message Wave
{
optional Coord dest = 1;
optional Coord pos = 2;
}
}

@ -3,6 +3,8 @@ package isoworldremote;
//Describes a very basic material structure for the map embark
option optimize_for = LITE_RUNTIME;
// Plugin: isoworldremote
enum BasicMaterial {
AIR = 0;
OTHER = 1;
@ -50,6 +52,7 @@ message EmbarkTile {
optional bool is_valid = 7;
}
// RPC GetEmbarkTile : TileRequest -> EmbarkTile
message TileRequest {
optional int32 want_x = 1;
optional int32 want_y = 2;
@ -59,6 +62,7 @@ message MapRequest {
optional string save_folder = 1;
}
// RPC GetEmbarkInfo : MapRequest -> MapReply
message MapReply {
required bool available = 1;
optional int32 region_x = 2;
@ -69,6 +73,7 @@ message MapReply {
optional int32 current_season = 7;
}
// RPC GetRawNames : MapRequest -> RawNames
message RawNames {
required bool available = 1;
repeated string inorganic = 2;

@ -2,6 +2,8 @@ package dfproto;
option optimize_for = LITE_RUNTIME;
// Plugin: rename
// RPC RenameSquad : RenameSquadIn -> EmptyMessage
message RenameSquadIn {
required int32 squad_id = 1;

@ -462,8 +462,13 @@ sub render_class_vmethods {
push @lines_rb, "def $name(" . join(', ', @argnames) . ')';
indent_rb {
my $args = join('', map { ", $_" } @argargs);
my $call = "DFHack.vmethod_call(self, $voff$args)";
my $ret = $meth->findnodes('child::ret-type')->[0];
my $call;
if (!$ret or $ret->getAttribute('ld:meta') ne 'primitive') {
$call = "DFHack.vmethod_call(self, $voff$args)";
} else {
$call = "DFHack.vmethod_call_mem_return(self, $voff, rv$args)";
}
render_class_vmethod_ret($call, $ret);
};
push @lines_rb, 'end';
@ -534,6 +539,18 @@ sub render_class_vmethod_ret {
};
push @lines_rb, "end._at(ptr) if ptr != 0";
}
elsif ($retmeta eq 'primitive')
{
my $subtype = $ret->getAttribute('ld:subtype');
if ($subtype eq 'stl-string') {
push @lines_rb, "rv = DFHack::StlString.cpp_new";
push @lines_rb, $call;
push @lines_rb, "rv";
} else {
print "Unknown return subtype for $call\n";
push @lines_rb, "nil";
}
}
else
{
print "vmethod unkret $call\n";

@ -994,6 +994,14 @@ module DFHack
vmethod_arg(a3), vmethod_arg(a4), vmethod_arg(a5))
end
def self.vmethod_call_mem_return(obj, voff, r0=0, a0=0, a1=0, a2=0, a3=0, a4=0)
this = obj._memaddr
vt = df.get_vtable_ptr(this)
fptr = df.memory_read_ptr(vt + voff)
vmethod_do_call(vmethod_arg(r0), fptr, this, vmethod_arg(a0), vmethod_arg(a1), vmethod_arg(a2),
vmethod_arg(a3), vmethod_arg(a4))
end
def self.vmethod_arg(arg)
case arg
when nil, false; 0

@ -69,7 +69,7 @@ command_result df_showmood (color_ostream &out, vector <string> & parameters)
out.printerr("Dwarf with strange mood does not have a mood type!\n");
continue;
}
out.print("%s is currently ", DF2CONSOLE(Translation::TranslateName(&unit->name, false)).c_str());
out.print("%s is currently ", DF2CONSOLE(out, Translation::TranslateName(&unit->name, false)).c_str());
switch (unit->mood)
{
case mood_type::Macabre:

@ -1,8 +1,9 @@
#include <vector>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <stack>
#include <string>
#include <cmath>
#include <vector>
#include "Core.h"
#include "Console.h"
@ -38,6 +39,12 @@ void draw_version(int start_x, int start_y) {
OutputString(COLOR_WHITE, x, y, "Git: ");
OutputString(COLOR_WHITE, x, y, DFHACK_GIT_DESCRIPTION);
}
if (strlen(DFHACK_BUILD_ID))
{
x = start_x; y++;
OutputString(COLOR_WHITE, x, y, "Build ID: ");
OutputString(COLOR_WHITE, x, y, DFHACK_BUILD_ID);
}
if (DFHACK_IS_PRERELEASE)
{
x = start_x; y++;
@ -66,7 +73,7 @@ struct options_version_hook : df::viewscreen_optionst {
INTERPOSE_NEXT(render)();
if (!msg_quit && !in_retire_adv && !msg_peasant &&
!in_retire_dwf_abandon_adv && !in_abandon_dwf && !ending_game)
draw_version(2, gps->dimy - 5);
draw_version(2, gps->dimy - 6);
}
};

@ -1 +1 @@
Subproject commit 6849148e8694f5da415cd6920dd9094c352110de
Subproject commit 66b39d888e5f2d4f5c194ece08ba62ac7f110478

@ -0,0 +1,25 @@
function test.getCurViewscreen()
local scr = dfhack.gui.getCurViewscreen()
local scr2 = df.global.gview.view
for i = 1, 100 do
if scr2.child then
scr2 = scr2.child
else
break
end
end
expect.eq(scr, scr2)
end
function test.getViewscreenByType()
local scr = dfhack.gui.getCurViewscreen()
local scr2 = dfhack.gui.getViewscreenByType(scr._type)
expect.eq(scr, scr2)
local bad_type = df.viewscreen_titlest
if scr._type == bad_type then
bad_type = df.viewscreen_optionst
end
local scr_bad = dfhack.gui.getViewscreenByType(bad_type)
expect.eq(scr_bad, nil)
end

@ -0,0 +1,15 @@
local utils = require 'utils'
function test.OrderedTable()
local t = utils.OrderedTable()
local keys = {'a', 'c', 'e', 'd', 'b', 'q', 58, -1.2}
for i = 1, #keys do
t[keys[i]] = i
end
local i = 1
for k, v in pairs(t) do
expect.eq(k, keys[i], 'key order')
expect.eq(v, i, 'correct value')
i = i + 1
end
end

@ -1,10 +1,172 @@
local script = require 'gui.script'
local utils = require 'utils'
local args = {...}
local done_command = args[1]
expect = {}
function expect.true_(value, comment)
return not not value, comment, 'expected true'
end
function expect.false_(value, comment)
return not value, comment, 'expected false'
end
function expect.eq(a, b, comment)
return a == b, comment, ('%s ~= %s'):format(a, b)
end
function expect.ne(a, b, comment)
return a ~= b, comment, ('%s == %s'):format(a, b)
end
function expect.error(func, ...)
local ok, ret = pcall(func, ...)
if ok then
return false, 'no error raised by function call'
else
return true
end
end
function delay(frames)
frames = frames or 1
script.sleep(frames, 'frames')
end
function build_test_env()
local env = {
test = utils.OrderedTable(),
expect = {},
delay = delay,
}
local private = {
checks = 0,
checks_ok = 0,
}
for name, func in pairs(expect) do
env.expect[name] = function(...)
private.checks = private.checks + 1
local ret = {func(...)}
local ok = table.remove(ret, 1)
local msg = ''
for _, part in pairs(ret) do
if part then
msg = msg .. ': ' .. tostring(part)
end
end
msg = msg:sub(3) -- strip leading ': '
if ok then
private.checks_ok = private.checks_ok + 1
else
dfhack.printerr('Check failed! ' .. (msg or '(no message)'))
local info = debug.getinfo(2)
dfhack.printerr((' at %s:%d'):format(info.short_src, info.currentline))
print('')
end
end
end
setmetatable(env, {__index = _G})
return env, private
end
function get_test_files()
local files = {}
for _, entry in ipairs(dfhack.filesystem.listdir_recursive(dfhack.getHackPath() .. 'test')) do
if not entry.isdir and not entry.path:match('main.lua') then
table.insert(files, entry.path)
end
end
table.sort(files)
return files
end
function set_test_stage(stage)
local f = io.open('test_stage.txt', 'w')
f:write(stage)
f:close()
end
print('running tests')
function finish_tests(ok)
if ok then
print('Tests finished')
else
dfhack.printerr('Tests failed!')
end
if done_command then
dfhack.run_command(done_command)
end
end
function main()
local files = get_test_files()
local counts = {
tests = 0,
tests_ok = 0,
checks = 0,
checks_ok = 0,
file_errors = 0,
}
local passed = true
print('Looking for title screen...')
for i = 0, 100 do
local scr = dfhack.gui.getCurViewscreen()
if df.viewscreen_titlest:is_instance(scr) then
print('Found title screen')
break
else
scr:feed_key(df.interface_key.LEAVESCREEN)
delay(10)
end
end
if not df.viewscreen_titlest:is_instance(dfhack.gui.getCurViewscreen()) then
qerror('Could not find title screen')
end
print('Running tests')
for _, file in ipairs(files) do
print('Running file: ' .. file:sub(file:find('test'), -1))
local env, env_private = build_test_env()
local code, err = loadfile(file, 't', env)
if not code then
passed = false
counts.file_errors = counts.file_errors + 1
dfhack.printerr('Failed to load file: ' .. tostring(err))
else
local ok, err = pcall(code)
if not ok then
passed = false
counts.file_errors = counts.file_errors + 1
dfhack.printerr('Error when running file: ' .. tostring(err))
else
for name, test in pairs(env.test) do
env_private.checks = 0
env_private.checks_ok = 0
counts.tests = counts.tests + 1
local ok, err = pcall(test)
if not ok then
dfhack.printerr('test errored: ' .. name .. ': ' .. tostring(err))
passed = false
elseif env_private.checks ~= env_private.checks_ok then
dfhack.printerr('test failed: ' .. name)
passed = false
else
print('test passed: ' .. name)
counts.tests_ok = counts.tests_ok + 1
end
counts.checks = counts.checks + (tonumber(env_private.checks) or 0)
counts.checks_ok = counts.checks_ok + (tonumber(env_private.checks_ok) or 0)
end
end
end
end
print('\nTest summary:')
print(('%d/%d tests passed'):format(counts.tests_ok, counts.tests))
print(('%d/%d checks passed'):format(counts.checks_ok, counts.checks))
print(('%d test files failed to load'):format(counts.file_errors))
set_test_stage(passed and 'done' or 'fail')
finish_tests(passed)
end
set_test_stage('done')
dfhack.run_command('die')
script.start(main)

@ -0,0 +1,109 @@
import glob
import sys
actual = {'': {}}
with open(sys.argv[1]) as f:
plugin_name = ''
for line in f:
line = line.rstrip()
if line.startswith('// Plugin: '):
plugin_name = line.split(' ')[2]
if plugin_name not in actual:
actual[plugin_name] = {}
elif line.startswith('// RPC '):
parts = line.split(' ')
actual[plugin_name][parts[2]] = (parts[4], parts[6])
expected = {'': {}}
for p in glob.iglob('library/proto/*.proto'):
with open(p) as f:
for line in f:
line = line.rstrip()
if line.startswith('// RPC '):
parts = line.split(' ')
expected[''][parts[2]] = (parts[4], parts[6])
for p in glob.iglob('plugins/proto/*.proto'):
plugin_name = ''
with open(p) as f:
for line in f:
line = line.rstrip()
if line.startswith('// Plugin: '):
plugin_name = line.split(' ')[2]
if plugin_name not in expected:
expected[plugin_name] = {}
break
if plugin_name == '':
continue
with open(p) as f:
for line in f:
line = line.rstrip()
if line.startswith('// RPC '):
parts = line.split(' ')
expected[plugin_name][parts[2]] = (parts[4], parts[6])
error_count = 0
for plugin_name in actual:
methods = actual[plugin_name]
if plugin_name not in expected:
print('Missing documentation for plugin proto files: ' + plugin_name)
print('Add the following lines:')
print('// Plugin: ' + plugin_name)
error_count += 1
for m in methods:
io = methods[m]
print('// RPC ' + m + ' : ' + io[0] + ' -> ' + io[1])
error_count += 1
else:
missing = []
wrong = []
for m in methods:
io = methods[m]
if m in expected[plugin_name]:
if expected[plugin_name][m] != io:
wrong.add('// RPC ' + m + ' : ' + io[0] + ' -> ' + io[1])
else:
missing.add('// RPC ' + m + ' : ' + io[0] + ' -> ' + io[1])
if len(missing) > 0:
print('Incomplete documentation for ' + ('core' if plugin_name == '' else 'plugin "' + plugin_name + '"') + ' proto files. Add the following lines:')
for m in missing:
print(m)
error_count += 1
if len(wrong) > 0:
print('Incorrect documentation for ' + ('core' if plugin_name == '' else 'plugin "' + plugin_name + '"') + ' proto files. Replace the following comments:')
for m in wrong:
print(m)
error_count += 1
for plugin_name in expected:
methods = expected[plugin_name]
if plugin_name not in actual:
print('Incorrect documentation for plugin proto files: ' + plugin_name)
print('The following methods are documented, but the plugin does not provide any RPC methods:')
for m in methods:
io = methods[m]
print('// RPC ' + m + ' : ' + io[0] + ' -> ' + io[1])
error_count += 1
else:
missing = []
for m in methods:
io = methods[m]
if m not in actual[plugin_name]:
missing.add('// RPC ' + m + ' : ' + io[0] + ' -> ' + io[1])
if len(missing) > 0:
print('Incorrect documentation for ' + ('core' if plugin_name == '' else 'plugin "' + plugin_name + '"') + ' proto files. Remove the following lines:')
for m in missing:
print(m)
error_count += 1
sys.exit(min(100, error_count))

@ -1,2 +1,4 @@
:lua dfhack.internal.addScriptPath(os.getenv('TRAVIS_BUILD_DIR'))
test/main
devel/dump-rpc dfhack-rpc.txt
:lua dfhack.internal.addScriptPath(dfhack.getHackPath())
test/main die

@ -23,8 +23,13 @@ while True:
print('Done!')
os.remove(test_stage)
sys.exit(0)
elif stage == 'fail':
print('Failed!')
os.remove(test_stage)
sys.exit(1)
if tries > MAX_TRIES:
print('Too many tries - aborting')
sys.exit(1)
os.system(dfhack)
process = subprocess.Popen([dfhack], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
process.communicate()