From f3e357d16195f063a7fbd2a050d13e89e1f2397c Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 30 Apr 2013 19:42:51 +0400 Subject: [PATCH 01/34] Fix detection of GET_MATERIAL_FROM_REAGENT results in workflow. Dereference pointer to strings. Also just return undefined material instead of skipping the product if it still fails. --- plugins/lua/workflow.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/lua/workflow.lua b/plugins/lua/workflow.lua index 563e83297..4326aa5d0 100644 --- a/plugins/lua/workflow.lua +++ b/plugins/lua/workflow.lua @@ -73,11 +73,10 @@ function job_outputs.CustomReaction(callback, job) if mat then local rp = mat.material.reaction_product - local idx = utils.linear_index(rp.id, p_code) - if not idx then - goto continue + local idx = utils.linear_index(rp.id, p_code, 'value') + if idx then + mat_type, mat_index = rp.material.mat_type[idx], rp.material.mat_index[idx] end - mat_type, mat_index = rp.material.mat_type[idx], rp.material.mat_index[idx] else if p_code == "SOAP_MAT" then mat_mask = { soap = true } From 578b178ac06f945df04aa97d746ff13ab381daf3 Mon Sep 17 00:00:00 2001 From: jj Date: Tue, 30 Apr 2013 18:31:28 +0200 Subject: [PATCH 02/34] scripts/startdwarf --- NEWS | 1 + scripts/startdwarf.rb | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 scripts/startdwarf.rb diff --git a/NEWS b/NEWS index 5100c0115..2e821df25 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ DFHack future - locate_ore: scan the map for unmined ore veins - multicmd: run a sequence of dfhack commands, separated by ';' - autobutcher: A GUI front-end for the autobutcher plugin. + - startdwarf: change the number of dwarves for a new embark Misc improvements: - exterminate: renamed from slayrace, add help message, add butcher mode - autoSyndrome: disable by default diff --git a/scripts/startdwarf.rb b/scripts/startdwarf.rb new file mode 100644 index 000000000..b47657c05 --- /dev/null +++ b/scripts/startdwarf.rb @@ -0,0 +1,9 @@ +# patch start dwarf count + +nr = $script_args[0].to_i + +raise 'too low' if nr < 7 + +addr = df.get_global_address('start_dwarf_count') +df.memory_patch(addr, [nr].pack('L')) + From dd739313b1e5c5f1ef13cb3559c4774b25cd0a1f Mon Sep 17 00:00:00 2001 From: jj Date: Tue, 30 Apr 2013 18:31:52 +0200 Subject: [PATCH 03/34] ruby: tweak item_isfree --- plugins/ruby/item.rb | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/plugins/ruby/item.rb b/plugins/ruby/item.rb index 0d65a707b..394e545c6 100644 --- a/plugins/ruby/item.rb +++ b/plugins/ruby/item.rb @@ -53,15 +53,26 @@ module DFHack end # check item flags to see if it is suitable for use as a job input material - def item_isfree(i) + def item_isfree(i, check_empty=true) !i.flags.trader and !i.flags.in_job and - (!i.flags.in_inventory or i.general_refs.grep(GeneralRefContainedInItemst).first) and + !i.flags.construction and !i.flags.removed and - !i.flags.in_building and + !i.flags.forbid and + !i.flags.dump and !i.flags.owned and - !i.flags.forbid + !i.flags.in_chest and # used as hospital supply ? + (!i.flags.container or not check_empty or + !i.general_refs.find { |ir| ir.kind_of?(DFHack::GeneralRefContainsItemst) }) and + (!i.flags.in_inventory or + (!i.general_refs.find { |ir| ir.kind_of?(DFHack::GeneralRefUnitHolderst) and # allow hauled items TODO check if holder is a thief + ir.unit_tg.inventory.find { |ii| ii.item == i and ii.mode != :Hauled } } and + !i.general_refs.find { |ir| ir.kind_of?(DFHack::GeneralRefContainedInItemst) and + !item_isfree(ir.item_tg, false) })) and + (!i.flags.in_building or + !i.general_refs.find { |ir| ir.kind_of?(DFHack::GeneralRefBuildingHolderst) and + ir.building_tg.contained_items.find { |bi| bi.use_mode == 2 and bi.item == i } }) and + (!i.flags.on_ground or !df.map_tile_at(i).designation.hidden) # i.flags.unk11? end - end end From c496b9f76a69f551cd0640d318541bafb72e9169 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 1 May 2013 14:43:00 +0400 Subject: [PATCH 04/34] Translate make-dt.pl from df-structures into lua to avoid lisp dependency. --- scripts/devel/export-dt-ini.lua | 321 ++++++++++++++++++++++++++++++++ scripts/devel/inject-raws.lua | 5 + 2 files changed, 326 insertions(+) create mode 100644 scripts/devel/export-dt-ini.lua diff --git a/scripts/devel/export-dt-ini.lua b/scripts/devel/export-dt-ini.lua new file mode 100644 index 000000000..9fb6285fe --- /dev/null +++ b/scripts/devel/export-dt-ini.lua @@ -0,0 +1,321 @@ +-- Exports an ini file for Dwarf Therapist. + +local utils = require 'utils' +local ms = require 'memscan' + +-- Utility functions + +local globals = df.global +local global_addr = dfhack.internal.getAddress +local os_type = dfhack.getOSType() + +local vbias = 0 +if os_type == 'windows' then vbias = -4 end + +local lines = {} +local complete = true + +local function header(name) + table.insert(lines, '') + table.insert(lines, '['..name..']') +end + +local function value(name,addr) + local line + + if not addr then + complete = false + line = name..'=0x0' + elseif addr < 0x10000 then + line = string.format('%s=0x%04x',name,addr) + else + line = string.format('%s=0x%08x',name,addr) + end + + table.insert(lines, line) +end +local function address(name,bias,base,field,...) + local addr + + if base == globals then + addr = global_addr(field) + if addr and select('#',...) > 0 then + _,addr = df.sizeof(ms.field_ref(base,field,...)) + end + elseif base._kind == 'class-type' then + -- field_offset crashes with classes due to vtable problems, + -- so we have to create a real temporary object here. + local obj = df.new(base) + if obj then + local _,a1 = df.sizeof(obj) + local _,a2 = df.sizeof(ms.field_ref(obj,field,...)) + addr = a2-a1 + obj:delete() + end + else + addr = ms.field_offset(base,field,...) + end + + if addr then + addr = addr + bias + end + + value(name, addr) +end + +local function offset(name,base,...) + address(name,0,base,...) +end +local function vector(name,base,...) + address(name,vbias,base,...) +end + +-- List of actual values + +header('addresses') +vector('translation_vector',globals,'world','raws','language','translations') +vector('language_vector',globals,'world','raws','language','words') +vector('creature_vector',globals,'world','units','all') +vector('active_creature_vector',globals,'world','units','active') +offset('dwarf_race_index',globals,'ui','race_id') +vector('squad_vector',globals,'world','squads','all') +offset('current_year',globals,'cur_year') +offset('cur_year_tick',globals,'cur_year_tick') +offset('dwarf_civ_index',globals,'ui','civ_id') +vector('races_vector',globals,'world','raws','creatures','all') +vector('reactions_vector',globals,'world','raws','reactions') +vector('historical_figures',globals,'world','history','figures') +vector('fake_identities',globals,'world','assumed_identities','all') +vector('historical_figures_vector',globals,'world','history','figures') +vector('fake_identities_vector',globals,'world','assumed_identities','all') +vector('fortress_entity',globals,'ui','main','fortress_entity') +vector('historical_entities_vector',globals,'world','entities','all') +vector('weapons_vector',globals,'world','raws','itemdefs','weapons') +vector('trap_vector',globals,'world','raws','itemdefs','trapcomps') +vector('toy_vector',globals,'world','raws','itemdefs','toys') +vector('tool_vector',globals,'world','raws','itemdefs','tools') +vector('instrument_vector',globals,'world','raws','itemdefs','instruments') +vector('armor_vector',globals,'world','raws','itemdefs','armor') +vector('ammo_vector',globals,'world','raws','itemdefs','ammo') +vector('siegeammo_vector',globals,'world','raws','itemdefs','siege_ammo') +vector('glove_vector',globals,'world','raws','itemdefs','gloves') +vector('shoe_vector',globals,'world','raws','itemdefs','shoes') +vector('shield_vector',globals,'world','raws','itemdefs','shields') +vector('helm_vector',globals,'world','raws','itemdefs','helms') +vector('pant_vector',globals,'world','raws','itemdefs','pants') +vector('food_vector',globals,'world','raws','itemdefs','food') +vector('colors_vector',globals,'world','raws','language','colors') +vector('shapes_vector',globals,'world','raws','language','shapes') +offset('base_materials',globals,'world','raws','mat_table','builtin') +vector('inorganics_vector',globals,'world','raws','inorganics') +vector('plants_vector',globals,'world','raws','plants','all') + +header('offsets') +offset('word_table',df.language_translation,'words') +value('string_buffer_offset', 0x0000) + +header('word_offsets') +offset('base',df.language_word,'word') +offset('noun_singular',df.language_word,'forms','Noun') +offset('noun_plural',df.language_word,'forms','NounPlural') +offset('adjective',df.language_word,'forms','Adjective') +offset('verb',df.language_word,'forms','Verb') +offset('present_simple_verb',df.language_word,'forms','Verb3rdPerson') +offset('past_simple_verb',df.language_word,'forms','VerbPast') +offset('past_participle_verb',df.language_word,'forms','VerbPassive') +offset('present_participle_verb',df.language_word,'forms','VerbGerund') +offset('words',df.language_name,'words') +offset('word_type',df.language_name,'parts_of_speech') +offset('language_id',df.language_name,'language') + +header('race_offsets') +offset('name_singular',df.creature_raw,'name',0) +offset('name_plural',df.creature_raw,'name',1) +offset('adjective',df.creature_raw,'name',2) +offset('baby_name_singular',df.creature_raw,'general_baby_name',0) +offset('baby_name_plural',df.creature_raw,'general_baby_name',1) +offset('child_name_singular',df.creature_raw,'general_child_name',0) +offset('child_name_plural',df.creature_raw,'general_child_name',1) +vector('pref_string_vector',df.creature_raw,'prefstring') +vector('castes_vector',df.creature_raw,'caste') +vector('pop_ratio_vector',df.creature_raw,'pop_ratio') +vector('materials_vector',df.creature_raw,'material') + +header('caste_offsets') +offset('caste_name',df.caste_raw,'caste_name') +offset('caste_descr',df.caste_raw,'description') +offset('caste_phys_att_ranges',df.caste_raw,'attributes','phys_att_range') +offset('caste_ment_att_ranges',df.caste_raw,'attributes','ment_att_range') +offset('adult_size',df.caste_raw,'misc','adult_size') + +header('hist_entity_offsets') +vector('squads',df.historical_entity,'squads') +vector('positions',df.historical_entity,'positions','own') +vector('assignments',df.historical_entity,'positions','assignments') +offset('assign_hist_id',df.entity_position_assignment,'histfig') +offset('assign_position_id',df.entity_position_assignment,'position_id') +offset('position_id',df.entity_position,'id') +offset('position_name',df.entity_position,'name') +offset('position_female_name',df.entity_position,'name_female') +offset('position_male_name',df.entity_position,'name_male') + +header('hist_figure_offsets') +offset('hist_race',df.historical_figure,'race') +offset('hist_name',df.historical_figure,'name') +offset('id',df.historical_figure,'id') +offset('hist_fig_info',df.historical_figure,'info') +offset('reputation',df.historical_figure_info,'reputation') +offset('current_ident',df.historical_figure_info.T_reputation,'cur_identity') +offset('fake_name',df.assumed_identity,'name') +offset('fake_birth_year',df.assumed_identity,'birth_year') +offset('fake_birth_time',df.assumed_identity,'birth_second') + +header('weapon_offsets') +offset('name_plural',df.itemdef_weaponst,'name_plural') +offset('single_size',df.itemdef_weaponst,'two_handed') +offset('multi_size',df.itemdef_weaponst,'minimum_size') + +header('material_offsets') +offset('solid_name',df.material_common,'state_name','Solid') +offset('liquid_name',df.material_common,'state_name','Liquid') +offset('gas_name',df.material_common,'state_name','Gas') +offset('powder_name',df.material_common,'state_name','Powder') +offset('paste_name',df.material_common,'state_name','Paste') +offset('pressed_name',df.material_common,'state_name','Pressed') +offset('inorganic_materials_vector',df.inorganic_raw,'material') + +header('plant_offsets') +offset('name',df.plant_raw,'name') +offset('name_plural',df.plant_raw,'name_plural') +offset('name_leaf_plural',df.plant_raw,'leaves_plural') +offset('name_seed_plural',df.plant_raw,'seed_plural') +vector('materials_vector',df.plant_raw,'material') + +header('item_offsets') +offset('name_plural',df.itemdef_armorst,'name_plural') +offset('adjective',df.itemdef_armorst,'name_preplural') +offset('mat_name',df.itemdef_armorst,'material_placeholder') + +header('descriptor_offsets') +offset('color_name',df.descriptor_color,'name') +offset('shape_name_plural',df.descriptor_shape,'name_plural') + +header('dwarf_offsets') +offset('first_name',df.unit,'name','first_name') +offset('nick_name',df.unit,'name','nickname') +offset('last_name',df.unit,'name','words') +offset('custom_profession',df.unit,'custom_profession') +offset('profession',df.unit,'profession') +offset('race',df.unit,'race') +offset('flags1',df.unit,'flags1') +offset('flags2',df.unit,'flags2') +offset('flags3',df.unit,'flags3') +offset('caste',df.unit,'caste') +offset('sex',df.unit,'sex') +offset('id',df.unit,'id') +offset('animal_type',df.unit,'training_level') +offset('civ',df.unit,'civ_id') +vector('specific_refs',df.unit,'specific_refs') +offset('squad_id',df.unit,'military','squad_id') +offset('squad_position',df.unit,'military','squad_position') +offset('recheck_equipment',df.unit,'military','pickup_flags') +offset('mood',df.unit,'mood') +offset('birth_year',df.unit,'relations','birth_year') +offset('birth_time',df.unit,'relations','birth_time') +offset('current_job',df.unit,'job','current_job') +offset('physical_attrs',df.unit,'body','physical_attrs') +vector('body_size',df.unit,'appearance','body_modifiers') +offset('curse',df.unit,'curse','name') +offset('turn_count',df.unit,'curse','time_on_site') +vector('souls',df.unit,'status','souls') +vector('states',df.unit,'status','misc_traits') +offset('labors',df.unit,'status','labors') +offset('happiness',df.unit,'status','happiness') +offset('squad_ref_id',df.unit,'hist_figure_id') +offset('hist_id',df.unit,'hist_figure_id') + +header('soul_details') +offset('name',df.unit_soul,'name') +offset('mental_attrs',df.unit_soul,'mental_attrs') +vector('skills',df.unit_soul,'skills') +offset('traits',df.unit_soul,'traits') + +header('job_details') +offset('id',df.job,'job_type') +value('on_break_flag',df.misc_trait_type.OnBreak) +offset('sub_job_id',df.job,'reaction_name') +offset('reaction',df.reaction,'name') +offset('reaction_skill',df.reaction,'skill') + +header('squad_offsets') +offset('id',df.squad,'id') +offset('name',df.squad,'name') +offset('name_old',df.squad,'name','words') +offset('alias',df.squad,'alias') +vector('members',df.squad,'positions') + +-- Final creation of the file + +local out = io.open('therapist.ini', 'w') + +out:write('[info]\n') +-- TODO: add an api function to retrieve the checksum +out:write('checksum=<>\n') +out:write('version_name='..dfhack.getDFVersion()..'\n') +out:write('complete='..(complete and 'true' or 'false')..'\n') + +for i,v in ipairs(lines) do + out:write(v..'\n') +end + +out:write[[ + +[valid_flags_1] +size=1 +1\name=Not from around these parts +1\value=0x80000000 + +[valid_flags_2] +size=0 + +[invalid_flags_1] +size=9 +1\name=a zombie +1\value=0x00001000 +2\name=a skeleton +2\value=0x00002000 +3\name=a merchant or diplomat +3\value=0x00000040 +4\name=outpost liason +4\value=0x00000800 +5\name=an invader or hostile +5\value=0x00020000 +6\name=an invader or hostile +6\value=0x00080000 +7\name=an invader or hostile +7\value=0x000C0000 +8\name=a merchant escort +8\value=0x00000080 +9\name="Dead, Jim." +9\value=0x00000002 + +[invalid_flags_2] +size=5 +1\name="killed, Jim." +1\value=0x00000080 +2\name=from the Underworld. SPOOKY! +2\value=0x00040000 +3\name=resident +3\value=0x00080000 +4\name=visitor_uninvited +4\value=0x00400000 +5\name=visitor +5\value=0x00800000 + +[invalid_flags_3] +size=1 +1\name=a ghost +1\value=0x00001000 +]] + +out:close() diff --git a/scripts/devel/inject-raws.lua b/scripts/devel/inject-raws.lua index a4ebeec1d..1450d1179 100644 --- a/scripts/devel/inject-raws.lua +++ b/scripts/devel/inject-raws.lua @@ -5,6 +5,11 @@ -- This allows just adding stub definitions, and simply saving and -- reloading the game. +-- Usage example: +--[[ +devel/inject-raws trapcomp ITEM_TRAPCOMP_STEAM_PISTON workshop STEAM_ENGINE MAGMA_STEAM_ENGINE reaction STOKE_BOILER +]] + local utils = require 'utils' local raws = df.global.world.raws From e5fd918b0c1a40fe1ec718206119814fbf4c113d Mon Sep 17 00:00:00 2001 From: jj Date: Sun, 5 May 2013 03:55:08 +0200 Subject: [PATCH 05/34] job: unk2 -> subtype --- library/modules/Job.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp index c6469657c..87bcde6f5 100644 --- a/library/modules/Job.cpp +++ b/library/modules/Job.cpp @@ -136,7 +136,7 @@ bool DFHack::operator== (const df::job &a, const df::job &b) CHECK_NULL_POINTER(&a); CHECK_NULL_POINTER(&b); - if (!(CMP(job_type) && CMP(unk2) && + if (!(CMP(job_type) && CMP(job_subtype) && CMP(mat_type) && CMP(mat_index) && CMP(item_subtype) && CMP(item_category.whole) && CMP(hist_figure_id) && CMP(material_category.whole) && From 29c77ff85a654ec90464a300e531c0951f6b23c4 Mon Sep 17 00:00:00 2001 From: Tareq A Khandaker Date: Wed, 1 May 2013 15:01:40 -0400 Subject: [PATCH 06/34] Add entries to match splintermind-attributes DT The added address/offset entries correspond to those found in the splintermind-attributes branch of Dwarf Therapist for Dwarf Fortress v0.34.11 SDL (windows). These entries can be found the following location: https://code.google.com/r/splintermind-attributes/source/detail?spec=svneeeac8544d94c133ef2eb7c9a4ef8f2865906b19&r=614f1a76b56a081048626e3acaa5c87bcd313fef under etc/memory_layouts/windows/v0.34.11_graphics.ini --- scripts/devel/export-dt-ini.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/scripts/devel/export-dt-ini.lua b/scripts/devel/export-dt-ini.lua index 9fb6285fe..54151577a 100644 --- a/scripts/devel/export-dt-ini.lua +++ b/scripts/devel/export-dt-ini.lua @@ -109,6 +109,8 @@ vector('shapes_vector',globals,'world','raws','language','shapes') offset('base_materials',globals,'world','raws','mat_table','builtin') vector('inorganics_vector',globals,'world','raws','inorganics') vector('plants_vector',globals,'world','raws','plants','all') +vector('active_sites_vector',df.world_data,'active_site') +offset('world_site_type',df.world_data,'next_site_id') header('offsets') offset('word_table',df.language_translation,'words') @@ -140,6 +142,7 @@ vector('pref_string_vector',df.creature_raw,'prefstring') vector('castes_vector',df.creature_raw,'caste') vector('pop_ratio_vector',df.creature_raw,'pop_ratio') vector('materials_vector',df.creature_raw,'material') +offset('flags',df.creature_raw,'flags') header('caste_offsets') offset('caste_name',df.caste_raw,'caste_name') @@ -147,6 +150,9 @@ offset('caste_descr',df.caste_raw,'description') offset('caste_phys_att_ranges',df.caste_raw,'attributes','phys_att_range') offset('caste_ment_att_ranges',df.caste_raw,'attributes','ment_att_range') offset('adult_size',df.caste_raw,'misc','adult_size') +offset('flags',df.caste_raw,'flags') +offset('extracts',df.caste_raw,'extracts') +offset('skill_rates',df.caste_raw,'skill_rates') header('hist_entity_offsets') vector('squads',df.historical_entity,'squads') @@ -174,6 +180,7 @@ header('weapon_offsets') offset('name_plural',df.itemdef_weaponst,'name_plural') offset('single_size',df.itemdef_weaponst,'two_handed') offset('multi_size',df.itemdef_weaponst,'minimum_size') +offset('ammo',df.itemdef_weaponst,'ranged_ammo') header('material_offsets') offset('solid_name',df.material_common,'state_name','Solid') @@ -183,6 +190,7 @@ offset('powder_name',df.material_common,'state_name','Powder') offset('paste_name',df.material_common,'state_name','Paste') offset('pressed_name',df.material_common,'state_name','Pressed') offset('inorganic_materials_vector',df.inorganic_raw,'material') +offset('flags',df.material_common,'flags') header('plant_offsets') offset('name',df.plant_raw,'name') @@ -190,6 +198,7 @@ offset('name_plural',df.plant_raw,'name_plural') offset('name_leaf_plural',df.plant_raw,'leaves_plural') offset('name_seed_plural',df.plant_raw,'seed_plural') vector('materials_vector',df.plant_raw,'material') +offset('flags',df.plant_raw,'flags') header('item_offsets') offset('name_plural',df.itemdef_armorst,'name_plural') @@ -233,12 +242,14 @@ offset('labors',df.unit,'status','labors') offset('happiness',df.unit,'status','happiness') offset('squad_ref_id',df.unit,'hist_figure_id') offset('hist_id',df.unit,'hist_figure_id') +offset('artifact_name',df.unit,'status','artifact_name') header('soul_details') offset('name',df.unit_soul,'name') offset('mental_attrs',df.unit_soul,'mental_attrs') vector('skills',df.unit_soul,'skills') offset('traits',df.unit_soul,'traits') +offset('preferences',df.unit_soul,'preferences') header('job_details') offset('id',df.job,'job_type') @@ -246,6 +257,9 @@ value('on_break_flag',df.misc_trait_type.OnBreak) offset('sub_job_id',df.job,'reaction_name') offset('reaction',df.reaction,'name') offset('reaction_skill',df.reaction,'skill') +offset('mat_type',df.job,'mat_type') +offset('mat_index',df.job,'mat_index') +offset('mat_category',df.job,'material_category') header('squad_offsets') offset('id',df.squad,'id') @@ -298,6 +312,8 @@ size=9 8\value=0x00000080 9\name="Dead, Jim." 9\value=0x00000002 +10\name=marauder +10\value=0x00000010 [invalid_flags_2] size=5 From cafa966be44cb7648a909f36a0c08c0eeebe3845 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 9 May 2013 18:58:52 +0400 Subject: [PATCH 07/34] Subtract the ASLR rebase delta in the export-dt-ini script. --- scripts/devel/export-dt-ini.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/devel/export-dt-ini.lua b/scripts/devel/export-dt-ini.lua index 54151577a..9c86ae46a 100644 --- a/scripts/devel/export-dt-ini.lua +++ b/scripts/devel/export-dt-ini.lua @@ -8,6 +8,7 @@ local ms = require 'memscan' local globals = df.global local global_addr = dfhack.internal.getAddress local os_type = dfhack.getOSType() +local rdelta = dfhack.internal.getRebaseDelta() local vbias = 0 if os_type == 'windows' then vbias = -4 end @@ -39,6 +40,7 @@ local function address(name,bias,base,field,...) if base == globals then addr = global_addr(field) + bias = bias - rdelta if addr and select('#',...) > 0 then _,addr = df.sizeof(ms.field_ref(base,field,...)) end From 89d91eddb60818f9810a6488e8eb165b63c43685 Mon Sep 17 00:00:00 2001 From: Tareq A Khandaker Date: Sat, 11 May 2013 13:08:01 -0400 Subject: [PATCH 08/34] Add & correct offsets for splintermind-attributes --- scripts/devel/export-dt-ini.lua | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/scripts/devel/export-dt-ini.lua b/scripts/devel/export-dt-ini.lua index 9c86ae46a..90776f1e2 100644 --- a/scripts/devel/export-dt-ini.lua +++ b/scripts/devel/export-dt-ini.lua @@ -90,7 +90,7 @@ vector('historical_figures',globals,'world','history','figures') vector('fake_identities',globals,'world','assumed_identities','all') vector('historical_figures_vector',globals,'world','history','figures') vector('fake_identities_vector',globals,'world','assumed_identities','all') -vector('fortress_entity',globals,'ui','main','fortress_entity') +offset('fortress_entity',globals,'ui','main','fortress_entity') vector('historical_entities_vector',globals,'world','entities','all') vector('weapons_vector',globals,'world','raws','itemdefs','weapons') vector('trap_vector',globals,'world','raws','itemdefs','trapcomps') @@ -111,8 +111,10 @@ vector('shapes_vector',globals,'world','raws','language','shapes') offset('base_materials',globals,'world','raws','mat_table','builtin') vector('inorganics_vector',globals,'world','raws','inorganics') vector('plants_vector',globals,'world','raws','plants','all') +vector('material_templates_vector',globals,'world','raws','material_templates') +offset('world_data',globals,'world','world_data') vector('active_sites_vector',df.world_data,'active_site') -offset('world_site_type',df.world_data,'next_site_id') +offset('world_site_type',df.world_site,'type') header('offsets') offset('word_table',df.language_translation,'words') @@ -153,8 +155,10 @@ offset('caste_phys_att_ranges',df.caste_raw,'attributes','phys_att_range') offset('caste_ment_att_ranges',df.caste_raw,'attributes','ment_att_range') offset('adult_size',df.caste_raw,'misc','adult_size') offset('flags',df.caste_raw,'flags') -offset('extracts',df.caste_raw,'extracts') +vector('extracts',df.caste_raw,'extracts','extract_matidx') offset('skill_rates',df.caste_raw,'skill_rates') +offset('caste_att_rates',df.caste_raw,'attributes','phys_att_rates') +offset('caste_att_caps',df.caste_raw,'attributes','phys_att_cap_perc') header('hist_entity_offsets') vector('squads',df.historical_entity,'squads') @@ -237,11 +241,13 @@ offset('current_job',df.unit,'job','current_job') offset('physical_attrs',df.unit,'body','physical_attrs') vector('body_size',df.unit,'appearance','body_modifiers') offset('curse',df.unit,'curse','name') +offset('curse_add_flags1',df.unit,'curse','add_tags1') offset('turn_count',df.unit,'curse','time_on_site') vector('souls',df.unit,'status','souls') vector('states',df.unit,'status','misc_traits') offset('labors',df.unit,'status','labors') offset('happiness',df.unit,'status','happiness') +vector('thoughts',df.unit,'status','recent_events') offset('squad_ref_id',df.unit,'hist_figure_id') offset('hist_id',df.unit,'hist_figure_id') offset('artifact_name',df.unit,'status','artifact_name') @@ -251,7 +257,7 @@ offset('name',df.unit_soul,'name') offset('mental_attrs',df.unit_soul,'mental_attrs') vector('skills',df.unit_soul,'skills') offset('traits',df.unit_soul,'traits') -offset('preferences',df.unit_soul,'preferences') +vector('preferences',df.unit_soul,'preferences') header('job_details') offset('id',df.job,'job_type') @@ -295,7 +301,7 @@ size=1 size=0 [invalid_flags_1] -size=9 +size=10 1\name=a zombie 1\value=0x00001000 2\name=a skeleton From 49409d46304f6c0765db72422a86e40f75f0afb5 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 11 Jun 2013 16:10:42 +0400 Subject: [PATCH 09/34] Follow xml changes. --- library/modules/Units.cpp | 10 ++++------ library/xml | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 193dffb24..19f89fbb2 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -1208,12 +1208,10 @@ int Units::computeMovementSpeed(df::unit *unit) if (unsigned(unit->counters.webbed-1) <= 8) speed += unit->counters.webbed*100; - // Muscle weight vs vascular tissue (?) + // Muscle and fat weight vs expected size - auto &attr_tissue = unit->body.physical_attr_tissues; - int muscle = attr_tissue[STRENGTH]; - int blood = attr_tissue[AGILITY]; - speed = std::max(speed*3/4, std::min(speed*3/2, int(int64_t(speed)*muscle/blood))); + auto &s_info = unit->body.size_info; + speed = std::max(speed*3/4, std::min(speed*3/2, int(int64_t(speed)*s_info.size_cur/s_info.size_base))); // Attributes @@ -1243,7 +1241,7 @@ int Units::computeMovementSpeed(df::unit *unit) // Inventory encumberance int total_weight = calcInventoryWeight(unit); - int free_weight = std::max(1, muscle/10 + strength_attr*3); + int free_weight = std::max(1, s_info.size_cur/10 + strength_attr*3); if (free_weight < total_weight) { diff --git a/library/xml b/library/xml index cacb0b9a3..24d29ef54 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit cacb0b9a347aaadc2b4b052a7cda40e58104f04f +Subproject commit 24d29ef54a4f60efb26fd9608c5cb11dfcd4d940 From 4193d9b5b39b64c0401797762243d02050087015 Mon Sep 17 00:00:00 2001 From: Quietust Date: Fri, 14 Jun 2013 14:40:02 -0500 Subject: [PATCH 10/34] Add new plugin for cleaning up construction building materials --- plugins/CMakeLists.txt | 1 + plugins/cleanconst.cpp | 90 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 plugins/cleanconst.cpp diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 638fed3fd..ee4bd9390 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -155,6 +155,7 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(mousequery mousequery.cpp) DFHACK_PLUGIN(autotrade autotrade.cpp) DFHACK_PLUGIN(stocks stocks.cpp) + DFHACK_PLUGIN(cleanconst cleanconst.cpp) endif() diff --git a/plugins/cleanconst.cpp b/plugins/cleanconst.cpp new file mode 100644 index 000000000..7d9fa69d5 --- /dev/null +++ b/plugins/cleanconst.cpp @@ -0,0 +1,90 @@ +// Destroys items being used as part of constructions +// and flags the constructions to recreate their components upon disassembly +using namespace std; + +#include "Core.h" +#include "Console.h" +#include "Export.h" +#include "PluginManager.h" +#include "modules/Maps.h" + +#include "DataDefs.h" +#include "df/item.h" +#include "df/world.h" +#include "df/construction.h" +#include "df/map_block.h" + +using namespace DFHack; +using namespace df::enums; + +using df::global::world; + +DFHACK_PLUGIN("cleanconst"); + +command_result df_cleanconst(color_ostream &out, vector & parameters); + +DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) +{ + commands.push_back(PluginCommand( + "cleanconst", "Cleans up construction materials.", + df_cleanconst, false, + " This utility alters all constructions on the map so that they spawn their.\n" + " building component when they are disassembled, allowing their actual\n" + " build items to be safely deleted.\n" + )); + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + return CR_OK; +} + +command_result df_cleanconst(color_ostream &out, vector & parameters) +{ + CoreSuspender suspend; + + if (!Maps::IsValid()) + { + out.printerr("Map is not available!\n"); + return CR_FAILURE; + } + size_t numItems = world->items.all.size(); + + int cleaned_total = 0; + + // proceed with the cleanup operation + for (size_t i = 0; i < numItems; i++) + { + df::item *item = world->items.all[i]; + // only process items marked as "in construction" + if (!item->flags.bits.construction) + continue; + df::coord pos(item->pos.x, item->pos.y, item->pos.z); + + df::construction *cons = df::construction::find(pos); + if (!cons) + { + out.printerr("Item at %i,%i,%i marked as construction but no construction is present!\n", pos.x, pos.y, pos.z); + continue; + } + // if the construction is already labeled as "no build item", then leave it alone + if (cons->flags.bits.no_build_item) + continue; + + // only destroy the item if the construction claims to be made of the exact same thing + if (item->getType() != cons->item_type || + item->getSubtype() != cons->item_subtype || + item->getMaterial() != cons->mat_type || + item->getMaterialIndex() != cons->mat_index) + continue; + + item->flags.bits.garbage_collect = 1; + cons->flags.bits.no_build_item = 1; + + cleaned_total++; + } + + out.print("Done. %d constructed items cleaned up.\n", cleaned_total); + return CR_OK; +} \ No newline at end of file From 8c6881beb68e6b0a440cdb7f7c44d9e9cba408d3 Mon Sep 17 00:00:00 2001 From: Quietust Date: Fri, 14 Jun 2013 14:46:10 -0500 Subject: [PATCH 11/34] Cleanup --- plugins/cleanconst.cpp | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/plugins/cleanconst.cpp b/plugins/cleanconst.cpp index 7d9fa69d5..3efdadecb 100644 --- a/plugins/cleanconst.cpp +++ b/plugins/cleanconst.cpp @@ -1,6 +1,5 @@ // Destroys items being used as part of constructions // and flags the constructions to recreate their components upon disassembly -using namespace std; #include "Core.h" #include "Console.h" @@ -14,32 +13,13 @@ using namespace std; #include "df/construction.h" #include "df/map_block.h" +using namespace std; using namespace DFHack; -using namespace df::enums; using df::global::world; DFHACK_PLUGIN("cleanconst"); -command_result df_cleanconst(color_ostream &out, vector & parameters); - -DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) -{ - commands.push_back(PluginCommand( - "cleanconst", "Cleans up construction materials.", - df_cleanconst, false, - " This utility alters all constructions on the map so that they spawn their.\n" - " building component when they are disassembled, allowing their actual\n" - " build items to be safely deleted.\n" - )); - return CR_OK; -} - -DFhackCExport command_result plugin_shutdown ( color_ostream &out ) -{ - return CR_OK; -} - command_result df_cleanconst(color_ostream &out, vector & parameters) { CoreSuspender suspend; @@ -85,6 +65,23 @@ command_result df_cleanconst(color_ostream &out, vector & parameters) cleaned_total++; } - out.print("Done. %d constructed items cleaned up.\n", cleaned_total); + out.print("Done. %d construction items cleaned up.\n", cleaned_total); + return CR_OK; +} + +DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) +{ + commands.push_back(PluginCommand( + "cleanconst", "Cleans up construction materials.", + df_cleanconst, false, + " This utility alters all constructions on the map so that they spawn their\n" + " building component when they are disassembled, allowing their actual\n" + " build items to be safely deleted.\n" + )); + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ return CR_OK; } \ No newline at end of file From 21dc3afede209419a7c218b95a5f9146cd78a4d1 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 20 Jun 2013 17:20:04 +0400 Subject: [PATCH 12/34] Find cur_year_tick_advmode in devel/find-offsets.lua --- scripts/devel/find-offsets.lua | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/scripts/devel/find-offsets.lua b/scripts/devel/find-offsets.lua index 2f7f1287c..ea5450a58 100644 --- a/scripts/devel/find-offsets.lua +++ b/scripts/devel/find-offsets.lua @@ -1306,6 +1306,27 @@ local function find_cur_year_tick() ms.found_offset('cur_year_tick', addr) end +local function find_cur_year_tick_advmode() + stop_autosave() + + local feed = dwarfmode_to_top() + local addr = searcher:find_interactive( + 'Searching for cur_year_tick_advmode.', + 'int32_t', + function(idx) + if idx > 0 then + if not step_n_frames(1, feed) then + return false + end + end + return true, nil, 144 + end, + 20 + ) + + ms.found_offset('cur_year_tick_advmode', addr) +end + -- -- cur_season_tick -- @@ -1499,6 +1520,7 @@ print('\nUnpausing globals:\n') exec_finder(find_cur_year, 'cur_year') exec_finder(find_cur_year_tick, 'cur_year_tick') +exec_finder(find_cur_year_tick_advmode, 'cur_year_tick_advmode') exec_finder(find_cur_season_tick, 'cur_season_tick') exec_finder(find_cur_season, 'cur_season') exec_finder(find_process_jobs, 'process_jobs') From e15f5c451fa3105ed628f9f30f4078278bbd8d87 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 20 Jun 2013 18:18:02 +0400 Subject: [PATCH 13/34] Fix search for cur_season_tick and cur_season in devel/find-offsets. It seems the one-off error is a problem with the original save I used, and not the result of some general behavior. --- scripts/devel/find-offsets.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/devel/find-offsets.lua b/scripts/devel/find-offsets.lua index ea5450a58..1ce7a4425 100644 --- a/scripts/devel/find-offsets.lua +++ b/scripts/devel/find-offsets.lua @@ -1350,7 +1350,7 @@ menu, then do as instructed below:]], return false end end - return true, math.floor(((df.global.cur_year_tick+10)%100800)/10) + return true, math.floor((df.global.cur_year_tick%100800)/10) end ) ms.found_offset('cur_season_tick', addr) @@ -1382,7 +1382,7 @@ menu, then do as instructed below:]], return false end end - return true, math.floor((df.global.cur_year_tick+10)/100800)%4 + return true, math.floor(df.global.cur_year_tick/100800)%4 end ) ms.found_offset('cur_season', addr) From 297fa86c34e136f6765a6353eb0b3311e9a5f918 Mon Sep 17 00:00:00 2001 From: jj Date: Sun, 7 Jul 2013 17:40:28 +0200 Subject: [PATCH 14/34] follow df-structures change to drawBuilding --- plugins/power-meter.cpp | 2 +- plugins/steam-engine.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/power-meter.cpp b/plugins/power-meter.cpp index 8db6c6f30..efda2c590 100644 --- a/plugins/power-meter.cpp +++ b/plugins/power-meter.cpp @@ -145,7 +145,7 @@ struct trap_hook : df::building_trapst { INTERPOSE_NEXT(updateAction)(); } - DEFINE_VMETHOD_INTERPOSE(void, drawBuilding, (df::building_drawbuffer *db, void *unk)) + DEFINE_VMETHOD_INTERPOSE(void, drawBuilding, (df::building_drawbuffer *db, int16_t unk)) { INTERPOSE_NEXT(drawBuilding)(db, unk); diff --git a/plugins/steam-engine.cpp b/plugins/steam-engine.cpp index a8ba0e214..01d1dbd58 100644 --- a/plugins/steam-engine.cpp +++ b/plugins/steam-engine.cpp @@ -721,7 +721,7 @@ struct workshop_hook : df::building_workshopst { INTERPOSE_NEXT(updateAction)(); } - DEFINE_VMETHOD_INTERPOSE(void, drawBuilding, (df::building_drawbuffer *db, void *unk)) + DEFINE_VMETHOD_INTERPOSE(void, drawBuilding, (df::building_drawbuffer *db, int16_t unk)) { INTERPOSE_NEXT(drawBuilding)(db, unk); From 6f5c03b912178ec92a1abacad28b13ffd6a1aa6a Mon Sep 17 00:00:00 2001 From: jj Date: Sun, 7 Jul 2013 23:34:55 +0200 Subject: [PATCH 15/34] core: generate SC_WORLD_LOADED for arena too --- library/Core.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/Core.cpp b/library/Core.cpp index 2021a8bc5..2b118e2ae 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -1160,7 +1160,8 @@ void Core::doUpdate(color_ostream &out, bool first_update) { df::world_data *wdata = df::global::world->world_data; // when the game is unloaded, world_data isn't deleted, but its contents are - if (wdata && !wdata->sites.empty()) + // regions work to detect arena too + if (wdata && !wdata->regions.empty()) new_wdata = wdata; new_mapdata = df::global::world->map.block_index; } From 090d7defc57ca17b37c17a96d155721b21ba7b16 Mon Sep 17 00:00:00 2001 From: jj Date: Sun, 7 Jul 2013 23:53:17 +0200 Subject: [PATCH 16/34] add NEWS entry for arena SC_WORLD_LOADED --- NEWS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 2e821df25..c8f8aee90 100644 --- a/NEWS +++ b/NEWS @@ -13,7 +13,8 @@ DFHack future - exterminate: renamed from slayrace, add help message, add butcher mode - autoSyndrome: disable by default - ruby: add df.dfhack_run "somecommand" - - magmasource: rename to source, allow water/magma sources/drains + - magmasource: rename to 'source', allow water/magma sources/drains + - core: fix SC_WORLD_(UN)LOADED event for arena mode New plugins: - buildingplan: Place furniture before it's built - resume: A plugin to help display and resume suspended constructions conveniently From 308e7b682003722c5226137486a14fe14170c193 Mon Sep 17 00:00:00 2001 From: jj Date: Sat, 10 Aug 2013 01:57:11 +0200 Subject: [PATCH 17/34] fix renamed pregnancy_ptr/mystery to pregnancy_genes/caste --- plugins/catsplosion.cpp | 6 +++--- plugins/lua/dfusion/tools.lua | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/catsplosion.cpp b/plugins/catsplosion.cpp index 179aec2fd..c0702df2c 100644 --- a/plugins/catsplosion.cpp +++ b/plugins/catsplosion.cpp @@ -120,14 +120,14 @@ command_result catsplosion (color_ostream &out, std::vector & para female->relations.pregnancy_timer = rand() % 100 + 1; totalchanged++; } - else if(!female->relations.pregnancy_ptr) + else if(!female->relations.pregnancy_genes) { df::unit_genes *preg = new df::unit_genes; preg->appearance = female->appearance.genes.appearance; preg->colors = female->appearance.genes.colors; - female->relations.pregnancy_ptr = preg; + female->relations.pregnancy_genes = preg; female->relations.pregnancy_timer = rand() % 100 + 1; - female->relations.pregnancy_mystery = 1; // WTF is this? + female->relations.pregnancy_caste = 1; totalcreated ++; } } diff --git a/plugins/lua/dfusion/tools.lua b/plugins/lua/dfusion/tools.lua index b066ccfcb..b2cbcf8e2 100644 --- a/plugins/lua/dfusion/tools.lua +++ b/plugins/lua/dfusion/tools.lua @@ -159,22 +159,22 @@ function empregnate(unit) unit.curse.add_tags2.STERILE=false end local genes = unit.appearance.genes - if unit.relations.pregnancy_ptr == nil then + if unit.relations.pregnancy_genes == nil then print("creating preg ptr.") if false then - print(string.format("%x %x",df.sizeof(unit.relations:_field("pregnancy_ptr")))) + print(string.format("%x %x",df.sizeof(unit.relations:_field("pregnancy_genes")))) return end - unit.relations.pregnancy_ptr = { new = true, assign = genes } + unit.relations.pregnancy_genes = { new = true, assign = genes } end - local ngenes = unit.relations.pregnancy_ptr + local ngenes = unit.relations.pregnancy_genes if #ngenes.appearance ~= #genes.appearance or #ngenes.colors ~= #genes.colors then print("Array sizes incorrect, fixing.") ngenes:assign(genes); end print("Setting preg timer.") unit.relations.pregnancy_timer=10 - unit.relations.pregnancy_mystery=1 + unit.relations.pregnancy_caste=1 end menu:add("Empregnate",empregnate) function healunit(unit) From df2e04db261ed9ef8bbb7d2fad9f879547bf43d9 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 16 Aug 2013 18:25:16 +0400 Subject: [PATCH 18/34] Update xml definitions. --- library/modules/Units.cpp | 1 + library/xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 19f89fbb2..39f9dd167 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -66,6 +66,7 @@ using namespace std; #include "df/game_mode.h" #include "df/unit_misc_trait.h" #include "df/unit_skill.h" +#include "df/curse_attr_change.h" using namespace DFHack; using namespace df::enums; diff --git a/library/xml b/library/xml index 24d29ef54..da9ab7db3 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 24d29ef54a4f60efb26fd9608c5cb11dfcd4d940 +Subproject commit da9ab7db3ff074b5f1feebbc37436b3c30e55bb4 From 70a2ab91417281a39df2d26ab2ca2d654e4b58ff Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 22 Aug 2013 11:22:18 +0400 Subject: [PATCH 19/34] Fix crash due to incorrect loop bounds in add-spatter. This caused a buffer overrun bug if an ADD_SPATTER reaction had any non-improvement outputs. --- plugins/add-spatter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/add-spatter.cpp b/plugins/add-spatter.cpp index ca37c8ee3..f7a611ec8 100644 --- a/plugins/add-spatter.cpp +++ b/plugins/add-spatter.cpp @@ -378,7 +378,7 @@ static bool find_reactions(color_ostream &out) parse_product(out, out_prod.back(), it->second.react, itprod); } - for (size_t i = 0; i < prod.size(); i++) + for (size_t i = 0; i < out_prod.size(); i++) { if (out_prod[i].isValid()) products[out_prod[i].product] = &out_prod[i]; From 896cd11fe92c5a069c0cdf14bdda17c7e286f0b0 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 22 Aug 2013 12:14:45 +0400 Subject: [PATCH 20/34] Track readable names of vmethod hooks for diagnostic messages. Note that this changes the ABI of all plugins that use hooks. --- library/VTableInterpose.cpp | 8 ++++---- library/include/VTableInterpose.h | 11 +++++++---- plugins/tweak.cpp | 6 +++--- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/library/VTableInterpose.cpp b/library/VTableInterpose.cpp index 0db5ac03c..1184865ac 100644 --- a/library/VTableInterpose.cpp +++ b/library/VTableInterpose.cpp @@ -288,9 +288,9 @@ void VMethodInterposeLinkBase::set_chain(void *chain) addr_to_method_pointer_(chain_mptr, chain); } -VMethodInterposeLinkBase::VMethodInterposeLinkBase(virtual_identity *host, int vmethod_idx, void *interpose_method, void *chain_mptr, int priority) +VMethodInterposeLinkBase::VMethodInterposeLinkBase(virtual_identity *host, int vmethod_idx, void *interpose_method, void *chain_mptr, int priority, const char *name) : host(host), vmethod_idx(vmethod_idx), interpose_method(interpose_method), - chain_mptr(chain_mptr), priority(priority), + chain_mptr(chain_mptr), priority(priority), name_str(name), applied(false), saved_chain(NULL), next(NULL), prev(NULL) { if (vmethod_idx < 0 || interpose_method == NULL) @@ -303,8 +303,8 @@ VMethodInterposeLinkBase::VMethodInterposeLinkBase(virtual_identity *host, int v * - interpose_method comes from method_pointer_to_addr_ */ - fprintf(stderr, "Bad VMethodInterposeLinkBase arguments: %d %08x\n", - vmethod_idx, unsigned(interpose_method)); + fprintf(stderr, "Bad VMethodInterposeLinkBase arguments: %d %08x (%s)\n", + vmethod_idx, unsigned(interpose_method), name_str); fflush(stderr); abort(); } diff --git a/library/include/VTableInterpose.h b/library/include/VTableInterpose.h index 8e2541c55..f93eb4176 100644 --- a/library/include/VTableInterpose.h +++ b/library/include/VTableInterpose.h @@ -141,7 +141,7 @@ namespace DFHack #define IMPLEMENT_VMETHOD_INTERPOSE_PRIO(class,name,priority) \ DFHack::VMethodInterposeLink \ - class::interpose_##name(&class::interpose_base::name, &class::interpose_fn_##name, priority); + class::interpose_##name(&class::interpose_base::name, &class::interpose_fn_##name, priority, #class"::"#name); #define IMPLEMENT_VMETHOD_INTERPOSE(class,name) IMPLEMENT_VMETHOD_INTERPOSE_PRIO(class,name,0) @@ -161,6 +161,7 @@ namespace DFHack void *interpose_method; // Pointer to the code of the interposing method void *chain_mptr; // Pointer to the chain field in the subclass below int priority; // Higher priority hooks are called earlier + const char *name_str; // Name of the hook bool applied; // True if this hook is currently applied void *saved_chain; // Pointer to the code of the original vmethod or next hook @@ -179,12 +180,14 @@ namespace DFHack VMethodInterposeLinkBase *get_first_interpose(virtual_identity *id); bool find_child_hosts(virtual_identity *cur, void *vmptr); public: - VMethodInterposeLinkBase(virtual_identity *host, int vmethod_idx, void *interpose_method, void *chain_mptr, int priority); + VMethodInterposeLinkBase(virtual_identity *host, int vmethod_idx, void *interpose_method, void *chain_mptr, int priority, const char *name); ~VMethodInterposeLinkBase(); bool is_applied() { return applied; } bool apply(bool enable = true); void remove(); + + const char *name() { return name_str; } }; template @@ -198,13 +201,13 @@ namespace DFHack operator Ptr () { return chain; } template - VMethodInterposeLink(Ptr target, Ptr2 src, int priority) + VMethodInterposeLink(Ptr target, Ptr2 src, int priority, const char *name) : VMethodInterposeLinkBase( &Base::_identity, vmethod_pointer_to_idx(target), method_pointer_to_addr(src), &chain, - priority + priority, name ) { src = target; /* check compatibility */ } }; diff --git a/plugins/tweak.cpp b/plugins/tweak.cpp index 59465b99f..337759348 100644 --- a/plugins/tweak.cpp +++ b/plugins/tweak.cpp @@ -937,14 +937,14 @@ static void enable_hook(color_ostream &out, VMethodInterposeLinkBase &hook, vect if (vector_get(parameters, 1) == "disable") { hook.remove(); - out.print("Disabled tweak %s\n", parameters[0].c_str()); + out.print("Disabled tweak %s (%s)\n", parameters[0].c_str(), hook.name()); } else { if (hook.apply()) - out.print("Enabled tweak %s\n", parameters[0].c_str()); + out.print("Enabled tweak %s (%s)\n", parameters[0].c_str(), hook.name()); else - out.printerr("Could not activate tweak %s\n", parameters[0].c_str()); + out.printerr("Could not activate tweak %s (%s)\n", parameters[0].c_str(), hook.name()); } } From b885123d4ea8ebe95536774a4798ddef3c67a6b1 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 26 Aug 2013 16:55:30 +0400 Subject: [PATCH 21/34] Fix a crash in beehive code if bees die with yet uncollected products. http://www.bay12games.com/dwarves/mantisbt/view.php?id=6368 --- NEWS | 2 ++ Readme.rst | 3 +++ dfhack.init-example | 3 +++ plugins/tweak.cpp | 48 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+) diff --git a/NEWS b/NEWS index c8f8aee90..b41444586 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,8 @@ DFHack future - ruby: add df.dfhack_run "somecommand" - magmasource: rename to 'source', allow water/magma sources/drains - core: fix SC_WORLD_(UN)LOADED event for arena mode + New tweaks: + - hive-crash: Prevent crash if bees die in a hive with ungathered products (bug 6368). New plugins: - buildingplan: Place furniture before it's built - resume: A plugin to help display and resume suspended constructions conveniently diff --git a/Readme.rst b/Readme.rst index cd9101aab..42ef41da3 100644 --- a/Readme.rst +++ b/Readme.rst @@ -1146,6 +1146,9 @@ Subcommands that persist until disabled or DF quit: (i.e. the more units you have, the slower it becomes), and making the units spar more. +:hive-crash: The hive code crashes if there are ungathered products in a hive without bees (bug 6368). + This tweak prevents it by auto-gathering the products if this happens. + fix-armory ---------- diff --git a/dfhack.init-example b/dfhack.init-example index 22d2c2972..b07603cc5 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -147,6 +147,9 @@ tweak military-color-assigned # remove inverse dependency of squad training speed on unit list size and use more sparring tweak military-training +# prevent crash if bees die in a hive with ungathered products by insta-gathering them +tweak hive-crash + # enable autoSyndrome autoSyndrome enable diff --git a/plugins/tweak.cpp b/plugins/tweak.cpp index 337759348..b9e6ea7f2 100644 --- a/plugins/tweak.cpp +++ b/plugins/tweak.cpp @@ -57,6 +57,7 @@ #include "df/activity_event_individual_skill_drillst.h" #include "df/activity_event_skill_demonstrationst.h" #include "df/activity_event_sparringst.h" +#include "df/building_hivest.h" #include @@ -136,6 +137,8 @@ DFhackCExport command_result plugin_init (color_ostream &out, std::vector item->getType() != item_type::VERMIN) + continue; + any_bees = true; + break; + } + + if (!any_bees) + { + bool any_products = false; + for (size_t i = 0; i < contained_items.size(); i++) + { + if (contained_items[i]->use_mode != 0 || + !contained_items[i]->item->flags.bits.in_building) + continue; + + contained_items[i]->item->flags.bits.in_building = false; + any_products = true; + } + + if (any_products) + { + color_ostream_proxy out(Core::getInstance().getConsole()); + out.print("Bees died in hive with products at (%d,%d,%d); preventing crash.\n", + centerx, centery, z); + } + } + + INTERPOSE_NEXT(updateAction)(); + } +}; + +IMPLEMENT_VMETHOD_INTERPOSE(hive_crash_hook, updateAction); + static void enable_hook(color_ostream &out, VMethodInterposeLinkBase &hook, vector ¶meters) { if (vector_get(parameters, 1) == "disable") @@ -1112,6 +1156,10 @@ static command_result tweak(color_ostream &out, vector ¶meters) enable_hook(out, INTERPOSE_HOOK(military_training_sp_hook, process), parameters); enable_hook(out, INTERPOSE_HOOK(military_training_id_hook, process), parameters); } + else if (cmd == "hive-crash") + { + enable_hook(out, INTERPOSE_HOOK(hive_crash_hook, updateAction), parameters); + } else return CR_WRONG_USAGE; From a487ede2b905cc5ec10b3e153cc2c2e174cf2416 Mon Sep 17 00:00:00 2001 From: jj Date: Mon, 23 Sep 2013 21:28:27 +0200 Subject: [PATCH 22/34] autodump: typo in errmsg --- plugins/autodump.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/autodump.cpp b/plugins/autodump.cpp index 5b4804647..c45b132ae 100644 --- a/plugins/autodump.cpp +++ b/plugins/autodump.cpp @@ -125,7 +125,7 @@ static command_result autodump_main(color_ostream &out, vector & parame { if (!Gui::getCursorCoords(cx,cy,cz)) { - out.printerr("Cursor position not found. Please enabled the cursor.\n"); + out.printerr("Cursor position not found. Please enable the cursor.\n"); return CR_FAILURE; } pos_cursor = DFCoord(cx,cy,cz); From d7e35c2d23a3b7837588c0965236f924992b841e Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 30 Sep 2013 13:19:51 +0400 Subject: [PATCH 23/34] Add built-in enable and disable commands. --- Readme.rst | 19 +++++++++ dfhack.init-example | 20 ++++++++- library/Core.cpp | 51 ++++++++++++++++++++++ library/PluginManager.cpp | 75 +++++++++++++++++++++++++++++++++ library/include/PluginManager.h | 14 ++++++ plugins/add-spatter.cpp | 2 + plugins/advtools.cpp | 2 +- plugins/autoSyndrome.cpp | 42 ++++++++++-------- plugins/autolabor.cpp | 37 ++++++++++------ plugins/automaterial.cpp | 22 ++++++++-- plugins/autotrade.cpp | 26 ++++++++++-- plugins/buildingplan.cpp | 23 ++++++++-- plugins/burrows.cpp | 3 +- plugins/devel/autolabor2.cpp | 38 +++++++++++------ plugins/devel/kittens.cpp | 5 +++ plugins/devel/memview.cpp | 5 +++ plugins/devel/nestboxes.cpp | 8 +++- plugins/devel/vshook.cpp | 23 ++++++++-- plugins/dwarfmonitor.cpp | 28 ++++++++++-- plugins/fastdwarf.cpp | 15 +++++++ plugins/fix-armory.cpp | 25 ++++++++--- plugins/follow.cpp | 5 +++ plugins/infiniteSky.cpp | 8 +++- plugins/manipulator.cpp | 21 ++++++++- plugins/misery.cpp | 22 +++++++++- plugins/mode.cpp | 6 --- plugins/mousequery.cpp | 22 ++++++++-- plugins/power-meter.cpp | 2 +- plugins/rename.cpp | 5 +++ plugins/resume.cpp | 27 +++++++++--- plugins/reveal.cpp | 6 +++ plugins/search.cpp | 24 ++++++++--- plugins/seedwatch.cpp | 8 +++- plugins/siege-engine.cpp | 21 ++++++++- plugins/steam-engine.cpp | 4 ++ plugins/workflow.cpp | 25 +++++++---- plugins/zone.cpp | 32 ++++++++++++-- 37 files changed, 607 insertions(+), 114 deletions(-) diff --git a/Readme.rst b/Readme.rst index 42ef41da3..d6ffcde23 100644 --- a/Readme.rst +++ b/Readme.rst @@ -209,6 +209,25 @@ or is a prefix ending at a '/' boundary would be considered for execution, i.e. for context ``foo/bar/baz``, possible matches are any of ``@foo/bar/baz``, ``@foo/bar``, ``@foo`` or none. +Enabling plugins +================ + +Many plugins can be in a distinct enabled or disabled state. Some of +them activate and deactivate automatically depending on the contents +of the world raws. Others store their state in world data. However a +number of them have to be enabled globally, and the init file is the +right place to do it. + +Most of such plugins support the built-in ``enable`` and ``disable`` +commands. Calling them at any time without arguments prints a list +of enabled and disabled plugins, and shows whether that can be changed +through the same commands. + +To enable or disable plugins that support this, use their names as +arguments for the command:: + + enable manipulator search + ======== Commands diff --git a/dfhack.init-example b/dfhack.init-example index b07603cc5..05fd020bc 100644 --- a/dfhack.init-example +++ b/dfhack.init-example @@ -150,8 +150,24 @@ tweak military-training # prevent crash if bees die in a hive with ungathered products by insta-gathering them tweak hive-crash -# enable autoSyndrome -autoSyndrome enable +########################### +# Globally acting plugins # +########################### + +# Dwarf Manipulator (simple in-game Dwarf Therapist replacement) +enable manipulator + +# Search tool in various screens (by falconne) +enable search + +# Improved build material selection interface (by falconne) +enable automaterial + +# Other interface improvement tools +#enable dwarfmonitor mousequery autotrade buildingplan resume zone + +# Auto Syndrome +#autoSyndrome enable ########### # Scripts # diff --git a/library/Core.cpp b/library/Core.cpp index 2b118e2ae..b8a5f4808 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -544,6 +544,56 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve } } } + else if( first == "enable" || first == "disable" ) + { + CoreSuspender suspend; + bool enable = (first == "enable"); + + if(parts.size()) + { + command_result res = CR_OK; + + for (size_t i = 0; i < parts.size(); i++) + { + Plugin * plug = plug_mgr->getPluginByName(parts[i]); + + if(!plug) + { + res = CR_NOT_FOUND; + con.printerr("No such plugin: %s\n", parts[i].c_str()); + } + else if (!plug->can_set_enabled()) + { + res = CR_NOT_IMPLEMENTED; + con.printerr("Cannot %s plugin: %s\n", first.c_str(), parts[i].c_str()); + } + else + { + res = plug->set_enabled(con, enable); + + if (res != CR_OK || plug->is_enabled() != enable) + con.printerr("Could not %s plugin: %s\n", first.c_str(), parts[i].c_str()); + } + } + + return res; + } + else + { + for(size_t i = 0; i < plug_mgr->size();i++) + { + Plugin * plug = (plug_mgr->operator[](i)); + if (!plug->can_be_enabled()) continue; + + con.print( + "%20s\t%-3s%s\n", + (plug->getName()+":").c_str(), + plug->is_enabled() ? "on" : "off", + plug->can_set_enabled() ? "" : " (controlled elsewhere)" + ); + } + } + } else if(first == "ls" || first == "dir") { bool all = false; @@ -584,6 +634,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve " load PLUGIN|all - Load a plugin by name or load all possible plugins.\n" " unload PLUGIN|all - Unload a plugin or all loaded plugins.\n" " reload PLUGIN|all - Reload a plugin or all loaded plugins.\n" + " enable/disable PLUGIN - Enable or disable a plugin if supported.\n" "\n" "plugins:\n" ); diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index d5636109b..3ef4910cb 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -172,6 +172,8 @@ Plugin::Plugin(Core * core, const std::string & filepath, const std::string & _f plugin_onupdate = 0; plugin_onstatechange = 0; plugin_rpcconnect = 0; + plugin_enable = 0; + plugin_is_enabled = 0; state = PS_UNLOADED; access = new RefLock(); } @@ -245,6 +247,8 @@ bool Plugin::load(color_ostream &con) plugin_shutdown = (command_result (*)(color_ostream &)) LookupPlugin(plug, "plugin_shutdown"); plugin_onstatechange = (command_result (*)(color_ostream &, state_change_event)) LookupPlugin(plug, "plugin_onstatechange"); plugin_rpcconnect = (RPCService* (*)(color_ostream &)) LookupPlugin(plug, "plugin_rpcconnect"); + plugin_enable = (command_result (*)(color_ostream &,bool)) LookupPlugin(plug, "plugin_enable"); + plugin_is_enabled = (bool*) LookupPlugin(plug, "plugin_is_enabled"); plugin_eval_ruby = (command_result (*)(color_ostream &, const char*)) LookupPlugin(plug, "plugin_eval_ruby"); index_lua(plug); this->name = *plug_name; @@ -254,11 +258,15 @@ bool Plugin::load(color_ostream &con) { state = PS_LOADED; parent->registerCommands(this); + if ((plugin_onupdate || plugin_enable) && !plugin_is_enabled) + con.printerr("Plugin %s has no enabled var!\n", name.c_str()); return true; } else { con.printerr("Plugin %s has failed to initialize properly.\n", filename.c_str()); + plugin_is_enabled = 0; + plugin_onupdate = 0; reset_lua(); ClosePlugin(plugin_lib); state = PS_BROKEN; @@ -294,6 +302,8 @@ bool Plugin::unload(color_ostream &con) if(plugin_shutdown) cr = plugin_shutdown(con); // cleanup... + plugin_is_enabled = 0; + plugin_onupdate = 0; reset_lua(); parent->unregisterCommands(this); commands.clear(); @@ -408,6 +418,12 @@ bool Plugin::can_invoke_hotkey(const std::string & command, df::viewscreen *top command_result Plugin::on_update(color_ostream &out) { + // Check things that are implicitly protected by the suspend lock + if (!plugin_onupdate) + return CR_NOT_IMPLEMENTED; + if (plugin_is_enabled && !*plugin_is_enabled) + return CR_OK; + // Grab mutex and call the thing command_result cr = CR_NOT_IMPLEMENTED; access->lock_add(); if(state == PS_LOADED && plugin_onupdate) @@ -419,6 +435,21 @@ command_result Plugin::on_update(color_ostream &out) return cr; } +command_result Plugin::set_enabled(color_ostream &out, bool enable) +{ + command_result cr = CR_NOT_IMPLEMENTED; + access->lock_add(); + if(state == PS_LOADED && plugin_is_enabled && plugin_enable) + { + cr = plugin_enable(out, enable); + + if (cr == CR_OK && enable != is_enabled()) + cr = CR_FAILURE; + } + access->lock_sub(); + return cr; +} + command_result Plugin::on_state_change(color_ostream &out, state_change_event event) { command_result cr = CR_NOT_IMPLEMENTED; @@ -524,6 +555,37 @@ void Plugin::reset_lua() } } +int Plugin::lua_is_enabled(lua_State *state) +{ + auto obj = (Plugin*)lua_touserdata(state, lua_upvalueindex(1)); + + RefAutoinc lock(obj->access); + if (obj->state == PS_LOADED && obj->plugin_is_enabled) + lua_pushboolean(state, obj->is_enabled()); + else + lua_pushnil(state); + + return 1; +} + +int Plugin::lua_set_enabled(lua_State *state) +{ + lua_settop(state, 1); + bool val = lua_toboolean(state, 1); + + auto obj = (Plugin*)lua_touserdata(state, lua_upvalueindex(1)); + RefAutoinc lock(obj->access); + + color_ostream *out = Lua::GetOutput(state); + + if (obj->state == PS_LOADED && obj->plugin_enable) + lua_pushboolean(state, obj->set_enabled(*out, val) == CR_OK); + else + luaL_error(state, "plugin %s unloaded, cannot enable or disable", obj->name.c_str()); + + return 1; +} + int Plugin::lua_cmd_wrapper(lua_State *state) { auto cmd = (LuaCommand*)lua_touserdata(state, lua_upvalueindex(1)); @@ -561,6 +623,19 @@ void Plugin::open_lua(lua_State *state, int table) RefAutolock lock(access); + if (plugin_is_enabled) + { + lua_pushlightuserdata(state, this); + lua_pushcclosure(state, lua_is_enabled, 1); + lua_setfield(state, table, "isEnabled"); + } + if (plugin_enable) + { + lua_pushlightuserdata(state, this); + lua_pushcclosure(state, lua_set_enabled, 1); + lua_setfield(state, table, "setEnabled"); + } + for (auto it = lua_commands.begin(); it != lua_commands.end(); ++it) { lua_pushlightuserdata(state, it->second); diff --git a/library/include/PluginManager.h b/library/include/PluginManager.h index 46a46f2df..506ca33c0 100644 --- a/library/include/PluginManager.h +++ b/library/include/PluginManager.h @@ -144,6 +144,11 @@ namespace DFHack bool unload(color_ostream &out); bool reload(color_ostream &out); + bool can_be_enabled() { return plugin_is_enabled != 0; } + bool is_enabled() { return plugin_is_enabled && *plugin_is_enabled; } + bool can_set_enabled() { return plugin_is_enabled != 0 && plugin_enable; } + command_result set_enabled(color_ostream &out, bool enable); + command_result invoke(color_ostream &out, const std::string & command, std::vector & parameters); bool can_invoke_hotkey(const std::string & command, df::viewscreen *top ); plugin_state getState () const; @@ -184,17 +189,22 @@ namespace DFHack static int lua_fun_wrapper(lua_State *state); void push_function(lua_State *state, LuaFunction *fn); + static int lua_is_enabled(lua_State *state); + static int lua_set_enabled(lua_State *state); + struct LuaEvent; std::map lua_events; void index_lua(DFLibrary *lib); void reset_lua(); + bool *plugin_is_enabled; command_result (*plugin_init)(color_ostream &, std::vector &); command_result (*plugin_status)(color_ostream &, std::string &); command_result (*plugin_shutdown)(color_ostream &); command_result (*plugin_onupdate)(color_ostream &); command_result (*plugin_onstatechange)(color_ostream &, state_change_event); + command_result (*plugin_enable)(color_ostream &, bool); RPCService* (*plugin_rpcconnect)(color_ostream &); command_result (*plugin_eval_ruby)(color_ostream &, const char*); }; @@ -250,6 +260,10 @@ namespace DFHack DFhackDataExport const char * name = plugin_name;\ DFhackDataExport Plugin *plugin_self = NULL; +#define DFHACK_PLUGIN_IS_ENABLED(varname) \ + DFhackDataExport bool plugin_is_enabled = false; \ + bool &varname = plugin_is_enabled; + #define DFHACK_PLUGIN_LUA_COMMANDS \ DFhackCExport const DFHack::CommandReg plugin_lua_commands[] = #define DFHACK_PLUGIN_LUA_FUNCTIONS \ diff --git a/plugins/add-spatter.cpp b/plugins/add-spatter.cpp index f7a611ec8..b915845fb 100644 --- a/plugins/add-spatter.cpp +++ b/plugins/add-spatter.cpp @@ -48,6 +48,7 @@ using df::global::ui; typedef df::reaction_product_item_improvementst improvement_product; DFHACK_PLUGIN("add-spatter"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); struct ReagentSource { int idx; @@ -390,6 +391,7 @@ static bool find_reactions(color_ostream &out) static void enable_hooks(bool enable) { + is_enabled = enable; INTERPOSE_HOOK(item_hook, isImprovable).apply(enable); INTERPOSE_HOOK(product_hook, produce).apply(enable); } diff --git a/plugins/advtools.cpp b/plugins/advtools.cpp index 5ceea69c8..ad50dbe5d 100644 --- a/plugins/advtools.cpp +++ b/plugins/advtools.cpp @@ -99,7 +99,7 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) df::nemesis_record *getPlayerNemesis(color_ostream &out, bool restore_swap); -static bool in_transient_swap = false; +DFHACK_PLUGIN_IS_ENABLED(in_transient_swap); DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { diff --git a/plugins/autoSyndrome.cpp b/plugins/autoSyndrome.cpp index 43b0b47fb..244e759f8 100644 --- a/plugins/autoSyndrome.cpp +++ b/plugins/autoSyndrome.cpp @@ -98,7 +98,7 @@ reaction_duck Next, start a new fort in a new world, build a duck workshop, then have someone become a duck. */ -bool enabled = false; +DFHACK_PLUGIN_IS_ENABLED(enabled); DFHACK_PLUGIN("autoSyndrome"); @@ -139,37 +139,45 @@ DFhackCExport command_result plugin_shutdown(color_ostream& out) { return CR_OK; }*/ +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) +{ + if (enabled == enable) + return CR_OK; + + enabled = enable; + + Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("autoSyndrome"); + if ( enabled ) { + EventManager::EventHandler handle(processJob, 5); + EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, handle, me); + } else { + EventManager::unregisterAll(me); + } + + return CR_OK; +} + command_result autoSyndrome(color_ostream& out, vector& parameters) { if ( parameters.size() > 1 ) return CR_WRONG_USAGE; - bool wasEnabled = enabled; + bool enable = false; if ( parameters.size() == 1 ) { if ( parameters[0] == "enable" ) { - enabled = true; + enable = true; } else if ( parameters[0] == "disable" ) { - enabled = false; + enable = false; } else { int32_t a = atoi(parameters[0].c_str()); if ( a < 0 || a > 1 ) return CR_WRONG_USAGE; - enabled = (bool)a; + enable = (bool)a; } } - out.print("autoSyndrome is %s\n", enabled ? "enabled" : "disabled"); - if ( enabled == wasEnabled ) - return CR_OK; - - Plugin* me = Core::getInstance().getPluginManager()->getPluginByName("autoSyndrome"); - if ( enabled ) { - EventManager::EventHandler handle(processJob, 5); - EventManager::registerListener(EventManager::EventType::JOB_COMPLETED, handle, me); - } else { - EventManager::unregisterAll(me); - } - return CR_OK; + out.print("autoSyndrome is %s\n", enable ? "enabled" : "disabled"); + return plugin_enable(out, enable); } bool maybeApply(color_ostream& out, df::syndrome* syndrome, int32_t workerId, df::unit* unit) { diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index b1293055f..738dc7c66 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -76,7 +76,7 @@ using df::global::world; * (mining, hunting, and woodcutting) need to be handled carefully to minimize churn. */ -static int enable_autolabor = 0; +DFHACK_PLUGIN_IS_ENABLED(enable_autolabor); static bool print_debug = 0; @@ -535,6 +535,7 @@ static void setOptionEnabled(ConfigFlags flag, bool on) static void cleanup_state() { + enable_autolabor = false; labor_infos.clear(); } @@ -1297,6 +1298,27 @@ void print_labor (df::unit_labor labor, color_ostream &out) } } +DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable ) +{ + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("World is not loaded: please load a game first.\n"); + return CR_FAILURE; + } + + if (enable && !enable_autolabor) + { + enable_plugin(out); + } + else if(!enable && enable_autolabor) + { + enable_autolabor = false; + setOptionEnabled(CF_ENABLED, false); + + out << "Autolabor is disabled." << endl; + } + + return CR_OK; +} command_result autolabor (color_ostream &out, std::vector & parameters) { @@ -1312,19 +1334,8 @@ command_result autolabor (color_ostream &out, std::vector & parame parameters[0] == "1" || parameters[0] == "disable")) { bool enable = (parameters[0] == "1" || parameters[0] == "enable"); - if (enable && !enable_autolabor) - { - enable_plugin(out); - } - else if(!enable && enable_autolabor) - { - enable_autolabor = false; - setOptionEnabled(CF_ENABLED, false); - out << "The plugin is disabled." << endl; - } - - return CR_OK; + return plugin_enable(out, enable); } else if (parameters.size() == 2 && parameters[0] == "haulpct") { diff --git a/plugins/automaterial.cpp b/plugins/automaterial.cpp index 16b258a02..848d02bc2 100644 --- a/plugins/automaterial.cpp +++ b/plugins/automaterial.cpp @@ -1179,11 +1179,27 @@ color_ostream_proxy console_out(Core::getInstance().getConsole()); IMPLEMENT_VMETHOD_INTERPOSE(jobutils_hook, feed); IMPLEMENT_VMETHOD_INTERPOSE(jobutils_hook, render); -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + +DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable) { - if (!gps || !INTERPOSE_HOOK(jobutils_hook, feed).apply() || !INTERPOSE_HOOK(jobutils_hook, render).apply()) - out.printerr("Could not insert jobutils hooks!\n"); + if (!gps) + return CR_FAILURE; + + if (enable != is_enabled) + { + if (!INTERPOSE_HOOK(jobutils_hook, feed).apply(enable) || + !INTERPOSE_HOOK(jobutils_hook, render).apply(enable)) + return CR_FAILURE; + + is_enabled = enable; + } + return CR_OK; +} + +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ hotkeys[construction_type::Wall] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_WALL; hotkeys[construction_type::Floor] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_FLOOR; hotkeys[construction_type::Ramp] = df::interface_key::HOTKEY_BUILDING_CONSTRUCTION_RAMP; diff --git a/plugins/autotrade.cpp b/plugins/autotrade.cpp index 62f7c05c0..2d3cf0ed2 100644 --- a/plugins/autotrade.cpp +++ b/plugins/autotrade.cpp @@ -583,7 +583,6 @@ static command_result autotrade_cmd(color_ostream &out, vector & parame return CR_OK; } - DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { switch (event) @@ -600,11 +599,30 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan return CR_OK; } -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { - if (!gps || !INTERPOSE_HOOK(trade_hook, feed).apply() || !INTERPOSE_HOOK(trade_hook, render).apply()) - out.printerr("Could not insert autotrade hooks!\n"); + if (!gps) + return CR_FAILURE; + + if (enable != is_enabled) + { + depot_info.reset(); + monitor.reset(); + + if (!INTERPOSE_HOOK(trade_hook, feed).apply(enable) || + !INTERPOSE_HOOK(trade_hook, render).apply(enable)) + return CR_FAILURE; + is_enabled = enable; + } + + return CR_OK; +} + +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ commands.push_back( PluginCommand( "autotrade", "Automatically send items in marked stockpiles to trade depot, when trading is possible.", diff --git a/plugins/buildingplan.cpp b/plugins/buildingplan.cpp index 810314bc1..70a324423 100644 --- a/plugins/buildingplan.cpp +++ b/plugins/buildingplan.cpp @@ -1153,12 +1153,29 @@ static command_result buildingplan_cmd(color_ostream &out, vector & par return CR_OK; } +DFHACK_PLUGIN_IS_ENABLED(is_enabled); -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { - if (!gps || !INTERPOSE_HOOK(buildingplan_hook, feed).apply() || !INTERPOSE_HOOK(buildingplan_hook, render).apply()) - out.printerr("Could not insert buildingplan hooks!\n"); + if (!gps) + return CR_FAILURE; + + if (enable != is_enabled) + { + planner.reset(out); + + if (!INTERPOSE_HOOK(buildingplan_hook, feed).apply(enable) || + !INTERPOSE_HOOK(buildingplan_hook, render).apply(enable)) + return CR_FAILURE; + + is_enabled = enable; + } + + return CR_OK; +} +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ commands.push_back( PluginCommand( "buildingplan", "Place furniture before it's built", diff --git a/plugins/burrows.cpp b/plugins/burrows.cpp index edcc01ecf..32a76ba0c 100644 --- a/plugins/burrows.cpp +++ b/plugins/burrows.cpp @@ -221,7 +221,8 @@ static void detect_digging(color_ostream &out) } } -static bool active = false; +DFHACK_PLUGIN_IS_ENABLED(active); + static bool auto_grow = false; static std::vector grow_burrows; diff --git a/plugins/devel/autolabor2.cpp b/plugins/devel/autolabor2.cpp index e41ba541b..621f41be0 100644 --- a/plugins/devel/autolabor2.cpp +++ b/plugins/devel/autolabor2.cpp @@ -76,7 +76,7 @@ using df::global::world; #define ARRAY_COUNT(array) (sizeof(array)/sizeof((array)[0])) -static int enable_autolabor = 0; +DFHACK_PLUGIN_IS_ENABLED(enable_autolabor); static bool print_debug = 0; @@ -1375,6 +1375,7 @@ static void setOptionEnabled(ConfigFlags flag, bool on) static void cleanup_state() { + enable_autolabor = false; labor_infos.clear(); } @@ -2384,6 +2385,27 @@ df::unit_labor lookup_labor_by_name (std::string& name) return labor; } +DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable ) +{ + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("World is not loaded: please load a game first.\n"); + return CR_FAILURE; + } + + if (enable && !enable_autolabor) + { + enable_plugin(out); + } + else if(!enable && enable_autolabor) + { + enable_autolabor = false; + setOptionEnabled(CF_ENABLED, false); + + out << "Autolabor is disabled." << endl; + } + + return CR_OK; +} command_result autolabor (color_ostream &out, std::vector & parameters) { @@ -2398,19 +2420,7 @@ command_result autolabor (color_ostream &out, std::vector & parame (parameters[0] == "enable" || parameters[0] == "disable")) { bool enable = (parameters[0] == "enable"); - if (enable && !enable_autolabor) - { - enable_plugin(out); - } - else if(!enable && enable_autolabor) - { - enable_autolabor = false; - setOptionEnabled(CF_ENABLED, false); - - out << "The plugin is disabled." << endl; - } - - return CR_OK; + return plugin_enable(out, enable); } else if (parameters.size() == 3 && (parameters[0] == "max" || parameters[0] == "priority")) diff --git a/plugins/devel/kittens.cpp b/plugins/devel/kittens.cpp index b610d4742..df0f3b947 100644 --- a/plugins/devel/kittens.cpp +++ b/plugins/devel/kittens.cpp @@ -16,6 +16,8 @@ using std::vector; using std::string; using namespace DFHack; +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + //FIXME: possible race conditions with calling kittens from the IO thread and shutdown from Core. bool shutdown_flag = false; bool final_flag = true; @@ -141,6 +143,7 @@ command_result trackmenu (color_ostream &out, vector & parameters) if(df::global::ui) { trackmenu_flg = true; + is_enabled = true; last_menu = df::global::ui->main.mode; out.print("Menu: %d\n",last_menu); return CR_OK; @@ -155,6 +158,7 @@ command_result trackmenu (color_ostream &out, vector & parameters) command_result trackpos (color_ostream &out, vector & parameters) { trackpos_flg = !trackpos_flg; + is_enabled = true; return CR_OK; } @@ -214,6 +218,7 @@ command_result ktimer (color_ostream &out, vector & parameters) // harmless potential data race here... timeLast = timeend; timering = true; + is_enabled = true; return CR_OK; } diff --git a/plugins/devel/memview.cpp b/plugins/devel/memview.cpp index a43e6a7bf..6a4baa295 100644 --- a/plugins/devel/memview.cpp +++ b/plugins/devel/memview.cpp @@ -15,6 +15,8 @@ using namespace DFHack; uint64_t timeLast=0; static tthread::mutex* mymutex=0; +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + struct memory_data { void * addr; @@ -96,6 +98,7 @@ void Deinit() { if(memdata.state==STATE_ON) { + is_enabled = false; memdata.state=STATE_OFF; delete [] memdata.buf; delete [] memdata.lbuf; @@ -140,6 +143,7 @@ command_result memview (color_ostream &out, vector & parameters) { Deinit(); memdata.state=STATE_OFF; + is_enabled = false; mymutex->unlock(); return CR_OK; } @@ -156,6 +160,7 @@ command_result memview (color_ostream &out, vector & parameters) mymutex->unlock(); return CR_OK; } + is_enabled = true; memdata.state=STATE_ON; } if(parameters.size()>1) diff --git a/plugins/devel/nestboxes.cpp b/plugins/devel/nestboxes.cpp index 8c51b57ae..f4f423515 100644 --- a/plugins/devel/nestboxes.cpp +++ b/plugins/devel/nestboxes.cpp @@ -31,7 +31,7 @@ static command_result nestboxes(color_ostream &out, vector & parameters DFHACK_PLUGIN("nestboxes"); -static bool enabled = false; +DFHACK_PLUGIN_IS_ENABLED(enabled); static void eggscan(color_ostream &out) { @@ -97,6 +97,12 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out) return CR_OK; } +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) +{ + enabled = enable; + return CR_OK; +} + static command_result nestboxes(color_ostream &out, vector & parameters) { CoreSuspender suspend; diff --git a/plugins/devel/vshook.cpp b/plugins/devel/vshook.cpp index ceec2d08d..025bb2b1d 100644 --- a/plugins/devel/vshook.cpp +++ b/plugins/devel/vshook.cpp @@ -23,6 +23,8 @@ using df::global::gps; DFHACK_PLUGIN("vshook"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + struct title_hook : df::viewscreen_titlest { typedef df::viewscreen_titlest interpose_base; @@ -37,17 +39,30 @@ struct title_hook : df::viewscreen_titlest { IMPLEMENT_VMETHOD_INTERPOSE(title_hook, render); -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable) { - if (gps) + if (!gps) + return CR_FAILURE; + + if (enable != is_enabled) { - if (!INTERPOSE_HOOK(title_hook, render).apply()) - out.printerr("Could not interpose viewscreen_titlest::render\n"); + if (!INTERPOSE_HOOK(title_hook, render).apply(enable)) + return CR_FAILURE; + + is_enabled = enable; } return CR_OK; } +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ + // DON'T DO THIS IN NON-EXAMPLE PLUGINS + plugin_enable(out, true); + + return CR_OK; +} + DFhackCExport command_result plugin_shutdown ( color_ostream &out ) { INTERPOSE_HOOK(title_hook, render).remove(); diff --git a/plugins/dwarfmonitor.cpp b/plugins/dwarfmonitor.cpp index 6ff62253a..883f0a002 100644 --- a/plugins/dwarfmonitor.cpp +++ b/plugins/dwarfmonitor.cpp @@ -1179,11 +1179,15 @@ IMPLEMENT_VMETHOD_INTERPOSE(dwarf_monitor_hook, feed); IMPLEMENT_VMETHOD_INTERPOSE(dwarf_monitor_hook, render); DFHACK_PLUGIN("dwarfmonitor"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); static bool set_monitoring_mode(const string &mode, const bool &state) { bool mode_recognized = false; + if (!is_enabled) + return false; + if (mode == "work" || mode == "all") { mode_recognized = true; @@ -1201,6 +1205,24 @@ static bool set_monitoring_mode(const string &mode, const bool &state) return mode_recognized; } +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) +{ + if (!gps) + return CR_FAILURE; + + if (is_enabled != enable) + { + if (!INTERPOSE_HOOK(dwarf_monitor_hook, feed).apply(enable) || + !INTERPOSE_HOOK(dwarf_monitor_hook, render).apply(enable)) + return CR_FAILURE; + + reset(); + is_enabled = enable; + } + + return CR_OK; +} + static command_result dwarfmonitor_cmd(color_ostream &out, vector & parameters) { bool show_help = false; @@ -1222,6 +1244,9 @@ static command_result dwarfmonitor_cmd(color_ostream &out, vector & par } else if ((cmd == 'e' || cmd == 'E') && !mode.empty()) { + if (!is_enabled) + plugin_enable(out, true); + if (set_monitoring_mode(mode, true)) { out << "Monitoring enabled: " << mode << endl; @@ -1257,9 +1282,6 @@ static command_result dwarfmonitor_cmd(color_ostream &out, vector & par DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { - if (!gps || !INTERPOSE_HOOK(dwarf_monitor_hook, feed).apply() || !INTERPOSE_HOOK(dwarf_monitor_hook, render).apply()) - out.printerr("Could not insert dwarfmonitor hooks!\n"); - activity_labels[JOB_IDLE] = "Idle"; activity_labels[JOB_MILITARY] = "Military Duty"; activity_labels[JOB_LEISURE] = "Leisure"; diff --git a/plugins/fastdwarf.cpp b/plugins/fastdwarf.cpp index 6292fc85e..2f7d674eb 100644 --- a/plugins/fastdwarf.cpp +++ b/plugins/fastdwarf.cpp @@ -19,6 +19,8 @@ using df::global::world; // dfhack interface DFHACK_PLUGIN("fastdwarf"); +DFHACK_PLUGIN_IS_ENABLED(active); + static bool enable_fastdwarf = false; static bool enable_teledwarf = false; @@ -152,6 +154,8 @@ static command_result fastdwarf (color_ostream &out, vector & parameter return CR_WRONG_USAGE; } + active = enable_fastdwarf || enable_teledwarf; + out.print("Current state: fast = %d, teleport = %d.\n", (df::global::debug_turbospeed && *df::global::debug_turbospeed) ? 2 : (enable_fastdwarf ? 1 : 0), enable_teledwarf ? 1 : 0); @@ -159,6 +163,17 @@ static command_result fastdwarf (color_ostream &out, vector & parameter return CR_OK; } +DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable ) +{ + if (active != enable) + { + active = enable_fastdwarf = enable; + enable_teledwarf = false; + } + + return CR_OK; +} + DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { commands.push_back(PluginCommand("fastdwarf", diff --git a/plugins/fix-armory.cpp b/plugins/fix-armory.cpp index 5a4821b4b..a217c1a04 100644 --- a/plugins/fix-armory.cpp +++ b/plugins/fix-armory.cpp @@ -698,7 +698,7 @@ static void try_store_ammo(df::squad *squad) } } -static bool is_enabled = false; +DFHACK_PLUGIN_IS_ENABLED(is_enabled); DFhackCExport command_result plugin_onupdate(color_ostream &out, state_change_event event) { @@ -810,6 +810,21 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan return CR_OK; } +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) +{ + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("World is not loaded: please load a game first.\n"); + return CR_FAILURE; + } + + if (enable) + enable_plugin(out); + else + disable_plugin(out); + + return CR_OK; +} + static command_result fix_armory(color_ostream &out, vector ¶meters) { CoreSuspender suspend; @@ -820,13 +835,9 @@ static command_result fix_armory(color_ostream &out, vector ¶meters string cmd = parameters[0]; if (cmd == "enable") - { - enable_plugin(out); - } + return plugin_enable(out, true); else if (cmd == "disable") - { - disable_plugin(out); - } + return plugin_enable(out, false); else return CR_WRONG_USAGE; diff --git a/plugins/follow.cpp b/plugins/follow.cpp index 5c14780a3..56a7cf3b2 100644 --- a/plugins/follow.cpp +++ b/plugins/follow.cpp @@ -24,6 +24,7 @@ int32_t prevX, prevY, prevZ; uint8_t prevMenuWidth; DFHACK_PLUGIN("follow"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { @@ -53,6 +54,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan followedUnit = 0; prevX=prevY=prevZ = -1; prevMenuWidth = 0; + is_enabled = false; break; default: break; @@ -97,6 +99,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) } else if((prevX != x || prevY != y || prevZ != z) && prevMenuWidth == menu_width) //User has manually moved the window, stop following the unit { + is_enabled = false; followedUnit = 0; prevX=prevY=prevZ = -1; prevMenuWidth = 0; @@ -146,6 +149,7 @@ command_result follow (color_ostream &out, std::vector & parameter followedUnit = Gui::getSelectedUnit(out); if (followedUnit) { + is_enabled = true; std::ostringstream ss; ss << "Unpause to begin following " << df::global::world->raws.creatures.all[followedUnit->race]->name[0]; if (followedUnit->name.has_name) ss << " " << followedUnit->name.first_name; @@ -153,5 +157,6 @@ command_result follow (color_ostream &out, std::vector & parameter out.print(ss.str().c_str()); } else followedUnit = 0; + is_enabled = (followedUnit != NULL); return CR_OK; } diff --git a/plugins/infiniteSky.cpp b/plugins/infiniteSky.cpp index 8750e5435..6405a5cf0 100644 --- a/plugins/infiniteSky.cpp +++ b/plugins/infiniteSky.cpp @@ -71,7 +71,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan */ static size_t constructionSize = 0; -static bool enabled = false; +DFHACK_PLUGIN_IS_ENABLED(enabled); void doInfiniteSky(color_ostream& out, int32_t howMany); DFhackCExport command_result plugin_onupdate ( color_ostream &out ) @@ -155,6 +155,12 @@ void doInfiniteSky(color_ostream& out, int32_t howMany) { } +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) +{ + enabled = enable; + return CR_OK; +} + command_result infiniteSky (color_ostream &out, std::vector & parameters) { if ( parameters.size() > 1 ) diff --git a/plugins/manipulator.cpp b/plugins/manipulator.cpp index 88dc61726..57f134447 100644 --- a/plugins/manipulator.cpp +++ b/plugins/manipulator.cpp @@ -1209,10 +1209,27 @@ IMPLEMENT_VMETHOD_INTERPOSE(unitlist_hook, render); DFHACK_PLUGIN("manipulator"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) +{ + if (!gps) + return CR_FAILURE; + + if (enable != is_enabled) + { + if (!INTERPOSE_HOOK(unitlist_hook, feed).apply(enable) || + !INTERPOSE_HOOK(unitlist_hook, render).apply(enable)) + return CR_FAILURE; + + is_enabled = enable; + } + + return CR_OK; +} + DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) { - if (!gps || !INTERPOSE_HOOK(unitlist_hook, feed).apply() || !INTERPOSE_HOOK(unitlist_hook, render).apply()) - out.printerr("Could not insert Dwarf Manipulator hooks!\n"); return CR_OK; } diff --git a/plugins/misery.cpp b/plugins/misery.cpp index a0c0a2390..cc69115db 100644 --- a/plugins/misery.cpp +++ b/plugins/misery.cpp @@ -15,6 +15,8 @@ using namespace std; using namespace DFHack; +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + static int factor = 1; static map processedThoughtCountTable; @@ -38,6 +40,7 @@ DFhackCExport command_result plugin_onupdate(color_ostream& out) { if ( wasLoaded ) { //we just unloaded the game: clear all data factor = 1; + is_enabled = false; processedThoughtCountTable.clear(); fakeThoughts.clear(); wasLoaded = false; @@ -138,6 +141,17 @@ DFhackCExport command_result plugin_init(color_ostream& out, vector& parameters) { if ( !df::global::world || !df::global::world->map.block_index ) { out.printerr("misery can only be enabled in fortress mode with a fully-loaded game.\n"); @@ -153,15 +167,18 @@ command_result misery(color_ostream &out, vector& parameters) { return CR_WRONG_USAGE; } factor = 1; + is_enabled = false; return CR_OK; } else if ( parameters[0] == "enable" ) { + is_enabled = true; factor = 2; if ( parameters.size() == 2 ) { - factor = atoi(parameters[1].c_str()); - if ( factor < 1 ) { + int a = atoi(parameters[1].c_str()); + if ( a <= 1 ) { out.printerr("Second argument must be a positive integer.\n"); return CR_WRONG_USAGE; } + factor = a; } } else if ( parameters[0] == "clear" ) { for ( size_t a = 0; a < fakeThoughts.size(); a++ ) { @@ -176,6 +193,7 @@ command_result misery(color_ostream &out, vector& parameters) { return CR_WRONG_USAGE; } factor = a; + is_enabled = factor > 1; } return CR_OK; diff --git a/plugins/mode.cpp b/plugins/mode.cpp index d18cf7529..66fb488a6 100644 --- a/plugins/mode.cpp +++ b/plugins/mode.cpp @@ -33,12 +33,6 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) return CR_OK; } -DFhackCExport command_result plugin_onupdate ( color_ostream &out ) -{ - // add tracking here - return CR_OK; -} - void printCurrentModes(t_gamemodes gm, Console & con) { con << "Current game type:\t" << gm.g_type << " ("; diff --git a/plugins/mousequery.cpp b/plugins/mousequery.cpp index dfb89f6f4..97e703752 100644 --- a/plugins/mousequery.cpp +++ b/plugins/mousequery.cpp @@ -239,12 +239,28 @@ struct mousequery_hook : public df::viewscreen_dwarfmodest IMPLEMENT_VMETHOD_INTERPOSE(mousequery_hook, feed); DFHACK_PLUGIN("mousequery"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable) { - if (!gps || !INTERPOSE_HOOK(mousequery_hook, feed).apply()) - out.printerr("Could not insert mousequery hooks!\n"); + if (!gps) + return CR_FAILURE; + + if (is_enabled != enable) + { + last_x = last_y = last_z = -1; + + if (!INTERPOSE_HOOK(mousequery_hook, feed).apply(enable)) + return CR_FAILURE; + + is_enabled = enable; + } + + return CR_OK; +} +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ last_x = last_y = last_z = -1; return CR_OK; diff --git a/plugins/power-meter.cpp b/plugins/power-meter.cpp index efda2c590..0f8135eed 100644 --- a/plugins/power-meter.cpp +++ b/plugins/power-meter.cpp @@ -160,7 +160,7 @@ IMPLEMENT_VMETHOD_INTERPOSE(trap_hook, getName); IMPLEMENT_VMETHOD_INTERPOSE(trap_hook, updateAction); IMPLEMENT_VMETHOD_INTERPOSE(trap_hook, drawBuilding); -static bool enabled = false; +DFHACK_PLUGIN_IS_ENABLED(enabled); static void enable_hooks(bool enable) { diff --git a/plugins/rename.cpp b/plugins/rename.cpp index bffb33111..fdebad1d3 100644 --- a/plugins/rename.cpp +++ b/plugins/rename.cpp @@ -55,6 +55,7 @@ DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_chan static command_result rename(color_ostream &out, vector & parameters); DFHACK_PLUGIN("rename"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); DFhackCExport command_result plugin_init (color_ostream &out, std::vector &commands) { @@ -174,6 +175,9 @@ KNOWN_BUILDINGS static bool enable_building_rename(char code, bool enable) { + if (enable) + is_enabled = true; + if (code == 'Z') INTERPOSE_HOOK(dwarf_render_zone_hook, render).apply(enable); @@ -189,6 +193,7 @@ KNOWN_BUILDINGS static void disable_building_rename() { + is_enabled = false; INTERPOSE_HOOK(dwarf_render_zone_hook, render).remove(); #define BUILDING(code, cname, tag) \ diff --git a/plugins/resume.cpp b/plugins/resume.cpp index e697a133f..9d37baac7 100644 --- a/plugins/resume.cpp +++ b/plugins/resume.cpp @@ -107,7 +107,7 @@ struct SuspendedBuilding } }; -static bool enabled = false; +DFHACK_PLUGIN_IS_ENABLED(enabled); static bool buildings_scanned = false; static vector suspended_buildings, resumed_buildings; @@ -234,6 +234,23 @@ struct resume_hook : public df::viewscreen_dwarfmodest IMPLEMENT_VMETHOD_INTERPOSE(resume_hook, render); +DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable) +{ + if (!gps) + return CR_FAILURE; + + if (enabled != enable) + { + clear_scanned(); + + if (!INTERPOSE_HOOK(resume_hook, render).apply(enable)) + return CR_FAILURE; + + enabled = enable; + } + + return CR_OK; +} static command_result resume_cmd(color_ostream &out, vector & parameters) { @@ -251,12 +268,12 @@ static command_result resume_cmd(color_ostream &out, vector & parameter } else if (cmd == 's') { - enabled = true; + plugin_enable(out, true); out << "Overlay enabled" << endl; } else if (cmd == 'h') { - enabled = false; + plugin_enable(out, false); out << "Overlay disabled" << endl; } else if (cmd == 'a') @@ -275,12 +292,8 @@ static command_result resume_cmd(color_ostream &out, vector & parameter return CR_OK; } - DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - if (!gps || !INTERPOSE_HOOK(resume_hook, render).apply()) - out.printerr("Could not insert resume hooks!\n"); - commands.push_back( PluginCommand( "resume", "A plugin to help display and resume suspended constructions conveniently", diff --git a/plugins/reveal.cpp b/plugins/reveal.cpp index 8db7c9112..2c51295a8 100644 --- a/plugins/reveal.cpp +++ b/plugins/reveal.cpp @@ -85,6 +85,8 @@ DFhackCExport command_result plugin_init ( color_ostream &out, vector & parameters) nopause_state = 0; else nopause_state = 1; + is_active = nopause_state || (revealed == REVEALED); out.print("nopause %sactivated.\n", (nopause_state ? "" : "de")); } else @@ -237,6 +240,7 @@ command_result reveal(color_ostream &out, vector & params) else revealed = DEMON_REVEALED; } + is_active = nopause_state || (revealed == REVEALED); con.print("Map revealed.\n"); if(!no_hell) con.print("Unpausing can unleash the forces of hell, so it has been temporarily disabled.\n"); @@ -296,6 +300,7 @@ command_result unreveal(color_ostream &out, vector & params) // give back memory. hidesaved.clear(); revealed = NOT_REVEALED; + is_active = nopause_state || (revealed == REVEALED); con.print("Map hidden!\n"); return CR_OK; } @@ -490,6 +495,7 @@ command_result revforget(color_ostream &out, vector & params) // give back memory. hidesaved.clear(); revealed = NOT_REVEALED; + is_active = nopause_state || (revealed == REVEALED); con.print("Reveal data forgotten!\n"); return CR_OK; } diff --git a/plugins/search.cpp b/plugins/search.cpp index 541fc9506..bb903b408 100644 --- a/plugins/search.cpp +++ b/plugins/search.cpp @@ -1609,6 +1609,7 @@ IMPLEMENT_HOOKS(df::viewscreen_dwarfmodest, burrow_search); DFHACK_PLUGIN("search"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); #define SEARCH_HOOKS \ HOOK_ACTION(unitlist_search_hook) \ @@ -1624,15 +1625,28 @@ DFHACK_PLUGIN("search"); HOOK_ACTION(burrow_search_hook) \ HOOK_ACTION(stockpile_search_hook) -DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) +DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable) { + if (!gps || !gview) + return CR_FAILURE; + + if (is_enabled != enable) + { #define HOOK_ACTION(hook) \ - !INTERPOSE_HOOK(hook, feed).apply() || \ - !INTERPOSE_HOOK(hook, render).apply() || + !INTERPOSE_HOOK(hook, feed).apply(enable) || \ + !INTERPOSE_HOOK(hook, render).apply(enable) || + + if (SEARCH_HOOKS 0) + return CR_FAILURE; + + is_enabled = enable; + } - if (!gps || !gview || SEARCH_HOOKS 0) - out.printerr("Could not insert Search hooks!\n"); + return CR_OK; +} +DFhackCExport command_result plugin_init ( color_ostream &out, vector &commands) +{ #undef HOOK_ACTION const string a[] = {"Meager Quarters", "Modest Quarters", "Quarters", "Decent Quarters", "Fine Quarters", "Great Bedroom", "Grand Bedroom", "Royal Bedroom"}; diff --git a/plugins/seedwatch.cpp b/plugins/seedwatch.cpp index ae87e4381..19fa9b154 100755 --- a/plugins/seedwatch.cpp +++ b/plugins/seedwatch.cpp @@ -22,7 +22,7 @@ using namespace df::enums; using df::global::world; const int buffer = 20; // seed number buffer - 20 is reasonable -bool running = false; // whether seedwatch is counting the seeds or not +DFHACK_PLUGIN_IS_ENABLED(running); // whether seedwatch is counting the seeds or not // abbreviations for the standard plants map abbreviations; @@ -96,6 +96,12 @@ string searchAbbreviations(string in) } }; +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) +{ + running = enable; + return CR_OK; +} + command_result df_seedwatch(color_ostream &out, vector& parameters) { CoreSuspender suspend; diff --git a/plugins/siege-engine.cpp b/plugins/siege-engine.cpp index 4b2060e99..2d6f5fcdd 100644 --- a/plugins/siege-engine.cpp +++ b/plugins/siege-engine.cpp @@ -1766,7 +1766,7 @@ DFHACK_PLUGIN_LUA_COMMANDS { DFHACK_LUA_END }; -static bool is_enabled = false; +DFHACK_PLUGIN_IS_ENABLED(is_enabled); static void enable_hooks(bool enable) { @@ -1809,6 +1809,25 @@ static void clear_caches(color_ostream &out) } } +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) +{ + if (gamemode && *gamemode != game_mode::DWARF) + return CR_FAILURE; + + if (enable != is_enabled) + { + if (enable) + enable_plugin(); + else + { + World::DeletePersistentData(World::GetPersistentData("siege-engine/enabled")); + enable_hooks(false); + } + } + + return CR_OK; +} + DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) { switch (event) { diff --git a/plugins/steam-engine.cpp b/plugins/steam-engine.cpp index 01d1dbd58..e3cd9b529 100644 --- a/plugins/steam-engine.cpp +++ b/plugins/steam-engine.cpp @@ -950,8 +950,12 @@ static bool find_engines(color_ostream &out) return !engines.empty(); } +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + static void enable_hooks(bool enable) { + is_enabled = enable; + INTERPOSE_HOOK(liquid_hook, getItemDescription).apply(enable); INTERPOSE_HOOK(liquid_hook, adjustTemperature).apply(enable); INTERPOSE_HOOK(liquid_hook, checkTemperatureDamage).apply(enable); diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index b70fd8b0c..810cc46fe 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -402,7 +402,8 @@ public: * GLOBAL VARIABLES * ******************************/ -static bool enabled = false; +DFHACK_PLUGIN_IS_ENABLED(enabled); + static PersistentDataItem config; static int last_tick_frame_count = 0; @@ -1387,10 +1388,13 @@ static void update_data_structures(color_ostream &out) * LUA API * *************/ -static bool isEnabled() { return enabled; } - -static void setEnabled(color_ostream &out, bool enable) +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) { + if (!Core::getInstance().isWorldLoaded()) { + out.printerr("World is not loaded: please load a game first.\n"); + return CR_FAILURE; + } + if (enable && !enabled) { enable_plugin(out); @@ -1401,6 +1405,8 @@ static void setEnabled(color_ostream &out, bool enable) setOptionEnabled(CF_ENABLED, false); stop_protect(out); } + + return CR_OK; } static void push_count_history(lua_State *L, ItemConstraint *icv) @@ -1591,8 +1597,6 @@ static int getCountHistory(lua_State *L) DFHACK_PLUGIN_LUA_FUNCTIONS { - DFHACK_LUA_FUNCTION(isEnabled), - DFHACK_LUA_FUNCTION(setEnabled), DFHACK_LUA_FUNCTION(deleteConstraint), DFHACK_LUA_END }; @@ -1767,10 +1771,10 @@ static command_result workflow_cmd(color_ostream &out, vector & paramet { bool enable = (cmd == "enable"); if (enable) - setEnabled(out, true); + plugin_enable(out, true); else if (parameters.size() == 1) { - setEnabled(out, false); + plugin_enable(out, false); out << "The plugin is disabled." << endl; return CR_OK; @@ -1805,7 +1809,10 @@ static command_result workflow_cmd(color_ostream &out, vector & paramet } if (!enabled) - out << "Note: the plugin is not enabled." << endl; + { + out.printerr("Error: the plugin is not enabled.\n"); + return CR_WRONG_USAGE; + } if (cmd == "jobs") { diff --git a/plugins/zone.cpp b/plugins/zone.cpp index 5649da252..0271cb75f 100644 --- a/plugins/zone.cpp +++ b/plugins/zone.cpp @@ -88,6 +88,10 @@ command_result df_autobutcher(color_ostream &out, vector & parameters); DFHACK_PLUGIN("zone"); +DFHACK_PLUGIN_IS_ENABLED(is_enabled); + +DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable); + const string zone_help = "Allows easier management of pens/pastures, pits and cages.\n" "Options:\n" @@ -3015,6 +3019,7 @@ command_result df_autobutcher(color_ostream &out, vector & parameters) } else if (p == "start") { + plugin_enable(out, true); enable_autobutcher = true; start_autobutcher(out); return autoButcher(out, verbose); @@ -3485,6 +3490,7 @@ command_result autoButcher( color_ostream &out, bool verbose = false ) command_result start_autobutcher(color_ostream &out) { + plugin_enable(out, true); enable_autobutcher = true; if (!config_autobutcher.isValid()) @@ -3545,6 +3551,7 @@ command_result init_autobutcher(color_ostream &out) if(!enable_autobutcher) return CR_OK; + plugin_enable(out, true); // read watchlist from save std::vector items; @@ -3579,6 +3586,7 @@ command_result cleanup_autobutcher(color_ostream &out) command_result start_autonestbox(color_ostream &out) { + plugin_enable(out, true); enable_autonestbox = true; if (!config_autonestbox.isValid()) @@ -3620,6 +3628,8 @@ command_result init_autonestbox(color_ostream &out) sleep_autonestbox = config_autonestbox.ival(1); } } + if (enable_autonestbox) + plugin_enable(out, true); return CR_OK; } @@ -3856,6 +3866,9 @@ static void autobutcher_setEnabled(color_ostream &out, bool enable) config_autobutcher.ival(0) = enable_autobutcher; out << "Autobutcher stopped." << endl; } + + if (enable) + plugin_enable(out, true); } static void autowatch_setEnabled(color_ostream &out, bool enable) @@ -4455,12 +4468,25 @@ IMPLEMENT_VMETHOD_INTERPOSE(zone_hook, feed); IMPLEMENT_VMETHOD_INTERPOSE(zone_hook, render); //END zone filters +DFhackCExport command_result plugin_enable ( color_ostream &out, bool enable) +{ + if (!gps) + return CR_FAILURE; + + if (enable != is_enabled) + { + if (!INTERPOSE_HOOK(zone_hook, feed).apply(enable) || + !INTERPOSE_HOOK(zone_hook, render).apply(enable)) + return CR_FAILURE; + + is_enabled = enable; + } + + return CR_OK; +} DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { - if (!gps || !INTERPOSE_HOOK(zone_hook, feed).apply() || !INTERPOSE_HOOK(zone_hook, render).apply()) - out.printerr("Could not insert jobutils hooks!\n"); - commands.push_back(PluginCommand( "zone", "manage activity zones.", df_zone, false, From 9e81d27cd13c43a7df353b6cd65c0a396eaecb55 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 30 Sep 2013 19:46:39 +0400 Subject: [PATCH 24/34] Implement the same random generator as DF uses for DFHack. --- Lua API.rst | 45 ++++++++++ library/CMakeLists.txt | 2 + library/LuaApi.cpp | 147 +++++++++++++++++++++++++++++++ library/include/modules/Random.h | 107 ++++++++++++++++++++++ library/lua/dfhack.lua | 6 ++ library/modules/Random.cpp | 147 +++++++++++++++++++++++++++++++ 6 files changed, 454 insertions(+) create mode 100644 library/include/modules/Random.h create mode 100644 library/modules/Random.cpp diff --git a/Lua API.rst b/Lua API.rst index 6d4e6b51b..42d9953e8 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -717,6 +717,51 @@ Functions: Checks if the material matches job_material_category or job_item. Accept dfhack_material_category auto-assign table. +Random number generation +------------------------ + +* ``dfhack.random.new([seed[,perturb_count]])`` + + Creates a new random number generator object. Without any + arguments, the object is initialized using current time. + Otherwise, the seed must be either a non-negative integer, + or a list of such integers. The second argument may specify + the number of additional randomization steps performed to + improve the initial state. + +* ``rng:init([seed[,perturb_count]])`` + + Re-initializes an already existing random number generator object. + +* ``rng:random([limit])`` + + Returns a random integer. If ``limit`` is specified, the value + is in the range [0, limit); otherwise it uses the whole 32-bit + unsigned integer range. + +* ``rng:drandom()`` + + Returns a random floating-point number in the range [0,1). + +* ``rng:drandom0()`` + + Returns a random floating-point number in the range (0,1). + +* ``rng:drandom1()`` + + Returns a random floating-point number in the range [0,1]. + +* ``rng:unitrandom()`` + + Returns a random floating-point number in the range [-1,1]. + +* ``rng:unitvector([size])`` + + Returns multiple values that form a random vector of length 1, + uniformly distributed over the corresponding sphere surface. + The default size is 3. + + C++ function wrappers ===================== diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 64dafd542..6df69fe27 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -120,6 +120,7 @@ include/modules/Maps.h include/modules/MapCache.h include/modules/Materials.h include/modules/Notes.h +include/modules/Random.h include/modules/Screen.h include/modules/Translation.h include/modules/Vermin.h @@ -141,6 +142,7 @@ modules/kitchen.cpp modules/Maps.cpp modules/Materials.cpp modules/Notes.cpp +modules/Random.cpp modules/Screen.cpp modules/Translation.cpp modules/Vermin.cpp diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index a4ebbea5b..3b9a8d6d6 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -51,6 +51,7 @@ distribution. #include "modules/Burrows.h" #include "modules/Buildings.h" #include "modules/Constructions.h" +#include "modules/Random.h" #include "LuaWrapper.h" #include "LuaTools.h" @@ -92,6 +93,7 @@ using namespace DFHack; using namespace DFHack::LuaWrapper; using Screen::Pen; +using Random::MersenneRNG; void dfhack_printerr(lua_State *S, const std::string &str); @@ -1035,6 +1037,150 @@ static void OpenPen(lua_State *state) lua_pop(state, 1); } +/******************** + * Random generator * + ********************/ + +static int DFHACK_RANDOM_TOKEN = 0; + +static MersenneRNG *check_random_native(lua_State *L, int index) +{ + lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_RANDOM_TOKEN); + + if (!lua_getmetatable(L, index) || !lua_rawequal(L, -1, -2)) + luaL_argerror(L, index, "not a random generator object"); + + lua_pop(L, 2); + + return (MersenneRNG*)lua_touserdata(L, index); +} + +static int dfhack_random_init(lua_State *L) +{ + lua_settop(L, 3); + + MersenneRNG *prng = check_random_native(L, 1); + + if (lua_isnil(L, 2)) + prng->init(); + else + { + std::vector data; + int tcnt = luaL_optint(L, 3, 1); + + if (lua_isnumber(L, 2)) + data.push_back(lua_tounsigned(L, 2)); + else if (lua_istable(L, 2)) + { + int cnt = lua_rawlen(L, 2); + if (cnt <= 0) + luaL_argerror(L, 2, "empty list in dfhack.random.init"); + + for (int i = 1; i <= cnt; i++) + { + lua_rawgeti(L, 2, i); + if (!lua_isnumber(L, -1)) + luaL_argerror(L, 2, "not a number in dfhack.random.init argument"); + + data.push_back(lua_tounsigned(L, -1)); + lua_pop(L, 1); + } + } + else + luaL_argerror(L, 2, "dfhack.random.init argument not number or table"); + + prng->init(data.data(), data.size(), tcnt); + } + + lua_settop(L, 1); + return 1; +} + +static int dfhack_random_new(lua_State *L) +{ + void *pdata = lua_newuserdata(L, sizeof(MersenneRNG)); + + lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_RANDOM_TOKEN); + lua_setmetatable(L, -2); + + new (pdata) MersenneRNG(); + + lua_insert(L, 1); + return dfhack_random_init(L); +} + +static int dfhack_random_random(lua_State *L) +{ + MersenneRNG *prng = check_random_native(L, 1); + + lua_settop(L, 2); + if (lua_gettop(L) < 2 || lua_isnil(L, 2)) + lua_pushunsigned(L, prng->random()); + else + lua_pushunsigned(L, prng->random(luaL_optunsigned(L, 2, 0))); + return 1; +} + +static int dfhack_random_drandom(lua_State *L) +{ + lua_pushnumber(L, check_random_native(L, 1)->drandom()); + return 1; +} +static int dfhack_random_drandom0(lua_State *L) +{ + lua_pushnumber(L, check_random_native(L, 1)->drandom0()); + return 1; +} +static int dfhack_random_drandom1(lua_State *L) +{ + lua_pushnumber(L, check_random_native(L, 1)->drandom1()); + return 1; +} +static int dfhack_random_unitrandom(lua_State *L) +{ + lua_pushnumber(L, check_random_native(L, 1)->unitrandom()); + return 1; +} +static int dfhack_random_unitvector(lua_State *L) +{ + MersenneRNG *prng = check_random_native(L, 1); + int size = luaL_optint(L, 2, 3); + if (size <= 0 || size > 32) + luaL_argerror(L, 2, "vector size must be positive"); + luaL_checkstack(L, size, "not enough stack in dfhack.random.unitvector"); + + std::vector buf(size); + prng->unitvector(buf.data(), size); + + for (int i = 0; i < size; i++) + lua_pushnumber(L, buf[i]); + return size; +} + +static const luaL_Reg dfhack_random_funcs[] = { + { "new", dfhack_random_new }, + { "init", dfhack_random_init }, + { "random", dfhack_random_random }, + { "drandom", dfhack_random_drandom }, + { "drandom0", dfhack_random_drandom0 }, + { "drandom1", dfhack_random_drandom1 }, + { "unitrandom", dfhack_random_unitrandom }, + { "unitvector", dfhack_random_unitvector }, + { NULL, NULL } +}; + +static void OpenRandom(lua_State *state) +{ + luaL_getsubtable(state, lua_gettop(state), "random"); + + lua_dup(state); + lua_rawsetp(state, LUA_REGISTRYINDEX, &DFHACK_RANDOM_TOKEN); + + luaL_setfuncs(state, dfhack_random_funcs, 0); + + lua_pop(state, 1); +} + /************************ * Wrappers for C++ API * ************************/ @@ -2023,6 +2169,7 @@ void OpenDFHackApi(lua_State *state) OpenPersistent(state); OpenMatinfo(state); OpenPen(state); + OpenRandom(state); LuaWrapper::SetFunctionWrappers(state, dfhack_module); OpenModule(state, "gui", dfhack_gui_module); diff --git a/library/include/modules/Random.h b/library/include/modules/Random.h new file mode 100644 index 000000000..c6f9a3f6a --- /dev/null +++ b/library/include/modules/Random.h @@ -0,0 +1,107 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#pragma once +#ifndef CL_MOD_RANDOM +#define CL_MOD_RANDOM +/** + * \defgroup grp_translation Translation: DF word tables and name translation/reading + * @ingroup grp_modules + */ + +#include "Export.h" +#include "Module.h" +#include "Types.h" + +#include "DataDefs.h" + +namespace DFHack +{ +namespace Random +{ + class DFHACK_EXPORT MersenneRNG + { + static const unsigned MT_LEN = 624; + + unsigned mt_index; + uint32_t mt_buffer[MT_LEN]; + + void twist(); + void prefill(unsigned step, int twist_cnt); + + public: + void init(const uint32_t *pseed, unsigned cnt, int twist_cnt = 1); + + void init(); // uses time + void init(uint32_t seed, int twist_cnt = 1) { init(&seed, 1, twist_cnt); } + + // [0, 2^32) + uint32_t random() { + if (mt_index >= MT_LEN) twist(); + return mt_buffer[mt_index++]; + } + // [0, limit) + uint32_t random(uint32_t limit) { + return uint32_t(uint64_t(random())*limit >> 32); + } + // (0, 1) + double drandom0() { + return (double(random())+1)/4294967297.0; + } + // [0, 1) + double drandom() { + return double(random())/4294967296.0; + } + // [0, 1] + double drandom1() { + return double(random())/4294967295.0; + } + // [-1, 1] + double unitrandom() { + return drandom1()*2.0 - 1.0; + } + + // Two exact replicas of functions in DF code + int32_t df_trandom(uint32_t max=2147483647LU); + int32_t df_loadtrandom(uint32_t max=2147483647LU); + + template + void unitvector(T *p, int size); + + template + void permute(T *p, int size) { + while(size > 1) + { + int j = random(size--); + T c = p[j]; p[j] = p[size]; p[size] = c; + } + } + }; + + extern template void MersenneRNG::unitvector(float *p, int size); + extern template void MersenneRNG::unitvector(double *p, int size); + +} +} +#endif diff --git a/library/lua/dfhack.lua b/library/lua/dfhack.lua index 1354701ff..def48ea88 100644 --- a/library/lua/dfhack.lua +++ b/library/lua/dfhack.lua @@ -220,6 +220,12 @@ function dfhack.matinfo:__tostring() return "" end +dfhack.random.__index = dfhack.random + +function dfhack.random:__tostring() + return "" +end + function dfhack.maps.getSize() local map = df.global.world.map return map.x_count_block, map.y_count_block, map.z_count_block diff --git a/library/modules/Random.cpp b/library/modules/Random.cpp new file mode 100644 index 000000000..5030d1449 --- /dev/null +++ b/library/modules/Random.cpp @@ -0,0 +1,147 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2012 Petr Mrázek (peterix@gmail.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "Internal.h" + +#include +#include +#include +using namespace std; + +#include "modules/Random.h" +#include "VersionInfo.h" +#include "MemAccess.h" +#include "Types.h" +#include "ModuleFactory.h" +#include "Core.h" +#include "Error.h" + +#include + +using namespace DFHack; +using namespace df::enums; + +using namespace DFHack::Random; + +//public domain RNG stuff by Michael Brundage +//modified to be compatible with the version in DF + +#define MT_IA 397 +#define MT_IB (MT_LEN - MT_IA) +#define UPPER_MASK 0x80000000 +#define LOWER_MASK 0x7FFFFFFF +#define MATRIX_A 0x9908B0DF +#define TWIST(b,i,j) ((b)[i] & UPPER_MASK) | ((b)[j] & LOWER_MASK) +#define MAGIC(s) (((s)&1)*MATRIX_A) + +void MersenneRNG::twist() +{ + uint32_t *b = mt_buffer; + uint32_t s; + unsigned i; + + i = 0; + for (; i < MT_IB; i++) { + s = TWIST(b, i, i+1); + b[i] = b[i + MT_IA] ^ (s >> 1) ^ MAGIC(s); + } + for (; i < MT_LEN-1; i++) { + s = TWIST(b, i, i+1); + b[i] = b[i - MT_IB] ^ (s >> 1) ^ MAGIC(s); + } + + s = TWIST(b, MT_LEN-1, 0); + b[MT_LEN-1] = b[MT_IA-1] ^ (s >> 1) ^ MAGIC(s); + + mt_index = 0; +} + +void MersenneRNG::prefill(unsigned step, int twist_cnt) +{ + for(unsigned i=step;i>30)) + i; + } + + mt_index = 0; + + for(int j=0;jgetTickCount(), 20); +} + +void MersenneRNG::init(const uint32_t *pseed, unsigned cnt, int twist_cnt) +{ + memcpy(mt_buffer, pseed, cnt*sizeof(uint32_t)); + + prefill(cnt, twist_cnt); +} + +int32_t MersenneRNG::df_trandom(uint32_t max) +{ + if(max<=1)return 0; + uint32_t seed=random(); + seed=seed%2147483647LU; + seed=seed/((2147483647LU/max)+1); + + return((int32_t)seed); +} + +int32_t MersenneRNG::df_loadtrandom(uint32_t max) +{ + uint32_t seed=random(); + seed=seed%max; + + return((int32_t)seed); +} + +template +void MersenneRNG::unitvector(T *p, int size) +{ + for (;;) + { + T rsqr = 0; + for (int i = 0; i < size; i++) + { + p[i] = (T)unitrandom(); + rsqr += p[i]*p[i]; + } + + if (rsqr > 0 && rsqr <= 1) + { + rsqr = std::sqrt(rsqr); + for (int i = 0; i < size; i++) + p[i] /= rsqr; + break; + } + } +} + +template void MersenneRNG::unitvector(float *p, int size); +template void MersenneRNG::unitvector(double *p, int size); From 599af0a4d9ef03d0099b09ead08175dcf114681a Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 30 Sep 2013 22:51:29 +0400 Subject: [PATCH 25/34] Fix linux and windows build issues. - Linux makefile overrides and removes the optimization flags. - Old linux gcc doesn't understand lambdas. - MSVC doesn't like extern and explicit instantiation in same file. --- CMakeLists.txt | 6 +++--- library/include/Pragma.h | 2 ++ library/include/modules/Random.h | 2 ++ library/modules/Random.cpp | 2 ++ plugins/uicommon.h | 8 +++++--- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c7c84e859..6bc339b42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,9 +104,9 @@ OPTION(BUILD_PLUGINS "Build the plugins." ON) # enable C++11 features IF(UNIX) add_definitions(-DLINUX_BUILD) - SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-g -Wall -Wno-unused-variable") - SET(CMAKE_CXX_FLAGS "-fvisibility=hidden -m32 -march=i686 -mtune=generic -std=c++0x") - SET(CMAKE_C_FLAGS "-fvisibility=hidden -m32 -march=i686 -mtune=generic") + SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -g -Wall -Wno-unused-variable") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -m32 -march=i686 -mtune=generic -std=c++0x") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden -m32 -march=i686 -mtune=generic") ELSEIF(MSVC) # for msvc, tell it to always use 8-byte pointers to member functions to avoid confusion SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /vmg /vmm /MP") diff --git a/library/include/Pragma.h b/library/include/Pragma.h index b6f776725..7a7e8268f 100644 --- a/library/include/Pragma.h +++ b/library/include/Pragma.h @@ -56,6 +56,8 @@ distribution. #pragma warning( disable: 4018) // nonstandard extension used: enum 'df::whatever::etc' used in qualified name #pragma warning( disable: 4482) + // nonstandard extension used: 'extern' before template explicit instantiation + #pragma warning( disable: 4231) #endif #endif diff --git a/library/include/modules/Random.h b/library/include/modules/Random.h index c6f9a3f6a..114336cd6 100644 --- a/library/include/modules/Random.h +++ b/library/include/modules/Random.h @@ -99,8 +99,10 @@ namespace Random } }; +#ifndef DFHACK_RANDOM_CPP extern template void MersenneRNG::unitvector(float *p, int size); extern template void MersenneRNG::unitvector(double *p, int size); +#endif } } diff --git a/library/modules/Random.cpp b/library/modules/Random.cpp index 5030d1449..06c885cad 100644 --- a/library/modules/Random.cpp +++ b/library/modules/Random.cpp @@ -29,6 +29,8 @@ distribution. #include using namespace std; +#define DFHACK_RANDOM_CPP + #include "modules/Random.h" #include "VersionInfo.h" #include "MemAccess.h" diff --git a/plugins/uicommon.h b/plugins/uicommon.h index 147556ff1..d9c225d88 100644 --- a/plugins/uicommon.h +++ b/plugins/uicommon.h @@ -427,7 +427,7 @@ public: void clearSelection() { - for_each_(list, [] (ListEntry &e) { e.selected = false; }); + for_each_(list, clear_fn); } void selectItem(const T elem) @@ -550,8 +550,7 @@ public: void sort() { if (force_sort || list.size() < 100) - std::sort(list.begin(), list.end(), - [] (ListEntry const& a, ListEntry const& b) { return a.text.compare(b.text) < 0; }); + std::sort(list.begin(), list.end(), sort_fn); filterDisplay(); } @@ -569,6 +568,9 @@ public: } private: + static void clear_fn(ListEntry &e) { e.selected = false; } + static bool sort_fn(ListEntry const& a, ListEntry const& b) { return a.text.compare(b.text) < 0; } + vector> list; vector*> display_list; string search_string; From e175efa689680a061282842f92bd6c147edb2579 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 1 Oct 2013 18:58:04 +0400 Subject: [PATCH 26/34] Implement a template-based classical Perlin noise generator. The mask argument of the Impl template is there because apparently an inner template cannot be fully specialized, so there needs to be some argument besides i. --- Lua API.rst | 6 + library/CMakeLists.txt | 2 +- library/LuaApi.cpp | 63 +++++++++- library/include/Export.h | 9 ++ library/include/LuaTools.h | 5 + library/include/modules/PerlinNoise.inc | 146 ++++++++++++++++++++++++ library/include/modules/Random.h | 72 +++++++++++- library/modules/Random.cpp | 7 ++ 8 files changed, 302 insertions(+), 8 deletions(-) create mode 100644 library/include/modules/PerlinNoise.inc diff --git a/Lua API.rst b/Lua API.rst index 42d9953e8..dde47c8d2 100644 --- a/Lua API.rst +++ b/Lua API.rst @@ -761,6 +761,12 @@ Random number generation uniformly distributed over the corresponding sphere surface. The default size is 3. +* ``fn = rng:perlin([dim]); fn(x[,y[,z]])`` + + Returns a closure that computes a classical Perlin noise function + of dimension *dim*, initialized from this random generator. + Dimension may be 1, 2 or 3 (default). + C++ function wrappers ===================== diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 6df69fe27..2ecbbfa42 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -362,7 +362,7 @@ if(BUILD_DEVEL) # without the '/', the directory itself is installed install(DIRECTORY include/ DESTINATION ${DFHACK_INCLUDES_DESTINATION} - FILES_MATCHING PATTERN "*.h" ) #linux: include + FILES_MATCHING PATTERN "*.h" PATTERN "*.inc" ) #linux: include # Building the docs IF(BUILD_DOXYGEN) add_subdirectory (doc) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 3b9a8d6d6..3f93e912d 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -94,6 +94,9 @@ using namespace DFHack::LuaWrapper; using Screen::Pen; using Random::MersenneRNG; +using Random::PerlinNoise1D; +using Random::PerlinNoise2D; +using Random::PerlinNoise3D; void dfhack_printerr(lua_State *S, const std::string &str); @@ -741,12 +744,10 @@ void Lua::Push(lua_State *L, const Screen::Pen &info) return; } - void *pdata = lua_newuserdata(L, sizeof(Pen)); + new (L) Pen(info); lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_PEN_TOKEN); lua_setmetatable(L, -2); - - new (pdata) Pen(info); } static Pen *check_pen_native(lua_State *L, int index) @@ -1098,13 +1099,11 @@ static int dfhack_random_init(lua_State *L) static int dfhack_random_new(lua_State *L) { - void *pdata = lua_newuserdata(L, sizeof(MersenneRNG)); + new (L) MersenneRNG(); lua_rawgetp(L, LUA_REGISTRYINDEX, &DFHACK_RANDOM_TOKEN); lua_setmetatable(L, -2); - new (pdata) MersenneRNG(); - lua_insert(L, 1); return dfhack_random_init(L); } @@ -1157,6 +1156,57 @@ static int dfhack_random_unitvector(lua_State *L) return size; } +static int eval_perlin_1(lua_State *L) +{ + auto &gen = *(PerlinNoise1D*)lua_touserdata(L, lua_upvalueindex(1)); + lua_pushnumber(L, gen(luaL_checknumber(L, 1))); + return 1; +} +static int eval_perlin_2(lua_State *L) +{ + auto &gen = *(PerlinNoise2D*)lua_touserdata(L, lua_upvalueindex(1)); + lua_pushnumber(L, gen(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); + return 1; +} +static int eval_perlin_3(lua_State *L) +{ + auto &gen = *(PerlinNoise3D*)lua_touserdata(L, lua_upvalueindex(1)); + lua_pushnumber(L, gen(luaL_checknumber(L, 1), luaL_checknumber(L, 2), luaL_checknumber(L, 3))); + return 1; +} + +static int dfhack_random_perlin(lua_State *L) +{ + MersenneRNG *prng = check_random_native(L, 1); + int size = luaL_optint(L, 2, 3); + + switch (size) + { + case 1: { + auto pdata = new (L) PerlinNoise1D(); + pdata->init(*prng); + lua_pushcclosure(L, eval_perlin_1, 1); + break; + } + case 2: { + auto pdata = new (L) PerlinNoise2D(); + pdata->init(*prng); + lua_pushcclosure(L, eval_perlin_2, 1); + break; + } + case 3: { + auto pdata = new (L) PerlinNoise3D(); + pdata->init(*prng); + lua_pushcclosure(L, eval_perlin_3, 1); + break; + } + default: + luaL_argerror(L, 2, "perlin noise dimension must be 1, 2 or 3"); + } + + return 1; +} + static const luaL_Reg dfhack_random_funcs[] = { { "new", dfhack_random_new }, { "init", dfhack_random_init }, @@ -1166,6 +1216,7 @@ static const luaL_Reg dfhack_random_funcs[] = { { "drandom1", dfhack_random_drandom1 }, { "unitrandom", dfhack_random_unitrandom }, { "unitvector", dfhack_random_unitvector }, + { "perlin", dfhack_random_perlin }, { NULL, NULL } }; diff --git a/library/include/Export.h b/library/include/Export.h index c3d007117..87d38b45f 100644 --- a/library/include/Export.h +++ b/library/include/Export.h @@ -29,15 +29,24 @@ distribution. #ifndef DFHACK_EXPORT #define DFHACK_EXPORT __attribute__ ((visibility("default"))) #endif + #ifndef DFHACK_IMPORT + #define DFHACK_IMPORT DFHACK_EXPORT + #endif #else #ifdef BUILD_DFHACK_LIB #ifndef DFHACK_EXPORT #define DFHACK_EXPORT __declspec(dllexport) #endif + #ifndef DFHACK_IMPORT + #define DFHACK_IMPORT + #endif #else #ifndef DFHACK_EXPORT #define DFHACK_EXPORT __declspec(dllimport) #endif + #ifndef DFHACK_IMPORT + #define DFHACK_IMPORT DFHACK_EXPORT + #endif #endif #endif diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index 3c15ebaac..d70e3ee33 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -34,6 +34,11 @@ distribution. #include #include +/// Allocate a new user data object and push it on the stack +inline void *operator new (std::size_t size, lua_State *L) { + return lua_newuserdata(L, size); +} + namespace DFHack { class function_identity_base; struct MaterialInfo; diff --git a/library/include/modules/PerlinNoise.inc b/library/include/modules/PerlinNoise.inc new file mode 100644 index 000000000..573968a95 --- /dev/null +++ b/library/include/modules/PerlinNoise.inc @@ -0,0 +1,146 @@ +#pragma once + +namespace DFHack { +namespace Random { + +/* + * A good explanation: + * http://webstaff.itn.liu.se/~stegu/TNM022-2005/perlinnoiselinks/perlin-noise-math-faq.html + */ + +// Interpolation functions + +template +inline T s_curve(T t) +{ + return t * t * (3 - 2*t); +} + +template +inline T lerp(T s, T a, T b) +{ + return a + s * (b-a); +} + +// Templates used to force unrolling and inlining of the loops + +template +template +struct PerlinNoise::Impl { + typedef typename PerlinNoise::Temp Temp; + static inline T dot(T *pa, T *pb); + static inline void setup(const T *pv, Temp *pt); + static inline T eval(PerlinNoise *self, Temp *pt, unsigned idx, T *pq); +}; + +// Dot product of VSIZE vectors pointed by pa, pb + +template +template +inline T PerlinNoise::Impl::dot(T *pa, T *pb) +{ + return pa[0] * pb[0]; +} + +template +template +inline T PerlinNoise::Impl::dot(T *pa, T *pb) +{ + return Impl::dot(pa, pb) + pa[i] * pb[i]; +} + +// Initialization of the temporaries from input coordinates + +template +template +inline void PerlinNoise::Impl::setup(const T *pv, Temp *pt) +{ + T t = std::floor(*pv); + pt->s = s_curve(pt->r0 = *pv - t); + pt->b0 = unsigned(int32_t(t)) & mask; +} + +template +template +inline void PerlinNoise::Impl::setup(const T *pv, Temp *pt) +{ + Impl::setup(pv,pt); + Impl::setup(pv+i,pt+i); +} + +// Main recursion. Uses tables from self and pt. +// Recursion changes current index idx, and current offset vector pq. + +template +template +inline T PerlinNoise::Impl::eval( + PerlinNoise *self, Temp *pt, unsigned idx, T *pq +) { + pq[0] = pt[0].r0; + idx += pt[0].b0; + T u = Impl::dot(pq, self->gradients[idx]); + + pq[0] -= 1; + idx += 1; + T v = Impl::dot(pq, self->gradients[idx]); + + return lerp(pt[0].s, u, v); +} + +template +template +inline T PerlinNoise::Impl::eval( + PerlinNoise *self, Temp *pt, unsigned idx, T *pq +) { + pq[i] = pt[i].r0; + idx += pt[i].b0; + T u = Impl::eval(self, pt, self->idxmap[idx], pq); + + pq[i] -= 1; + idx += 1; + T v = Impl::eval(self, pt, self->idxmap[idx], pq); + + return lerp(pt[i].s, u, v); +} + +// Actual methods of the object + +template +void PerlinNoise::init(MersenneRNG &rng) +{ + STATIC_ASSERT(VSIZE > 0 && BITS <= 8*sizeof(IDXT)); + + // Random unit gradient vectors + for (unsigned i = 0; i < TSIZE; i++) + rng.unitvector(gradients[i], VSIZE); + + // Random permutation table + for (unsigned i = 0; i < TSIZE; i++) + idxmap[i] = i; + + rng.permute(idxmap, TSIZE); + + // Extended part of the table to avoid doing bitwise ops + for (unsigned i = TSIZE; i < TSIZE_EXT; i++) + { + for (unsigned j = 0; j < VSIZE; j++) + gradients[i][j] = gradients[i-TSIZE][j]; + + idxmap[i] = idxmap[i-TSIZE]; + } +} + +template +T PerlinNoise::eval(const T coords[VSIZE]) +{ + // Precomputed properties from the coordinates + Temp tmp[VSIZE]; + // Temporary used to build the current offset vector + T q[VSIZE]; + + Impl::setup(coords, tmp); + + return Impl::eval(this, tmp, 0, q); +} + +}} // namespace diff --git a/library/include/modules/Random.h b/library/include/modules/Random.h index 114336cd6..67a9f904b 100644 --- a/library/include/modules/Random.h +++ b/library/include/modules/Random.h @@ -26,7 +26,7 @@ distribution. #ifndef CL_MOD_RANDOM #define CL_MOD_RANDOM /** - * \defgroup grp_translation Translation: DF word tables and name translation/reading + * \defgroup grp_random Random: Random number and noise generation * @ingroup grp_modules */ @@ -51,6 +51,8 @@ namespace Random void prefill(unsigned step, int twist_cnt); public: + /* No constructor or destructor - safe to treat as data */ + void init(const uint32_t *pseed, unsigned cnt, int twist_cnt = 1); void init(); // uses time @@ -104,6 +106,74 @@ namespace Random extern template void MersenneRNG::unitvector(double *p, int size); #endif + /* + * Classical Perlin noise function in template form. + * http://mrl.nyu.edu/~perlin/doc/oscar.html#noise + */ + + template + class PerlinNoise + { + // Size of randomness tables + static const unsigned TSIZE = 1< + struct Impl { + static inline T dot(T *pa, T *pb); + static inline void setup(const T *pv, Temp *pt); + static inline T eval(PerlinNoise *self, Temp *pt, unsigned idx, T *pq); + }; + + public: + /* No constructor or destructor - safe to treat as data */ + + void init(MersenneRNG &rng); + + T eval(const T coords[VSIZE]); + }; + +#ifndef DFHACK_RANDOM_CPP + extern template class DFHACK_IMPORT PerlinNoise; + extern template class DFHACK_IMPORT PerlinNoise; + extern template class DFHACK_IMPORT PerlinNoise; +#endif + + template + class PerlinNoise1D : public PerlinNoise + { + public: + T operator() (T x) { return this->eval(&x); } + }; + + template + class PerlinNoise2D : public PerlinNoise + { + public: + T operator() (T x, T y) { + T tmp[2] = { x, y }; + return this->eval(tmp); + } + }; + + template + class PerlinNoise3D : public PerlinNoise + { + public: + T operator() (T x, T y, T z) { + T tmp[3] = { x, y, z }; + return this->eval(tmp); + } + }; } } #endif diff --git a/library/modules/Random.cpp b/library/modules/Random.cpp index 06c885cad..5fedb74f5 100644 --- a/library/modules/Random.cpp +++ b/library/modules/Random.cpp @@ -38,6 +38,7 @@ using namespace std; #include "ModuleFactory.h" #include "Core.h" #include "Error.h" +#include "VTableInterpose.h" #include @@ -147,3 +148,9 @@ void MersenneRNG::unitvector(T *p, int size) template void MersenneRNG::unitvector(float *p, int size); template void MersenneRNG::unitvector(double *p, int size); + +#include "modules/PerlinNoise.inc" + +template class DFHACK_EXPORT PerlinNoise; +template class DFHACK_EXPORT PerlinNoise; +template class DFHACK_EXPORT PerlinNoise; From 864baa22410352b8e7323aa56618df68ea61481f Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 1 Oct 2013 18:58:56 +0400 Subject: [PATCH 27/34] Fix the lua script not printing any errors if executing a file fails. --- scripts/lua.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/lua.lua b/scripts/lua.lua index 9bf6ce793..6c31e67aa 100644 --- a/scripts/lua.lua +++ b/scripts/lua.lua @@ -8,7 +8,7 @@ if cmd=="--file" or cmd=="-f" then if f==nil then qerror(err) end - dfhack.pcall(f,table.unpack(args,3)) + dfhack.safecall(f,table.unpack(args,3)) elseif cmd=="--save" or cmd=="-s" then if df.global.world.cur_savegame.save_dir=="" then qerror("Savefile not loaded") @@ -19,7 +19,7 @@ elseif cmd=="--save" or cmd=="-s" then if f==nil then qerror(err) end - dfhack.pcall(f,table.unpack(args,3)) + dfhack.safecall(f,table.unpack(args,3)) elseif cmd~=nil then -- Support some of the prefixes allowed by dfhack.interpreter local prefix From 33469f5bb2f4eb393fdac4e5bd7ed72b6b6868af Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 2 Oct 2013 18:55:41 +0400 Subject: [PATCH 28/34] Use a better hash function in the Perlin noise generator. --- library/include/modules/PerlinNoise.inc | 104 ++++++++++-------------- library/include/modules/Random.h | 16 ++-- 2 files changed, 52 insertions(+), 68 deletions(-) diff --git a/library/include/modules/PerlinNoise.inc b/library/include/modules/PerlinNoise.inc index 573968a95..6b7fec17b 100644 --- a/library/include/modules/PerlinNoise.inc +++ b/library/include/modules/PerlinNoise.inc @@ -13,7 +13,11 @@ namespace Random { template inline T s_curve(T t) { - return t * t * (3 - 2*t); + // Classical function + //return t * t * (3 - 2*t); + + // 2002 version from http://mrl.nyu.edu/~perlin/paper445.pdf + return t * t * t * (t * (t * 6 - 15) + 10); } template @@ -22,50 +26,46 @@ inline T lerp(T s, T a, T b) return a + s * (b-a); } +// Dot product of VSIZE vectors pointed by pa, pb + +template +struct DotProduct { + static inline T eval(T *pa, T *pb); +}; +template +struct DotProduct { + static inline T eval(T *pa, T *pb) { return pa[0]*pb[0]; } +}; +template +inline T DotProduct::eval(T *pa, T *pb) { + return DotProduct::eval(pa, pb) + pa[i]*pb[i]; +} + // Templates used to force unrolling and inlining of the loops template template -struct PerlinNoise::Impl { +struct PerlinNoise::Impl { typedef typename PerlinNoise::Temp Temp; - static inline T dot(T *pa, T *pb); - static inline void setup(const T *pv, Temp *pt); + static inline void setup(PerlinNoise *, const T *, Temp *) {} static inline T eval(PerlinNoise *self, Temp *pt, unsigned idx, T *pq); }; -// Dot product of VSIZE vectors pointed by pa, pb - -template -template -inline T PerlinNoise::Impl::dot(T *pa, T *pb) -{ - return pa[0] * pb[0]; -} - -template -template -inline T PerlinNoise::Impl::dot(T *pa, T *pb) -{ - return Impl::dot(pa, pb) + pa[i] * pb[i]; -} - // Initialization of the temporaries from input coordinates template -template -inline void PerlinNoise::Impl::setup(const T *pv, Temp *pt) -{ - T t = std::floor(*pv); - pt->s = s_curve(pt->r0 = *pv - t); - pt->b0 = unsigned(int32_t(t)) & mask; -} +template +inline void PerlinNoise::Impl::setup( + PerlinNoise *self, const T *pv, Temp *pt +) { + Impl::setup(self, pv, pt); -template -template -inline void PerlinNoise::Impl::setup(const T *pv, Temp *pt) -{ - Impl::setup(pv,pt); - Impl::setup(pv+i,pt+i); + T t = std::floor(pv[i]); + pt[i].s = s_curve(pt[i].r0 = pv[i] - t); + + unsigned b = unsigned(int32_t(t)); + pt[i].b0 = self->idxmap[i][b & mask]; + pt[i].b1 = self->idxmap[i][(b+1) & mask]; } // Main recursion. Uses tables from self and pt. @@ -73,32 +73,22 @@ inline void PerlinNoise::Impl::setup(const T *pv, Tem template template -inline T PerlinNoise::Impl::eval( +inline T PerlinNoise::Impl::eval( PerlinNoise *self, Temp *pt, unsigned idx, T *pq ) { - pq[0] = pt[0].r0; - idx += pt[0].b0; - T u = Impl::dot(pq, self->gradients[idx]); - - pq[0] -= 1; - idx += 1; - T v = Impl::dot(pq, self->gradients[idx]); - - return lerp(pt[0].s, u, v); + return DotProduct::eval(pq, self->gradients[idx]); } template -template +template inline T PerlinNoise::Impl::eval( PerlinNoise *self, Temp *pt, unsigned idx, T *pq ) { pq[i] = pt[i].r0; - idx += pt[i].b0; - T u = Impl::eval(self, pt, self->idxmap[idx], pq); + T u = Impl::eval(self, pt, idx ^ pt[i].b0, pq); pq[i] -= 1; - idx += 1; - T v = Impl::eval(self, pt, self->idxmap[idx], pq); + T v = Impl::eval(self, pt, idx ^ pt[i].b1, pq); return lerp(pt[i].s, u, v); } @@ -114,19 +104,13 @@ void PerlinNoise::init(MersenneRNG &rng) for (unsigned i = 0; i < TSIZE; i++) rng.unitvector(gradients[i], VSIZE); - // Random permutation table - for (unsigned i = 0; i < TSIZE; i++) - idxmap[i] = i; - - rng.permute(idxmap, TSIZE); - - // Extended part of the table to avoid doing bitwise ops - for (unsigned i = TSIZE; i < TSIZE_EXT; i++) + // Random permutation tables + for (unsigned j = 0; j < VSIZE; j++) { - for (unsigned j = 0; j < VSIZE; j++) - gradients[i][j] = gradients[i-TSIZE][j]; + for (unsigned i = 0; i < TSIZE; i++) + idxmap[j][i] = i; - idxmap[i] = idxmap[i-TSIZE]; + rng.permute(idxmap[j], TSIZE); } } @@ -138,7 +122,7 @@ T PerlinNoise::eval(const T coords[VSIZE]) // Temporary used to build the current offset vector T q[VSIZE]; - Impl::setup(coords, tmp); + Impl::setup(this, coords, tmp); return Impl::eval(this, tmp, 0, q); } diff --git a/library/include/modules/Random.h b/library/include/modules/Random.h index 67a9f904b..25d694ad7 100644 --- a/library/include/modules/Random.h +++ b/library/include/modules/Random.h @@ -109,6 +109,9 @@ namespace Random /* * Classical Perlin noise function in template form. * http://mrl.nyu.edu/~perlin/doc/oscar.html#noise + * + * Using an improved hash function from: + * http://www.cs.utah.edu/~aek/research/noise.pdf */ template @@ -116,21 +119,18 @@ namespace Random { // Size of randomness tables static const unsigned TSIZE = 1< + template struct Impl { - static inline T dot(T *pa, T *pb); - static inline void setup(const T *pv, Temp *pt); + static inline void setup(PerlinNoise *self, const T *pv, Temp *pt); static inline T eval(PerlinNoise *self, Temp *pt, unsigned idx, T *pq); }; From f3be0ee187b02cd4c1de60aade4b62a11dfe0c0a Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 2 Oct 2013 19:55:48 +0400 Subject: [PATCH 29/34] Remove the use of lambdas in buildingplan. --- plugins/buildingplan.cpp | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/plugins/buildingplan.cpp b/plugins/buildingplan.cpp index 70a324423..02b549a56 100644 --- a/plugins/buildingplan.cpp +++ b/plugins/buildingplan.cpp @@ -92,6 +92,8 @@ static void debug(const string &msg) * Material Choice Screen */ +static string material_to_string_fn(MaterialInfo m) { return m.toString(); } + struct ItemFilter { df::dfhack_material_category mat_mask; @@ -114,8 +116,10 @@ struct ItemFilter bool matches(MaterialInfo &material) const { - return any_of(materials.begin(), materials.end(), - [&] (const MaterialInfo &m) { return material.matches(m); }); + for (auto it = materials.begin(); it != materials.end(); ++it) + if (material.matches(*it)) + return true; + return false; } bool matches(df::item *item) @@ -137,8 +141,7 @@ struct ItemFilter { vector descriptions; - transform_(materials, descriptions, - [] (MaterialInfo m) { return m.toString(); }); + transform_(materials, descriptions, material_to_string_fn); if (descriptions.size() == 0) bitfield_to_string(&descriptions, mat_mask); @@ -157,8 +160,8 @@ struct ItemFilter str.append("/"); if (materials.size() > 0) { - for_each_(materials, - [&] (MaterialInfo &m) { str.append(m.getToken() + ","); }); + for (size_t i = 0; i < materials.size(); i++) + str.append(materials[i].getToken() + ","); if (str[str.size()-1] == ',') str.resize(str.size () - 1); @@ -217,6 +220,7 @@ private: bool valid; }; +static MaterialInfo &material_info_identity_fn(MaterialInfo &m) { return m; } class ViewscreenChooseMaterial : public dfhack_viewscreen { @@ -284,13 +288,12 @@ public: // Category masks auto masks = masks_column.getSelectedElems(); - for_each_(masks, - [&] (df::dfhack_material_category &m) { filter->mat_mask.whole |= m.whole; }); + for (auto it = masks.begin(); it != masks.end(); ++it) + filter->mat_mask.whole |= it->whole; // Specific materials auto materials = materials_column.getSelectedElems(); - transform_(materials, filter->materials, - [] (MaterialInfo &m) { return m; }); + transform_(materials, filter->materials, material_info_identity_fn); Screen::dismiss(this); } @@ -465,6 +468,7 @@ private: } }; +static void delete_item_fn(df::job_item *x) { delete x; } // START Planning class PlannedBuilding @@ -555,7 +559,7 @@ public: auto job = building->jobs[0]; - for_each_(job->job_items, [] (df::job_item *x) { delete x; }); + for_each_(job->job_items, delete_item_fn); job->job_items.clear(); job->flags.bits.suspend = false; @@ -613,9 +617,10 @@ private: ItemFilter filter; }; - static map planmode_enabled, saved_planmodes; +static void enable_quickfort_fn(pair& pair) { pair.second = true; } + class Planner { public: @@ -804,8 +809,7 @@ public: void enableQuickfortMode() { saved_planmodes = planmode_enabled; - for_each_(planmode_enabled, - [] (pair& pair) { pair.second = true; } ); + for_each_(planmode_enabled, enable_quickfort_fn); quickfort_mode = true; } @@ -1093,8 +1097,8 @@ struct buildingplan_hook : public df::viewscreen_dwarfmodest OutputHotkeyString(x, y, "Material Filter:", "m", true, left_margin); auto filter_descriptions = filter->getMaterialFilterAsVector(); - for_each_(filter_descriptions, - [&](string d) { OutputString(COLOR_BROWN, x, y, " *" + d, true, left_margin); }); + for (auto it = filter_descriptions.begin(); it != filter_descriptions.end(); ++it) + OutputString(COLOR_BROWN, x, y, " *" + *it, true, left_margin); } else { @@ -1121,8 +1125,8 @@ struct buildingplan_hook : public df::viewscreen_dwarfmodest OutputString(COLOR_BROWN, x, y, "Materials:", true, left_margin); auto filters = filter->getMaterialFilterAsVector(); - for_each_(filters, - [&](string d) { OutputString(COLOR_BLUE, x, y, "*" + d, true, left_margin); }); + for (auto it = filters.begin(); it != filters.end(); ++it) + OutputString(COLOR_BLUE, x, y, "*" + *it, true, left_margin); } else { From ccc1d936df191bb43465e3828362f31f8d387b26 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 2 Oct 2013 20:00:03 +0400 Subject: [PATCH 30/34] Add a Perlin noise test script. --- scripts/devel/test-perlin.lua | 132 ++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 scripts/devel/test-perlin.lua diff --git a/scripts/devel/test-perlin.lua b/scripts/devel/test-perlin.lua new file mode 100644 index 000000000..a6642e125 --- /dev/null +++ b/scripts/devel/test-perlin.lua @@ -0,0 +1,132 @@ +-- Generates an image using multiple octaves of perlin noise. + +local args = {...} +local rng = dfhack.random.new(3) + +if #args < 3 then + qerror('Usage: devel/test-perlin ...') +end + +local fname = table.remove(args,1) +local goal = tonumber(table.remove(args,1)) or qerror('Invalid density') + +local zscale = 6 + +local coeffs = {} +local fns = {} + +local env = copyall(math) +env.apow = function(x,y) return math.pow(math.abs(x),y) end + +for i = 1,#args do + local fn = rng:perlin(3); + local fn2 = rng:perlin(3); + local fn3 = rng:perlin(3); + local fn4 = rng:perlin(3); + + fns[i] = fn; + + local val = string.match(args[i],'^([+-]?[%d.]+)$') + if val then + coeffs[i] = function(x) return x * val end + else + local argstr = 'x' + if string.match(args[i], '%f[%w]w%f[^%w]') then + argstr = 'x,y,z,w' + fns[i] = function(x,y,z) + return fn(x,y,z), fn2(x,y,z), fn3(x+0.5,y+0.5,z+0.5), fn4(x+0.5,y+0.5,z+0.5) + end + elseif string.match(args[i], '%f[%w]z%f[^%w]') then + argstr = 'x,y,z' + fns[i] = function(x,y,z) + return fn(x,y,z), fn2(x,y,z), fn3(x+0.5,y+0.5,z+0.5) + end + elseif string.match(args[i], '%f[%w]y%f[^%w]') then + argstr = 'x,y' + fns[i] = function(x,y,z) + return fn(x,y,z), fn2(x,y,z) + end + end + + local f,err = load( + 'return function('..argstr..') return ('..args[i]..') end', + '=(expr)', 't', env + ) + if not f then + qerror(err) + end + coeffs[i] = f() + end +end + +function render(thresh,file) + local area = 0 + local line, arr = '', {} + + for zy = 0,1 do + for y = 0,48*4-1 do + line = '' + for zx = 0,1 do + for x = 0,48*4-1 do + local tx = (0.5+x)/(48*2) + local ty = (0.5+y)/(48*2) + local tz = 0.5+(zx+zy*2)/(48*2/zscale) + local v1 = 0 + for i = 1,#coeffs do + v1 = v1 + coeffs[i](fns[i](tx,ty,tz)) + tx = tx*2 + ty = ty*2 + tz = tz*2 + end + local v = -1 + if v1 > thresh then + v = v1; + area = area + 1 + end + if file then + local c = math.max(0, math.min(255, v * 127 + 128)) + arr[2*x+1] = c + arr[2*x+2] = c + end + end + if file then + line = line..string.char(table.unpack(arr)) + end + end + if file then + file:write(line,line) + end + end + end + + return area/4/(48*4)/(48*4) +end + +function search(fn,min,max,goal,eps) + while true do + local center = (max+min)/2 + local cval = fn(center) + print('At '..center..': '..cval) + if math.abs(cval-goal) < eps then + return center + end + if cval > goal then + min = center + else + max = center + end + end +end + +local thresh = search(render, -1, 1, goal, 0.001) + +local file,err = io.open(fname, 'wb') +if not file then + print('error: ',err) + return +end +file:write('P5\n768 768\n255\n') +local area = render(thresh, file) +file:close() + +print('Area fraction: '..area) From 32a6073daddfe0d4802f93d1c09f24ffaec9f7ac Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 3 Oct 2013 16:30:06 +0400 Subject: [PATCH 31/34] Remove lambdas from dwarfmonitor and resume. --- plugins/dwarfmonitor.cpp | 21 +++++++++------------ plugins/resume.cpp | 6 ++++-- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/plugins/dwarfmonitor.cpp b/plugins/dwarfmonitor.cpp index 883f0a002..2776227c5 100644 --- a/plugins/dwarfmonitor.cpp +++ b/plugins/dwarfmonitor.cpp @@ -179,9 +179,9 @@ public: addDwarfActivity(unit, *entry); } - for_each_(dwarf_activity_values[unit], - [&] (const pair &x) - { dwarf_activity_values[unit][x.first] = getPercentage(x.second, dwarf_total); } ); + auto &values = dwarf_activity_values[unit]; + for (auto it = values.begin(); it != values.end(); ++it) + it->second = getPercentage(it->second, dwarf_total); dwarves_column.add(getUnitName(unit), unit); } @@ -212,9 +212,8 @@ public: vector> rev_vec(dwarf_activities->begin(), dwarf_activities->end()); sort(rev_vec.begin(), rev_vec.end(), less_second()); - for_each_(rev_vec, - [&] (pair x) - { dwarf_activity_column.add(getActivityItem(x.first, x.second), x.first); }); + for (auto it = rev_vec.begin(); it != rev_vec.end(); ++it) + dwarf_activity_column.add(getActivityItem(it->first, it->second), it->first); } dwarf_activity_column.fixWidth(); @@ -754,9 +753,8 @@ public: vector> rev_vec(dwarf_activities->begin(), dwarf_activities->end()); sort(rev_vec.begin(), rev_vec.end(), less_second()); - for_each_(rev_vec, - [&] (pair x) - { dwarf_activity_column.add(getDwarfAverage(x.first, x.second), x.first); }); + for (auto it = rev_vec.begin(); it != rev_vec.end(); ++it) + dwarf_activity_column.add(getDwarfAverage(it->first, it->second), it->first); } } @@ -778,9 +776,8 @@ public: vector> rev_vec(category_activities->begin(), category_activities->end()); sort(rev_vec.begin(), rev_vec.end(), less_second()); - for_each_(rev_vec, - [&] (pair x) - { category_breakdown_column.add(getBreakdownAverage(x.first, x.second), x.first); }); + for (auto it = rev_vec.begin(); it != rev_vec.end(); ++it) + category_breakdown_column.add(getBreakdownAverage(it->first, it->second), it->first); } category_breakdown_column.fixWidth(); diff --git a/plugins/resume.cpp b/plugins/resume.cpp index 9d37baac7..01127ef94 100644 --- a/plugins/resume.cpp +++ b/plugins/resume.cpp @@ -125,8 +125,10 @@ void scan_for_suspended_buildings() SuspendedBuilding sb(bld); sb.is_planned = job->job_items.size() == 1 && job->job_items[0]->item_type == item_type::NONE; - auto it = find_if(resumed_buildings.begin(), resumed_buildings.end(), - [&] (SuspendedBuilding &rsb) { return rsb.bld == bld; }); + auto it = resumed_buildings.begin(); + + for (; it != resumed_buildings.end(); ++it) + if (it->bld == bld) break; sb.was_resumed = it != resumed_buildings.end(); From a18347608af6885d90ceeb2a0b75182a75b93941 Mon Sep 17 00:00:00 2001 From: jj Date: Sun, 29 Sep 2013 15:11:56 +0200 Subject: [PATCH 32/34] ruby: vectors default to int32_t --- plugins/ruby/ruby-autogen-defs.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ruby/ruby-autogen-defs.rb b/plugins/ruby/ruby-autogen-defs.rb index c8303128d..7e26e93ac 100644 --- a/plugins/ruby/ruby-autogen-defs.rb +++ b/plugins/ruby/ruby-autogen-defs.rb @@ -475,7 +475,7 @@ module DFHack class StlVector32 < MemStruct attr_accessor :_tg def initialize(tg) - @_tg = tg + @_tg = tg || Number.new(32, false, 0, nil) end def length From 4ba4f901475854da04bfc48fb7647360cdc739fe Mon Sep 17 00:00:00 2001 From: jj Date: Mon, 7 Oct 2013 13:58:28 +0200 Subject: [PATCH 33/34] ruby: add support for enable/disable plugin --- plugins/ruby/ruby.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 69b9b15c9..33477d56e 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -46,6 +46,16 @@ static std::vector *dfhack_run_queue; DFHACK_PLUGIN("ruby") + +DFhackDataExport bool plugin_is_enabled = true; + +DFhackCExport command_result plugin_enable(color_ostream &out, bool enable) +{ + plugin_is_enabled = enable; + return CR_OK; +} + + DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { onupdate_active = 0; From 84033bd58615d2a1318e3e97de933070aebce18a Mon Sep 17 00:00:00 2001 From: jj Date: Mon, 7 Oct 2013 14:17:38 +0200 Subject: [PATCH 34/34] ruby: dont list ruby scripts when ruby plugin is disabled --- library/Core.cpp | 11 +++++++---- library/PluginManager.cpp | 6 +++--- library/include/PluginManager.h | 8 +++++++- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/library/Core.cpp b/library/Core.cpp index b8a5f4808..2459559dd 100644 --- a/library/Core.cpp +++ b/library/Core.cpp @@ -259,7 +259,7 @@ static void listScripts(PluginManager *plug_mgr, std::map &pset, pset[prefix + files[i].substr(0, files[i].size()-4)] = help; } - else if (plug_mgr->eval_ruby && hasEnding(files[i], ".rb")) + else if (plug_mgr->ruby && plug_mgr->ruby->is_enabled() && hasEnding(files[i], ".rb")) { std::string help = getScriptHelp(path + files[i], "# "); @@ -312,6 +312,9 @@ static command_result runLuaScript(color_ostream &out, std::string name, vector< static command_result runRubyScript(color_ostream &out, PluginManager *plug_mgr, std::string name, vector &args) { + if (!plug_mgr->ruby || !plug_mgr->ruby->is_enabled()) + return CR_FAILURE; + std::string rbcmd = "$script_args = ["; for (size_t i = 0; i < args.size(); i++) rbcmd += "'" + args[i] + "', "; @@ -319,7 +322,7 @@ static command_result runRubyScript(color_ostream &out, PluginManager *plug_mgr, rbcmd += "catch(:script_finished) { load './hack/scripts/" + name + ".rb' }"; - return plug_mgr->eval_ruby(out, rbcmd.c_str()); + return plug_mgr->ruby->eval_ruby(out, rbcmd.c_str()); } command_result Core::runCommand(color_ostream &out, const std::string &command) @@ -450,7 +453,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve con.print("%s: %s\n", parts[0].c_str(), help.c_str()); return CR_OK; } - if (plug_mgr->eval_ruby && fileExists(filename + ".rb")) + if (plug_mgr->ruby && plug_mgr->ruby->is_enabled() && fileExists(filename + ".rb")) { string help = getScriptHelp(filename + ".rb", "# "); con.print("%s: %s\n", parts[0].c_str(), help.c_str()); @@ -767,7 +770,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve if (fileExists(filename + ".lua")) res = runLuaScript(con, first, parts); - else if (plug_mgr->eval_ruby && fileExists(filename + ".rb")) + else if (plug_mgr->ruby && plug_mgr->ruby->is_enabled() && fileExists(filename + ".rb")) res = runRubyScript(con, plug_mgr, first, parts); else if (try_autocomplete(con, first, completed)) return CR_NOT_IMPLEMENTED;// runCommand(con, completed, parts); diff --git a/library/PluginManager.cpp b/library/PluginManager.cpp index 3ef4910cb..28d57c76f 100644 --- a/library/PluginManager.cpp +++ b/library/PluginManager.cpp @@ -679,7 +679,7 @@ void Plugin::push_function(lua_State *state, LuaFunction *fn) PluginManager::PluginManager(Core * core) { cmdlist_mutex = new mutex(); - eval_ruby = NULL; + ruby = NULL; } PluginManager::~PluginManager() @@ -774,7 +774,7 @@ void PluginManager::registerCommands( Plugin * p ) belongs[cmds[i].name] = p; } if (p->plugin_eval_ruby) - eval_ruby = p->plugin_eval_ruby; + ruby = p; cmdlist_mutex->unlock(); } @@ -788,6 +788,6 @@ void PluginManager::unregisterCommands( Plugin * p ) belongs.erase(cmds[i].name); } if (p->plugin_eval_ruby) - eval_ruby = NULL; + ruby = NULL; cmdlist_mutex->unlock(); } diff --git a/library/include/PluginManager.h b/library/include/PluginManager.h index 506ca33c0..b47863d48 100644 --- a/library/include/PluginManager.h +++ b/library/include/PluginManager.h @@ -170,6 +170,12 @@ namespace DFHack void open_lua(lua_State *state, int table); + command_result eval_ruby(color_ostream &out, const char* cmd) { + if (!plugin_eval_ruby || !is_enabled()) + return CR_FAILURE; + return plugin_eval_ruby(out, cmd); + } + private: RefLock * access; std::vector commands; @@ -236,7 +242,7 @@ namespace DFHack { return all_plugins.size(); } - command_result (*eval_ruby)(color_ostream &, const char*); + Plugin *ruby; // DATA private: tthread::mutex * cmdlist_mutex;