Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

47 #400

Merged
merged 44 commits into from
Aug 6, 2020
Merged

47 #400

merged 44 commits into from
Aug 6, 2020

Conversation

PatrikLundell
Copy link
Contributor

@PatrikLundell PatrikLundell commented Apr 26, 2020

Additional research on army controllers, including some field naming/identification.

Edit: fix #403
Edit2: Fix DFHack/dfhack#1578

@PatrikLundell
Copy link
Contributor Author

I'm not going to try to deal with the remaining two schema failures as those are things I haven't changed and I don't know enough to determine what the correct correction should be.

@BenLubar
Copy link
Member

those were my mistake - you can merge or rebase to fix them

@PatrikLundell
Copy link
Contributor Author

It seems the introduction of a bitfield-type (in history.xml) is a first, but it's legal according to the XSD and did compile (as well as display the expected data).
It's a slight guess that the bitfield type is a bitfield rather than an enum, as I haven't seen value 3, i.e. scout mercs employed by both sides at the same time, but usage of scouts is not the norm.

df.history.xml Outdated Show resolved Hide resolved
@PatrikLundell
Copy link
Contributor Author

A couple of comments:

  • gui/gm-editor didn't seem to like to display rhythm_pattern.bars [X].beat [0] as an enum value, possibly because of the "is_array" attribute.
  • Given that rhythm_pattern string pointers are arrays (I tried to set them as static arrays with the largest count seen, and that caused gui/gm-editor to run out of memory, while an array allowed me to view the 3 elements of both arrays expected to be present and verify that their contents matched what the exported XML indicated they should have), are we sure the other art related structures use static arrays with uninitialized contents for unused elements, or could it be that all of them actually use the cumbersome parameter dependent array size? I think this is @BenLubar 's area of expertise.

<stl-vector name='unk_f0' type-name='int32_t' comment="4 bytes allocated seen. Cannot be pointer"/>
<stl-vector name='unk_100' type-name='int32_t' comment="4 bytes allocated seen. Cannot be pointer"/>
<stl-vector name='unk_110' type-name='int32_t' comment="4 bytes allocated seen. Cannot be pointer. Seen on the same element, so f0, 100, and 110 may be connected"/>
<compound name='picked_growths' comment="also includes 'automatically picked' i.e. fallen fruit that becomes item_spatter">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be good to move the version into a since attribute:

Suggested change
<compound name='picked_growths' comment="also includes 'automatically picked' i.e. fallen fruit that becomes item_spatter">
<compound name='picked_growths' since='v0.40.14' comment="also includes 'automatically picked' i.e. fallen fruit that becomes item_spatter">

<compound name='picked_growths' comment="also includes 'automatically picked' i.e. fallen fruit that becomes item_spatter">
<stl-vector name='x' type-name='int16_t' comment="within the MLT"/>
<stl-vector name='y' type-name='int16_t' comment="within the MLT"/>
<stl-vector name='z' type-name='int16_t' comment="uncertain coordinate system. 100 = surface on my flat embark, 101 in the air, while the normal coordinates don't go that high"/>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it match either of the coordinates on the right edge of the screen?

Copy link
Contributor Author

@PatrikLundell PatrikLundell May 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it didn't match any of those.

Edit: Correction it does indeed seem to match the lower value, which looks like the Elevation of the tile (not sure why I did see a different value there previously). I don't know how to translate from Elevation to whatever format is used elsewhere, though, but regardless, the comment should be updated.

<stl-vector name='y' type-name='int16_t' comment="within the MLT"/>
<stl-vector name='z' type-name='int16_t' comment="uncertain coordinate system. 100 = surface on my flat embark, 101 in the air, while the normal coordinates don't go that high"/>
<stl-vector name='subtype' type-name='int32_t' comment="subtype of the growth within the raws of the implicit plant"/>
<stl-vector name='density' type-name='int32_t' comment="unknown why the raw density field is placed here, as there's nothing left"/>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this allows for partial harvests of extremely dense growths, so you can pick the rest of it later.

