From 8c68f54f50af134b82ce187d4ac88da84e9e7a2f Mon Sep 17 00:00:00 2001 From: 20k Date: Fri, 20 Jan 2023 18:02:55 +0000 Subject: [PATCH 01/18] update with squad removal support --- library/modules/Buildings.cpp | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 653f8bccf..d010210f5 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -82,6 +82,7 @@ using namespace DFHack; #include "df/map_block.h" #include "df/tile_occupancy.h" #include "df/plotinfost.h" +#include "df/squad.h" #include "df/ui_look_list.h" #include "df/unit.h" #include "df/unit_relationship_type.h" @@ -1348,6 +1349,43 @@ bool Buildings::constructWithFilters(df::building *bld, std::vectorgetType() != building_type::Civzone) + return; + + auto zone = strict_virtual_cast(bld); + + if (zone == nullptr) + return; + + for (df::building_civzonest::T_squad_room_info* room_info : zone->squad_room_info) + { + int32_t squad_id = room_info->squad_id; + + df::squad* squad = df::squad::find(squad_id); + + //if this is null, something has gone just *terribly* wrong + if (squad) + { + for (int i=0; i < squad->rooms.size(); i++) + { + if (squad->rooms[i]->building_id == bld->id) + { + auto room = squad->rooms[i]; + squad->rooms.erase(squad->rooms.begin() + i); + delete room; + i--; + } + } + } + + delete room_info; + } + + zone->squad_room_info.clear(); +} + bool Buildings::deconstruct(df::building *bld) { using df::global::plotinfo; @@ -1388,6 +1426,8 @@ bool Buildings::deconstruct(df::building *bld) remove_building_from_all_zones(bld); remove_zone_from_all_buildings(bld); + delete_civzone_squad_links(bld); + delete bld; if (world->selected_building == bld) From 1dbf01e5d19de1d5979574ca4b8a9ddee431dc9d Mon Sep 17 00:00:00 2001 From: 20k Date: Fri, 20 Jan 2023 18:30:38 +0000 Subject: [PATCH 02/18] Civzone interop style changes --- library/modules/Buildings.cpp | 58 ++++++++++------------------------- 1 file changed, 17 insertions(+), 41 deletions(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index d010210f5..4ec9f9e12 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -169,34 +169,24 @@ void buildings_onUpdate(color_ostream &out) static void building_into_zone_unidir(df::building* bld, df::building_civzonest* zone) { - for (size_t bid = 0; bid < zone->contained_buildings.size(); bid++) + for (auto contained_building : zone->contained_buildings) { - if (zone->contained_buildings[bid] == bld) + if (contained_building == bld) return; } - zone->contained_buildings.push_back(bld); - - std::sort(zone->contained_buildings.begin(), zone->contained_buildings.end(), [](df::building* b1, df::building* b2) - { - return b1->id < b2->id; - }); + insert_into_vector(zone->contained_buildings, &df::building::id, bld); } static void zone_into_building_unidir(df::building* bld, df::building_civzonest* zone) { - for (size_t bid = 0; bid < bld->relations.size(); bid++) + for (auto relation : bld->relations) { - if (bld->relations[bid] == zone) + if (relation == zone) return; } - bld->relations.push_back(zone); - - std::sort(bld->relations.begin(), bld->relations.end(), [](df::building* b1, df::building* b2) - { - return b1->id < b2->id; - }); + insert_into_vector(bld->relations, &df::building::id, (df::building*)zone); } static bool is_suitable_building_for_zoning(df::building* bld) @@ -223,10 +213,8 @@ static void add_building_to_all_zones(df::building* bld) std::vector cv; Buildings::findCivzonesAt(&cv, coord); - for (size_t i=0; i < cv.size(); i++) - { - add_building_to_zone(bld, cv[i]); - } + for (auto zone : cv) + add_building_to_zone(bld, zone); } static void add_zone_to_all_buildings(df::building* zone_as_building) @@ -239,20 +227,16 @@ static void add_zone_to_all_buildings(df::building* zone_as_building) if (zone == nullptr) return; - auto& vec = world->buildings.other[buildings_other_id::IN_PLAY]; - - for (size_t i = 0; i < vec.size(); i++) + for (auto bld : world->buildings.other.IN_PLAY) { - auto against = vec[i]; - - if (zone->z != against->z) + if (zone->z != bld->z) continue; - if (!is_suitable_building_for_zoning(against)) + if (!is_suitable_building_for_zoning(bld)) continue; - int32_t cx = against->centerx; - int32_t cy = against->centery; + int32_t cx = bld->centerx; + int32_t cy = bld->centery; df::coord2d coord(cx, cy); @@ -263,7 +247,7 @@ static void add_zone_to_all_buildings(df::building* zone_as_building) if (!etile || !*etile) continue; - add_building_to_zone(against, zone); + add_building_to_zone(bld, zone); } } } @@ -296,10 +280,8 @@ static void remove_building_from_all_zones(df::building* bld) std::vector cv; Buildings::findCivzonesAt(&cv, coord); - for (size_t i=0; i < cv.size(); i++) - { - remove_building_from_zone(bld, cv[i]); - } + for (auto zone : cv) + remove_building_from_zone(bld, zone); } static void remove_zone_from_all_buildings(df::building* zone_as_building) @@ -312,16 +294,10 @@ static void remove_zone_from_all_buildings(df::building* zone_as_building) if (zone == nullptr) return; - auto& vec = world->buildings.other[buildings_other_id::IN_PLAY]; - //this is a paranoid check and slower than it could be. Zones contain a list of children //good for fixing potentially bad game states when deleting a zone - for (size_t i = 0; i < vec.size(); i++) - { - df::building* bld = vec[i]; - + for (auto bld : world->buildings.other.IN_PLAY) remove_building_from_zone(bld, zone); - } } uint32_t Buildings::getNumBuildings() From 3024c4a0dfdf3a281cf96d6576b33f44b1db23a7 Mon Sep 17 00:00:00 2001 From: 20k Date: Sat, 21 Jan 2023 18:43:45 +0000 Subject: [PATCH 03/18] update to remove ambiguity after structures change --- library/modules/Buildings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 4ec9f9e12..8f0c68eb2 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -186,7 +186,7 @@ static void zone_into_building_unidir(df::building* bld, df::building_civzonest* return; } - insert_into_vector(bld->relations, &df::building::id, (df::building*)zone); + insert_into_vector(bld->relations, &df::building_civzonest::id, zone); } static bool is_suitable_building_for_zoning(df::building* bld) From 9054efd7c8b9ecf487816c1d59de7b92293ee9e9 Mon Sep 17 00:00:00 2001 From: 20k Date: Sat, 21 Jan 2023 19:15:28 +0000 Subject: [PATCH 04/18] Update miscutils to support member pointer to a variable defined in the base when passing in a derived type --- library/include/MiscUtils.h | 8 ++++---- library/modules/Buildings.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/library/include/MiscUtils.h b/library/include/MiscUtils.h index 124987535..c9a5f66d6 100644 --- a/library/include/MiscUtils.h +++ b/library/include/MiscUtils.h @@ -167,8 +167,8 @@ int linear_index(const std::vector &vec, FT CT::*field, FT key) return -1; } -template -int binsearch_index(const std::vector &vec, FT CT::*field, FT key, bool exact = true) +template +int binsearch_index(const std::vector &vec, FT MT::*field, FT key, bool exact = true) { // Returns the index of the value >= the key int min = -1, max = (int)vec.size(); @@ -245,8 +245,8 @@ unsigned insert_into_vector(std::vector &vec, FT key, bool *inserted = NULL) return pos; } -template -unsigned insert_into_vector(std::vector &vec, FT CT::*field, CT *obj, bool *inserted = NULL) +template +unsigned insert_into_vector(std::vector &vec, FT MT::*field, CT *obj, bool *inserted = NULL) { unsigned pos = (unsigned)binsearch_index(vec, field, obj->*field, false); bool to_ins = (pos >= vec.size() || vec[pos] != obj); diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 8f0c68eb2..9d525d96b 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -186,7 +186,7 @@ static void zone_into_building_unidir(df::building* bld, df::building_civzonest* return; } - insert_into_vector(bld->relations, &df::building_civzonest::id, zone); + insert_into_vector(bld->relations, &df::building_civzonest::id, zone); } static bool is_suitable_building_for_zoning(df::building* bld) @@ -1344,7 +1344,7 @@ static void delete_civzone_squad_links(df::building* bld) //if this is null, something has gone just *terribly* wrong if (squad) { - for (int i=0; i < squad->rooms.size(); i++) + for (int i=0; i < (int)squad->rooms.size(); i++) { if (squad->rooms[i]->building_id == bld->id) { From 5a4d61e7fc22adf5c82145912b2860cbdaaa1b64 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 22 Jan 2023 00:43:10 -0800 Subject: [PATCH 05/18] don't close the hotspot menu if the logo is clicked some people click the logo by mistake. this shouldn't close the menu --- plugins/lua/hotkeys.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/lua/hotkeys.lua b/plugins/lua/hotkeys.lua index 2ec38fff6..a6eaf7d81 100644 --- a/plugins/lua/hotkeys.lua +++ b/plugins/lua/hotkeys.lua @@ -32,7 +32,7 @@ function HotspotMenuWidget:overlay_onupdate() end function HotspotMenuWidget:overlay_trigger() - return MenuScreen{hotspot_frame=self.frame}:show() + return MenuScreen{hotspot=self}:show() end local dscreen = dfhack.screen @@ -74,9 +74,9 @@ local ARROW = string.char(26) local MAX_LIST_WIDTH = 45 local MAX_LIST_HEIGHT = 15 -Menu = defclass(MenuScreen, widgets.Panel) +Menu = defclass(Menu, widgets.Panel) Menu.ATTRS{ - hotspot_frame=DEFAULT_NIL, + hotspot=DEFAULT_NIL, } -- get a map from the binding string to a list of hotkey strings that all @@ -136,10 +136,10 @@ end function Menu:init() local hotkeys, bindings = getHotkeys() - local is_inverted = not not self.hotspot_frame.b + local is_inverted = not not self.hotspot.frame.b local choices,list_width = get_choices(hotkeys, bindings, is_inverted) - local list_frame = copyall(self.hotspot_frame) + local list_frame = copyall(self.hotspot.frame) local list_widget_frame = {h=math.min(#choices, MAX_LIST_HEIGHT)} local quickstart_frame = {} list_frame.w = list_width + 2 @@ -248,7 +248,7 @@ function Menu:onInput(keys) self:onSubmit2(list:getSelected()) return true end - if not self:getMouseFramePos() then + if not self:getMouseFramePos() and not self.hotspot:getMousePos() then self.parent_view:dismiss() return true end @@ -297,12 +297,12 @@ end MenuScreen = defclass(MenuScreen, gui.ZScreen) MenuScreen.ATTRS { focus_path='hotkeys/menu', - hotspot_frame=DEFAULT_NIL, + hotspot=DEFAULT_NIL, } function MenuScreen:init() self:addviews{ - Menu{hotspot_frame=self.hotspot_frame}, + Menu{hotspot=self.hotspot}, } end From 0f433c61a170fe8bd1566e15486bb3e533e395c1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 22 Jan 2023 00:44:14 -0800 Subject: [PATCH 06/18] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 94b7d61c9..5e1904e43 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,6 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes ## Misc Improvements +- `hotkeys`: clicking on the DFHack logo no longer closes the popup menu ## Documentation From b84bce719d5dfa01e93b2eb12b8526a4d5a751d5 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 22 Jan 2023 01:07:30 -0800 Subject: [PATCH 07/18] remove unused always_enabled attribute for overlays it was made for gui/pathable, but that became a ZScreen this option made me uncomfortable for overlays. it didn't seem like the right user experience --- docs/dev/overlay-dev-guide.rst | 4 ---- plugins/lua/overlay.lua | 4 +--- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/docs/dev/overlay-dev-guide.rst b/docs/dev/overlay-dev-guide.rst index bce623180..50998530a 100644 --- a/docs/dev/overlay-dev-guide.rst +++ b/docs/dev/overlay-dev-guide.rst @@ -125,10 +125,6 @@ The ``overlay.OverlayWidget`` superclass defines the following class attributes: not annoy the player. Set to 0 to be called at the maximum rate. Be aware that running more often than you really need to will impact game FPS, especially if your widget can run while the game is unpaused. -- ``always_enabled`` (default: ``false``) - Set this to ``true`` if you don't want to let the user disable the widget. - This is useful for widgets that are controlled purely through their - triggers. See `gui/pathable` for an example. Registering a widget with the overlay framework *********************************************** diff --git a/plugins/lua/overlay.lua b/plugins/lua/overlay.lua index cf599fd08..b90c1c651 100644 --- a/plugins/lua/overlay.lua +++ b/plugins/lua/overlay.lua @@ -184,7 +184,6 @@ end local function do_disable(args, quiet) local disable_fn = function(name, db_entry) - if db_entry.widget.always_enabled then return end overlay_config[name].enabled = false if db_entry.widget.hotspot then active_hotspot_widgets[name] = nil @@ -252,7 +251,7 @@ local function load_widget(name, widget_class) local config = overlay_config[name] config.pos = sanitize_pos(config.pos or widget.default_pos) widget.frame = make_frame(config.pos, widget.frame) - if config.enabled or widget.always_enabled then + if config.enabled then do_enable(name, true, true) else config.enabled = false @@ -492,7 +491,6 @@ OverlayWidget.ATTRS{ hotspot=false, -- whether to call overlay_onupdate on all screens viewscreens={}, -- override with associated viewscreen or list of viewscrens overlay_onupdate_max_freq_seconds=5, -- throttle calls to overlay_onupdate - always_enabled=false, -- for overlays that should never be disabled } function OverlayWidget:init() From f0d44342d891bc42e9cd45d66d7732ea1a50d535 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 22 Jan 2023 03:08:36 -0800 Subject: [PATCH 08/18] first attempt at a manager orders overlay --- plugins/CMakeLists.txt | 2 +- plugins/lua/orders.lua | 100 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 plugins/lua/orders.lua diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 3d36b95fc..a05ae6a83 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -134,7 +134,7 @@ dfhack_plugin(misery misery.cpp) #dfhack_plugin(mode mode.cpp) #dfhack_plugin(mousequery mousequery.cpp) dfhack_plugin(nestboxes nestboxes.cpp) -dfhack_plugin(orders orders.cpp LINK_LIBRARIES jsoncpp_static) +dfhack_plugin(orders orders.cpp LINK_LIBRARIES jsoncpp_static lua) dfhack_plugin(overlay overlay.cpp LINK_LIBRARIES lua) dfhack_plugin(pathable pathable.cpp LINK_LIBRARIES lua) #dfhack_plugin(petcapRemover petcapRemover.cpp) diff --git a/plugins/lua/orders.lua b/plugins/lua/orders.lua new file mode 100644 index 000000000..aecd940d6 --- /dev/null +++ b/plugins/lua/orders.lua @@ -0,0 +1,100 @@ +local _ENV = mkmodule('plugins.orders') + +local dialogs = require('gui.dialogs') +local gui = require('gui') +local overlay = require('plugins.overlay') +local widgets = require('gui.widgets') + +-- +-- OrdersOverlay +-- + +local function is_orders_panel_visible() + local info = df.global.game.main_interface.info + return info.open and info.current_mode == df.info_interface_mode_type.WORK_ORDERS +end + +local function do_sort() + dfhack.run_command('orders', 'sort') +end + +local function do_clear() + dialogs.showYesNoPrompt('Clear manager orders?', + 'Are you sure you want to clear the manager orders?', nil, + function() dfhack.run_command('orders', 'clear') end) +end + +local function do_import() + local output = dfhack.run_command_silent('orders', 'list') + dialogs.ListBox{ + frame_title='Import Manager Orders', + with_filter=true, + choices=output:split('\n'), + on_select=function(idx, choice) + dfhack.run_command('orders', 'import', choice.text) + end, + }:show() +end + +local function do_export() + dialogs.InputBox{ + frame_title='Export Manager Orders', + on_input=function(text) + dfhack.run_command('orders', 'export', text) + end + }:show() +end + +OrdersOverlay = defclass(OrdersOverlay, overlay.OverlayWidget) +OrdersOverlay.ATTRS{ + default_pos={x=61,y=-6}, + viewscreens='dwarfmode', + frame={w=30, h=4}, + frame_style=gui.GREY_LINE_FRAME, + frame_background=gui.CLEAR_PEN, +} + +function OrdersOverlay:init() + self:addviews{ + widgets.HotkeyLabel{ + frame={t=0, l=0}, + label='import', + key='CUSTOM_CTRL_I', + on_activate=do_import, + }, + widgets.HotkeyLabel{ + frame={t=1, l=0}, + label='export', + key='CUSTOM_CTRL_E', + on_activate=do_export, + }, + widgets.HotkeyLabel{ + frame={t=0, l=15}, + label='sort', + key='CUSTOM_CTRL_O', + on_activate=do_sort, + }, + widgets.HotkeyLabel{ + frame={t=1, l=15}, + label='clear', + key='CUSTOM_CTRL_C', + on_activate=do_clear, + }, + } +end + +function OrdersOverlay:render(dc) + if not is_orders_panel_visible() then return false end + OrdersOverlay.super.render(self, dc) +end + +function OrdersOverlay:onInput(keys) + if not is_orders_panel_visible() then return false end + OrdersOverlay.super.onInput(self, keys) +end + +OVERLAY_WIDGETS = { + overlay=OrdersOverlay, +} + +return _ENV From c7ca9d0d7b7fd0c207467022cdaad106acf85ae7 Mon Sep 17 00:00:00 2001 From: 20k Date: Sun, 22 Jan 2023 13:56:33 +0000 Subject: [PATCH 09/18] reverse squad iteration delete --- library/modules/Buildings.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index 9d525d96b..a17c79de2 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -1344,14 +1344,13 @@ static void delete_civzone_squad_links(df::building* bld) //if this is null, something has gone just *terribly* wrong if (squad) { - for (int i=0; i < (int)squad->rooms.size(); i++) + for (int i=(int)squad->rooms.size() - 1; i >= 0; i--) { if (squad->rooms[i]->building_id == bld->id) { auto room = squad->rooms[i]; squad->rooms.erase(squad->rooms.begin() + i); delete room; - i--; } } } From b15fcc93d2247e144de6c51979d7bb9a92d7c2b3 Mon Sep 17 00:00:00 2001 From: 20k Date: Sun, 22 Jan 2023 14:53:56 +0000 Subject: [PATCH 10/18] remove assignments on zone destroy --- library/modules/Buildings.cpp | 46 ++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index a17c79de2..c4615c61a 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -1325,16 +1325,8 @@ bool Buildings::constructWithFilters(df::building *bld, std::vectorgetType() != building_type::Civzone) - return; - - auto zone = strict_virtual_cast(bld); - - if (zone == nullptr) - return; - for (df::building_civzonest::T_squad_room_info* room_info : zone->squad_room_info) { int32_t squad_id = room_info->squad_id; @@ -1346,7 +1338,7 @@ static void delete_civzone_squad_links(df::building* bld) { for (int i=(int)squad->rooms.size() - 1; i >= 0; i--) { - if (squad->rooms[i]->building_id == bld->id) + if (squad->rooms[i]->building_id == zone->id) { auto room = squad->rooms[i]; squad->rooms.erase(squad->rooms.begin() + i); @@ -1361,6 +1353,31 @@ static void delete_civzone_squad_links(df::building* bld) zone->squad_room_info.clear(); } +//unit owned_building pointers are known-bad as of 50.05 and dangle on zone delete +//do not use anything that touches anything other than the pointer value +//this means also that if dwarf fortress reuses a memory allocation, we will end up with duplicates +//this vector is also not sorted by id +static void delete_assigned_unit_links(df::building_civzonest* zone) +{ + if (zone->assigned_unit_id == -1) + return; + + df::unit* unit = zone->assigned_unit; + + for (int i=(int)unit->owned_buildings.size() - 1; i >= 0; i--) + { + if (unit->owned_buildings[i] == zone) + unit->owned_buildings.erase(unit->owned_buildings.begin() + i); + } +} + +static void on_civzone_delete(df::building_civzonest* civzone) +{ + remove_zone_from_all_buildings(civzone); + delete_civzone_squad_links(civzone); + delete_assigned_unit_links(civzone); +} + bool Buildings::deconstruct(df::building *bld) { using df::global::plotinfo; @@ -1399,9 +1416,14 @@ bool Buildings::deconstruct(df::building *bld) bld->uncategorize(); remove_building_from_all_zones(bld); - remove_zone_from_all_buildings(bld); - delete_civzone_squad_links(bld); + if (bld->getType() == df::building_type::Civzone) + { + auto zone = strict_virtual_cast(bld); + + if (zone) + on_civzone_delete(zone); + } delete bld; From aaaf2d9f22d577510f145508886a50ac2992a336 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Sat, 14 Jan 2023 22:14:06 -0800 Subject: [PATCH 11/18] Add cross-compile script That will simply generate the win64 artifacts for those of us that run DF under Proton on Steam for Linux. --- build/.gitignore | 1 + build/build-win64-from-linux.sh | 39 +++++++++++++++++++++ docs/dev/compile/Compile.rst | 60 +++++++++++++++++++++++++++++++-- 3 files changed, 98 insertions(+), 2 deletions(-) create mode 100755 build/build-win64-from-linux.sh diff --git a/build/.gitignore b/build/.gitignore index 3c85ab4df..f25d10ca7 100644 --- a/build/.gitignore +++ b/build/.gitignore @@ -6,3 +6,4 @@ DF_PATH.txt _CPack_Packages *.tar.* .cmake +win64-cross diff --git a/build/build-win64-from-linux.sh b/build/build-win64-from-linux.sh new file mode 100755 index 000000000..74c307c89 --- /dev/null +++ b/build/build-win64-from-linux.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env sh + +# Number of jobs == core count +jobs=$(grep -c ^processor /proc/cpuinfo) + +# Calculate absolute paths for docker to do mounts +srcdir=$(realpath "$(dirname "$(readlink -f "$0")")"/..) + +cd "$srcdir"/build + +mkdir -p win64-cross +mkdir -p win64-cross/output + +# Assumes you built a container image called dfhack-build-msvc from +# https://github.com/BenLubar/build-env/tree/master/msvc, see +# docs/dev/compile/Compile.rst. +# +# NOTE: win64-cross is mounted in /src/build due to the hardcoded `cmake ..` in +# the Dockerfile +# +# TODO: make this work for rootless docker, i.e. remove the sudo for those that +# don't normally need to use sudo to run docker. +if ! sudo docker run --rm -it -v "$srcdir":/src -v "$srcdir/build/win64-cross/":/src/build \ + --user buildmaster \ + --name dfhack-win \ + dfhack-build-msvc bash -c "cd /src/build && dfhack-configure windows 64 Release -DCMAKE_INSTALL_PREFIX=/src/build/output && dfhack-make -j$jobs install" \ + ; then + echo + echo "Build failed" + exit 1 +else + echo + echo "Windows artifacts are at win64-cross/output. Copy or symlink them to" + echo "your steam DF directory to install dfhack (and optionally delete the" + echo "hack/ directory already present)" + echo + echo "Typically this can be done like this:" + echo " cp -r win64-cross/output/* \"$HOME/.local/share/Steam/steamapps/common/Dwarf Fortress\"" +fi diff --git a/docs/dev/compile/Compile.rst b/docs/dev/compile/Compile.rst index 032eb7524..8e38139fa 100644 --- a/docs/dev/compile/Compile.rst +++ b/docs/dev/compile/Compile.rst @@ -284,8 +284,8 @@ addition to the normal ``CC`` and ``CXX`` flags above:: export PATH=/usr/local/bin:$PATH -Windows cross compiling from Linux -================================== +Windows cross compiling from Linux (running DF inside docker) +============================================================= .. highlight:: bash @@ -368,6 +368,62 @@ host when you want to reattach:: If you edit code and need to rebuild, run ``dfhack-make`` and then ``ninja install``. That will handle all the wineserver management for you. +Cross-compiling windows files for running DF in Steam for Linux +=============================================================== + +.. highlight:: bash + +If you wish, you can use Docker to build just the Windows files to copy to your +existing Steam installation on Linux. + +.. contents:: + :local: + :depth: 1 + +Step 1: Build the MSVC builder image +------------------------------------ + +It'll be called ``dfhack-build-msvc:latest`` after it's done building:: + + git clone https://github.com/BenLubar/build-env.git + cd build-env/msvc + docker build --build-arg BUILDER_UID=$(id -u) -t dfhack-build-msvc . + +The ``BUILDER_UID`` argument is used to make sure the build user can access your +source code directory inside the container. + +The docker build takes a while, but only needs to be done once, unless the build +environment changes. + +Step 2: Get dfhack, and run the build script +-------------------------------------------- + +Check out ``dfhack`` into another directory, and run the build script:: + + git clone https://github.com/DFHack/dfhack.git + cd dfhack + git submodule update --init --recursive + cd build + ./build-win64-from-linux.sh + +The last step may ask you for your ``sudo`` password -- if you are able to run +``docker`` without ``sudo`` normally, you can remove the ``sudo`` from the build +script. + +The script will mount your host's ``dfhack`` directory to docker, use it to +build the artifacts in ``build/win64-cross``, and put all the files needed to +install in ``build/win64-cross/output``. + +Step 3: install dfhack to your Steam DF install +----------------------------------------------- +As the script will tell you, you can then copy the files into your DF folder:: + + # Optional -- remove the old hack directory in case we leave files behind + rm ~/.local/share/Steam/steamapps/common/"Dwarf Fortress"/hack + cp -r win64-cross/output/* ~/.local/share/Steam/steamapps/common/"Dwarf Fortress"/ + +Afterward, just run DF as normal. + .. _note-offline-builds: Building DFHack Offline From 5a20f775bf13baac435a739defd99d4601638d01 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Sun, 15 Jan 2023 20:34:52 -0800 Subject: [PATCH 12/18] Set UIDs properly on run; fix sudo use This depends on a new entrypoint put inside the docker image, that will read BUILDER_UID and fix up the users and files. This also removes "sudo" and allows the entire script to be run with sudo without messing with the build file permissions. --- build/build-win64-from-linux.sh | 29 +++++++++++++++++++++++------ docs/dev/compile/Compile.rst | 14 ++++++-------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/build/build-win64-from-linux.sh b/build/build-win64-from-linux.sh index 74c307c89..7f23d78c3 100755 --- a/build/build-win64-from-linux.sh +++ b/build/build-win64-from-linux.sh @@ -1,5 +1,6 @@ -#!/usr/bin/env sh +#!/bin/bash +set -e # Number of jobs == core count jobs=$(grep -c ^processor /proc/cpuinfo) @@ -8,20 +9,36 @@ srcdir=$(realpath "$(dirname "$(readlink -f "$0")")"/..) cd "$srcdir"/build +builder_uid=$(id -u) + mkdir -p win64-cross mkdir -p win64-cross/output +# Check for sudo; we want to use the real user +if [[ $(id -u) -eq 0 ]]; then + if [[ -z "$SUDO_UID" || "$SUDO_UID" -eq 0 ]]; then + echo "Please don't run this script directly as root, use sudo instead:" + echo + echo " sudo $0" + # This is because we can't change the buildmaster UID in the container to 0 -- + # that's already taken by root. + exit 1 + fi + + # If this was run using sudo, let's make sure the directories are owned by the + # real user (and set the BUILDER_UID to it) + builder_uid=$SUDO_UID + chown $builder_uid win64-cross win64-cross/output +fi + # Assumes you built a container image called dfhack-build-msvc from # https://github.com/BenLubar/build-env/tree/master/msvc, see # docs/dev/compile/Compile.rst. # # NOTE: win64-cross is mounted in /src/build due to the hardcoded `cmake ..` in # the Dockerfile -# -# TODO: make this work for rootless docker, i.e. remove the sudo for those that -# don't normally need to use sudo to run docker. -if ! sudo docker run --rm -it -v "$srcdir":/src -v "$srcdir/build/win64-cross/":/src/build \ - --user buildmaster \ +if ! docker run --rm -it -v "$srcdir":/src -v "$srcdir/build/win64-cross/":/src/build \ + -e BUILDER_UID=$builder_uid \ --name dfhack-win \ dfhack-build-msvc bash -c "cd /src/build && dfhack-configure windows 64 Release -DCMAKE_INSTALL_PREFIX=/src/build/output && dfhack-make -j$jobs install" \ ; then diff --git a/docs/dev/compile/Compile.rst b/docs/dev/compile/Compile.rst index 8e38139fa..f91c43c07 100644 --- a/docs/dev/compile/Compile.rst +++ b/docs/dev/compile/Compile.rst @@ -387,10 +387,7 @@ It'll be called ``dfhack-build-msvc:latest`` after it's done building:: git clone https://github.com/BenLubar/build-env.git cd build-env/msvc - docker build --build-arg BUILDER_UID=$(id -u) -t dfhack-build-msvc . - -The ``BUILDER_UID`` argument is used to make sure the build user can access your -source code directory inside the container. + docker build -t dfhack-build-msvc . The docker build takes a while, but only needs to be done once, unless the build environment changes. @@ -406,14 +403,15 @@ Check out ``dfhack`` into another directory, and run the build script:: cd build ./build-win64-from-linux.sh -The last step may ask you for your ``sudo`` password -- if you are able to run -``docker`` without ``sudo`` normally, you can remove the ``sudo`` from the build -script. - The script will mount your host's ``dfhack`` directory to docker, use it to build the artifacts in ``build/win64-cross``, and put all the files needed to install in ``build/win64-cross/output``. +If you need to run ``docker`` using ``sudo``, run the script using ``sudo`` +rather than directly:: + + sudo ./build-win64-from-linux.sh + Step 3: install dfhack to your Steam DF install ----------------------------------------------- As the script will tell you, you can then copy the files into your DF folder:: From 839636a56339d449d6a8ed0a80ccafbef1207397 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Fri, 20 Jan 2023 17:21:33 -0800 Subject: [PATCH 13/18] Change $HOME to ~ As $HOME shows /root under sudo --- build/build-win64-from-linux.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/build-win64-from-linux.sh b/build/build-win64-from-linux.sh index 7f23d78c3..713ae90e4 100755 --- a/build/build-win64-from-linux.sh +++ b/build/build-win64-from-linux.sh @@ -52,5 +52,5 @@ else echo "hack/ directory already present)" echo echo "Typically this can be done like this:" - echo " cp -r win64-cross/output/* \"$HOME/.local/share/Steam/steamapps/common/Dwarf Fortress\"" + echo " cp -r win64-cross/output/* ~/.local/share/Steam/steamapps/common/\"Dwarf Fortress\"" fi From ce12e2d90633af18166a31543447884df9ebfd36 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Sat, 21 Jan 2023 09:38:46 -0800 Subject: [PATCH 14/18] Also build docs. The in-game docs aren't built otherwise, so you won't get help for the various commands if this isn't set. --- build/build-win64-from-linux.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/build-win64-from-linux.sh b/build/build-win64-from-linux.sh index 713ae90e4..08ea88793 100755 --- a/build/build-win64-from-linux.sh +++ b/build/build-win64-from-linux.sh @@ -40,7 +40,7 @@ fi if ! docker run --rm -it -v "$srcdir":/src -v "$srcdir/build/win64-cross/":/src/build \ -e BUILDER_UID=$builder_uid \ --name dfhack-win \ - dfhack-build-msvc bash -c "cd /src/build && dfhack-configure windows 64 Release -DCMAKE_INSTALL_PREFIX=/src/build/output && dfhack-make -j$jobs install" \ + dfhack-build-msvc bash -c "cd /src/build && dfhack-configure windows 64 Release -DCMAKE_INSTALL_PREFIX=/src/build/output cmake .. -DBUILD_DOCS=1 && dfhack-make -j$jobs install" \ ; then echo echo "Build failed" From fb1ce3fed7062ce6d9d0637f5dc2d461e6b72345 Mon Sep 17 00:00:00 2001 From: Kelvie Wong Date: Sun, 22 Jan 2023 11:44:35 -0800 Subject: [PATCH 15/18] Add changelog. --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 94b7d61c9..8d5665c85 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,6 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes ## Misc Improvements +- A new cross-compile build script was added for building the Windows files from a Linux Docker builder (see the Compile instructions in the docs) ## Documentation From 4559168005ccf7c496dbf8a5e982b802be551c64 Mon Sep 17 00:00:00 2001 From: 20k Date: Sun, 22 Jan 2023 22:56:51 +0000 Subject: [PATCH 16/18] update for multiple ownership --- library/modules/Buildings.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/library/modules/Buildings.cpp b/library/modules/Buildings.cpp index c4615c61a..5077b5b52 100644 --- a/library/modules/Buildings.cpp +++ b/library/modules/Buildings.cpp @@ -1357,17 +1357,20 @@ static void delete_civzone_squad_links(df::building_civzonest* zone) //do not use anything that touches anything other than the pointer value //this means also that if dwarf fortress reuses a memory allocation, we will end up with duplicates //this vector is also not sorted by id +//it also turns out that multiple units eg (solely?) spouses can point to one room static void delete_assigned_unit_links(df::building_civzonest* zone) { - if (zone->assigned_unit_id == -1) - return; - - df::unit* unit = zone->assigned_unit; + //not clear if this is always true + /*if (zone->assigned_unit_id == -1) + return;*/ - for (int i=(int)unit->owned_buildings.size() - 1; i >= 0; i--) + for (df::unit* unit : world->units.active) { - if (unit->owned_buildings[i] == zone) - unit->owned_buildings.erase(unit->owned_buildings.begin() + i); + for (int i=(int)unit->owned_buildings.size() - 1; i >= 0; i--) + { + if (unit->owned_buildings[i] == zone) + unit->owned_buildings.erase(unit->owned_buildings.begin() + i); + } } } From b4c49f36e11fa98f75ef6cf3afc55bf0d6975eef Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 22 Jan 2023 15:39:05 -0800 Subject: [PATCH 17/18] document orders overlay --- docs/plugins/orders.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/plugins/orders.rst b/docs/plugins/orders.rst index 8beb246e4..5dfe23758 100644 --- a/docs/plugins/orders.rst +++ b/docs/plugins/orders.rst @@ -40,6 +40,17 @@ Examples Import manager orders from the library that keep your fort stocked with basic essentials. +Overlay +------- + +Orders plugin functionality is directly available when the manager orders screen +is open via an `overlay` widget. There are hotkeys assigned to export, import, +sort, and clear. You can also click on the hotkey hints as if they were buttons. +Clearing will ask for confirmation before acting. + +If you want to change where the hotkey hints appear, you can move them via +`gui/overlay`. + The orders library ------------------ From 0c5514ff2d0aa2079468965b19a2b6e968eaffbd Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 22 Jan 2023 15:39:12 -0800 Subject: [PATCH 18/18] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index 94b7d61c9..4a484f692 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -38,6 +38,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes ## Misc Improvements +- `orders`: orders plugin functionality is now offered via an overlay widget when the manager orders screen is open ## Documentation