Copy link
Contributor Author

@PatrikLundell PatrikLundell May 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly, although I've seen only 1, possibly 2, and 1000 here. If your idea is correct we'd briefly see something in between 2 and 1000 for trees, but that would require a bit of luck (or care) with the timing.
I haven't seen anything indicating a gradual picking graphically, but the "graphics" might not show the full picture, and I haven't studied fruit picking for hours...

Edit: I've run a callback script that checks these fields every frame, and it hasn't registered a single case of a density value in the range 3 - 999, which ought to happen while dorfs are vigorously picking fruit (at least the durians and olives had a raw density value of 1000).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible that it only happens when gathering items in Adventurer mode - I'd try it myself, but I don't have a script handy for finding the correct object_data entry for my current location.

Copy link
Contributor Author

@PatrikLundell PatrikLundell May 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  local embark_x = df.global.world.world_data.region_details [0].pos.x
  local embark_y = df.global.world.world_data.region_details [0].pos.y
  local embark_min_x, embark_max_x, embark_min_y, embark_max_y
  local object_id
  
  for i = 0, 15 do
    for k = 0, 15 do
      if df.global.world.world_data.region_details [0].biome [i] [k] >= 48 then
        if not embark_min_x then
          embark_min_x = i
        end
        
        embark_max_x = i
        
        if not embark_min_y then
          embark_min_y = k
        end
        
        embark_max_y = k

        object_id = i + embark_x * 16 + k * 16 * df.global.world.world_data.world_width + embark_y * 256 * df.global.world.world_data.world_width
        dfhack.println (df.world_object_data.find (object_id), object_id)        
      end
    end
  end

This snippet gets the object ids for an embark. It obviously would have to be adapted to adventurer mode, where I guess the region_details [0] part remains the same, but there'd have to be some other logic to find the current MLT. I'm rather unfamiliar with adventurer mode, though.

Copy link
Member

@quietust quietust May 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Adventurer mode, region_details[4] is the one that contained >=48 biome values (possibly because it corresponded to the town I started in), but none of the calculated object_id values corresponded to existing world_object_data records.

I tried a more brute-force approach and simply searched through all records to find ones with non-empty picked_growths lists, and it looks like Adventurer mode just doesn't use them at all - no matter how many finger limes I picked from a nearby tree, all of those lists remained empty.

It's possible Toady added the ability to handle partial harvests but just never finished implementing it - perhaps in a later version, dwarves won't harvest the entire tile at once.

Did you only monitor those values while dwarves were harvesting, or did you also check while growths were falling off and landing on the ground as spatters?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Brute force is what I've used mostly, until trying a callback every frame, where the frame rate dropped to 28 even when accessing only the appropriate entries. It's odd, however, that different structures would be used for the same purpose in Adventurer mode and Fortress mode, as I assume the trees actually were picked visually (and not possible to pick indefinitely).

A fruit tree density of 1000 looks like some kind of max value rather than a sensible one, when shrubs tend to have a value of 1, so it might be that partial picking was waiting for the agriculture overhaul.

Have you used tools to look at what code Adventurer mode picking uses? It might be something different that uses different data structures.

df.world-data.xml Outdated Show resolved Hide resolved
@quietust
Copy link
Member

(disregard the above force-push - I tried using the "Resolve Conflicts" button below and introduced a typo, so I reverted it)

Copy link
Member

@lethosor lethosor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I think I've gotten the merge conflicts sorted out. (For some reason, git had the most trouble with your minor changes to historical_figure_info.relationships.intrigues - adding Corruptly_Punish_Actor and naming a couple unk fields - probably because it moved to historical_figure_relationships.intrigues. I ended up just copying the relevant section from master and applying those couple changes manually, instead of trying to sort through git's output, so I guess that's a strategy that could be helpful sometimes.)

To be safe, I'm going to double-check the diff between this branch and master before I merge, so it might not be merged until tomorrow. The good news is that no structures changed sizes, so the merge probably didn't break anything! (A couple names of less-commonly-used structures were changed, presumably intentionally: army_controller_villain_visiting -> army_controller_villainous_visit, army_controller_invasion -> army_controller_invasion_order.) check-structures-sanity also passed on world in a small world, so that's another good sign that nothing broke.

Overall, this is some great research. Again, sorry that it took so long to get to. Mostly, I think I looked at this a few times, thought "that's a big PR, I'll look at it later when I have more time", and got wrapped up in other tasks. It definitely would have been easier to deal with if I had merged it sooner, and I'll try to be on top of that next time.

It might help to know for sure if you're ready for a PR to be merged. I think your "0.4x" PRs in general have been a steady stream of in-progress work that could be merged at any time - am I correct in assuming that I can merge an open PR that you created whenever I want? I'd hate to merge something and have you push an additional change minutes later (it wouldn't be too hard to just open a new PR for it, but it could be noisy). A suggestion: if you want to indicate that you're still working on a PR, you could set it to "draft" mode, and I'll assume it's ready to be merged otherwise. GitHub has a help page here about draft PRs. (I should probably add something similar to the contributing guide...) - edit: how does the pull requests section of https://docs.dfhack.org/en/latest/docs/Contributing.html look?

I left a couple comments related to things I noticed while fixing conflicts. Some might not be relevant anymore, but hopefully some are useful.

df.history.xml Outdated
Comment on lines 476 to 481
<int32_t name='unk_4' comment='probably uninitialized' since='v0.47.01'/>
<int32_t name='unk_5' comment='probably uninitialized' since='v0.47.01'/>
<int32_t name='unk_6' comment='probably uninitialized' since='v0.47.01'/>
<int32_t name='unk_7' comment='probably uninitialized' since='v0.47.01'/>
<int32_t name='unk_8' comment='only seen 0' since='v0.47.01'/>
<int32_t name='unk_9' comment='only seen 0' since='v0.47.01'/>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unk_4 to unk_9 are actually a vector (identities), according to 81e269f#diff-35ab45624fa78d4a8c071f3c0fee5d76R482
I am curious about the comments, though - usually, all three pointers making up a vector will point to valid memory or be set to 0. In my largest save, I couldn't find any cases where these vectors were populated or where any pointers were non-0, so I can't say for sure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 47 branch was used for a steady stream of small unrelated changes, intended to be possible to merge at any time (the only "outstanding" cases are the ones where a conflict is detected when the updates are pushed, and they're handled as quickly as possible after detection. What I'd hoped for, and what happened early on, was that it would be merged with reasonably short intervals so there wouldn't be such a mountain building up (getting increasingly burdensome as parallel changes clash with it). I'm certainly aware of the "this is too much effort to tackle now, I need a bigger chunk of time to do that" issue.
It's certainly possible that you'd make a merge and a new change trickles in minutes later, but the new change should be (mostly) unrelated to the previous ones, so it's not a big deal if it ends up waiting until the next merge opportunity.

So far I've kept my drafts local, so I haven't had any need to flag them (I've seen others using that, though, and it's useful when you want/need comments).

It's good that the fields listed have been identified as a vector, which means my comments are no longer relevant. The comments state what I saw (as pointers look like changing data), but I don't remember things in such details as to be able to comment on the comments beyond that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I'll do my best to keep up with them faster.
I forgot to go into more detail earlier: devel/visualize-structures (and the memview plugin, if you build with dev plugins enabled) marks valid pointers, so that could be helpful to tell whether random-looking data is actually a pointer (since vectors consist of three consecutive pointers). The vectors plugin will do some additional checks to find valid vectors. None of these will deal with empty vectors with 3 null pointers, though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually just ran into some of these fields populated in a pocket-sized world. Turns out they're not vectors after all, so you were right. (They're not even valid pointers.) I'll spin up a 32-bit instance to investigate whether they're intptr_ts or int32_ts.

There's a more complete list of memory-research tools here, in case it's useful: https://docs.dfhack.org/en/latest/docs/Memory-research.html

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for digging deeper into this.

df.history.xml Outdated
<int32_t/>
<int32_t name='unk_1' init-value='-1'/>
<int32_t name='unk_2' init-value='-1'/>
<int32_t name='circumstance' init-value='-1' comment="Only 257 seen apart from init. Probably circumstance='is entity subordinate' in XML"/>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out this is a unit_thought_type:

df-structures/df.history.xml

Lines 1525 to 1526 in ecd6bcc

<struct-type type-name='history_event_circumstance_info'>
<enum name='type' type-name='unit_thought_type' base-type='int32_t'/>

257 is not currently known, so that could be interesting to investigate.

df.history.xml Outdated
@@ -3488,6 +3550,64 @@
<enum name='profession' type-name='profession'/>
</struct-type>

<enum-type type-name='intrigue_relation_type' base-type='int16_t'>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This turned out to be a duplicate of the existing vague_relationship_type (although it only went up to 16 items at the time of your PR)

df.history.xml Outdated
Comment on lines 3673 to 3674
<int32_t name='coconspirator_bonus' init-value='0'/>
<int32_t name='ally_defense_bonus' init-value='0'/>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

init-value=0 is technically default for int fields, so these aren't strictly necessary.

I double-checked - here's the generated constructor for intrigue_corruption from my last build on develop (which lacks the init-value attrs; the relevant fields are the last two, although they have different names):

  df::intrigue_corruption::intrigue_corruption()
    :  crime(ENUM_FIRST_ITEM(crime_type)), corruptor_id(-1), target_id(-1), target_relationship(ENUM_FIRST_ITEM(vague_relationship_type)), target_relationship_entity_id(-1), lurer_id(-1), manipulation_type(Threat), anon_1(0), anon_2(0), manipulated_facet(ENUM_FIRST_ITEM(personality_facet_type)), anon_3(0), facet_roll(0), manipulated_value(ENUM_FIRST_ITEM(value_type)), anon_4(0), value_roll(0), manipulated_emotion(Trust), anon_5(0), emotion_roll(0), position_entity_id(-1), position_assignment_id(-1), offered_id(-1), offered_relationship(ENUM_FIRST_ITEM(vague_relationship_type)), corruptor_ally_roll(0), target_ally_roll(0)
  {
  }

df.history.xml Outdated
<int32_t name='relevant_position_profile_id' init-value='-1'/>

<int32_t name='relevant_id_for_method' ref-target='historical_figure' comment="Paired with next field"/>
<int32_t name='relevant_relation_for_method' init-value='-1' comment="11 seen with offered revenge, 16 with play for sympathy appealing to common worship of HFID. Guess it's intrigue_relation_type, but not enough to connect it"/>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your guess appears to have been correct, since this is also vague_relation_type (same enum) according to BenLubar's research

Last merge apparently wasn't a proper merge. Fortunately all conflicts resolved in favor of the PR branch this time, so the diff is empty.

Conflicts:
	df.art.xml
	df.entities.xml
	df.history.xml
	df.military.xml
@lethosor lethosor merged commit 9fca46c into DFHack:master Aug 6, 2020
lethosor added a commit to DFHack/dfhack that referenced this pull request Aug 6, 2020
lethosor added a commit that referenced this pull request Aug 8, 2020
…t fields

Verified that these are not a valid vector (or pointers), and that they are the
same size (6*int32_t) on 64-bit and 32-bit Linux

Reverts 81e269f#diff-35ab45624fa78d4a8c071f3c0fee5d76R482
See #400
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

exportlegends: unknown identity_type (-1) item_plant_growthst GROWTH_PRINT tile identification
5 participants