diff --git a/CMakeLists.txt b/CMakeLists.txt index 58bce6dc3..a5a1b2579 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -173,8 +173,8 @@ if(NOT EXISTS ${dfhack_SOURCE_DIR}/library/xml/codegen.pl OR NOT EXISTS ${dfhack endif() # set up versioning. -set(DF_VERSION "0.47.03") -set(DFHACK_RELEASE "beta1") +set(DF_VERSION "0.47.04") +set(DFHACK_RELEASE "alpha0") set(DFHACK_PRERELEASE TRUE) set(DFHACK_VERSION "${DF_VERSION}-${DFHACK_RELEASE}") diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index 92bd19fd4..cd3474619 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.cpp @@ -454,3 +454,90 @@ void DFHack::flagarrayToString(std::vector *pvec, const void *p, } } } + +static const struct_field_info *find_union_tag_candidate(const struct_field_info *fields, const struct_field_info *union_field) +{ + std::string name(union_field->name); + if (name.length() >= 4 && name.substr(name.length() - 4) == "data") + { + name.erase(name.length() - 4, 4); + name += "type"; + + for (auto field = fields; field->mode != struct_field_info::END; field++) + { + if (field->name == name) + { + return field; + } + } + } + + if (name.length() > 7 && + name.substr(name.length() - 7) == "_target" && + fields != union_field && + (union_field - 1)->name == name.substr(0, name.length() - 7)) + { + return union_field - 1; + } + + return union_field + 1; +} + +const struct_field_info *DFHack::find_union_tag(const struct_field_info *fields, const struct_field_info *union_field) +{ + CHECK_NULL_POINTER(fields); + CHECK_NULL_POINTER(union_field); + + auto tag_candidate = find_union_tag_candidate(fields, union_field); + + if (union_field->mode == struct_field_info::SUBSTRUCT && + union_field->type && + union_field->type->type() == IDTYPE_UNION) + { + // union field + + if (tag_candidate->mode == struct_field_info::PRIMITIVE && + tag_candidate->type && + tag_candidate->type->type() == IDTYPE_ENUM) + { + return tag_candidate; + } + + return nullptr; + } + + if (union_field->mode != struct_field_info::CONTAINER || + !union_field->type || + union_field->type->type() != IDTYPE_CONTAINER) + { + // not a union field or a vector; bail + return nullptr; + } + + auto container_type = static_cast(union_field->type); + if (container_type->getFullName(nullptr) != "vector" || + !container_type->getItemType() || + container_type->getItemType()->type() != IDTYPE_UNION) + { + // not a vector of unions + return nullptr; + } + + if (tag_candidate->mode != struct_field_info::CONTAINER || + !tag_candidate->type || + tag_candidate->type->type() != IDTYPE_CONTAINER) + { + // candidate is not a vector + return nullptr; + } + + auto tag_container_type = static_cast(tag_candidate->type); + if (tag_container_type->getFullName(nullptr) == "vector" && + tag_container_type->getItemType() && + tag_container_type->getItemType()->type() == IDTYPE_ENUM) + { + return tag_candidate; + } + + return nullptr; +} diff --git a/library/Types.cpp b/library/Types.cpp index 5067c32ae..420f3b866 100644 --- a/library/Types.cpp +++ b/library/Types.cpp @@ -127,7 +127,7 @@ bool DFHack::removeRef(std::vector &vec, df::specific_ref_typ for (int i = vec.size()-1; i >= 0; i--) { df::specific_ref *ref = vec[i]; - if (ref->type != type || ref->object != ptr) + if (ref->type != type || ref->data.object != ptr) continue; vector_erase_at(vec, i); diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index f772629cf..704b1abf7 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -796,6 +796,16 @@ namespace DFHack { flagarray_to_string(&tmp, val); return join_strings(sep, tmp); } + + /** + * Finds the tag field for a given union field. + * + * The returned tag field is a primitive enum field or nullptr. + * + * If the union field is a container type, the returned tag field is + * a container of primitive enum types. + */ + DFHACK_EXPORT const struct_field_info *find_union_tag(const struct_field_info *fields, const struct_field_info *union_field); } #define ENUM_ATTR(enum,attr,val) (df::enum_traits::attrs(val).attr) diff --git a/library/modules/Items.cpp b/library/modules/Items.cpp index a1b4ace4d..4939aa9dd 100644 --- a/library/modules/Items.cpp +++ b/library/modules/Items.cpp @@ -658,7 +658,7 @@ df::coord Items::getPosition(df::item *item) switch (ref->type) { case specific_ref_type::VERMIN_ESCAPED_PET: - return ref->vermin->pos; + return ref->data.VERMIN_ESCAPED_PET->pos; default: break; diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp index 7673737b2..d0487941f 100644 --- a/library/modules/Job.cpp +++ b/library/modules/Job.cpp @@ -311,7 +311,7 @@ void DFHack::Job::disconnectJobItem(df::job *job, df::job_item_ref *ref) { auto ref = item->specific_refs[refIndex]; if (ref->type == df::specific_ref_type::JOB) { - if (ref->job == job) { + if (ref->data.JOB == job) { vector_erase_at(item->specific_refs, refIndex); delete ref; } else { @@ -579,7 +579,7 @@ bool DFHack::Job::attachJobItem(df::job *job, df::item *item, auto item_link = new df::specific_ref(); item_link->type = specific_ref_type::JOB; - item_link->job = job; + item_link->data.JOB = job; item->specific_refs.push_back(item_link); auto job_link = new df::job_item_ref(); diff --git a/library/modules/Units.cpp b/library/modules/Units.cpp index 1907b1641..3ac235203 100644 --- a/library/modules/Units.cpp +++ b/library/modules/Units.cpp @@ -1001,7 +1001,7 @@ int Units::computeMovementSpeed(df::unit *unit) if (in_magma) speed *= 2; - if (craw->flags.is_set(caste_raw_flags::SWIMS_LEARNED)) + if (craw->flags.is_set(caste_raw_flags::CAN_SWIM)) { int skill = Units::getEffectiveSkill(unit, job_skill::SWIMMING); @@ -1441,7 +1441,7 @@ int8_t Units::getCasteProfessionColor(int race, int casteid, df::profession pid) { if (auto caste = vector_get(creature->caste, casteid)) { - if (caste->flags.is_set(caste_raw_flags::CASTE_COLOR)) + if (caste->flags.is_set(caste_raw_flags::HAS_COLOR)) return caste->caste_color[0] + caste->caste_color[2] * 8; } return creature->color[0] + creature->color[2] * 8; diff --git a/library/xml b/library/xml index 212e3b4d6..167d54bf7 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 212e3b4d69b65c260a034522429c0b39c8f09bc0 +Subproject commit 167d54bf7c0e01a71ffb2de548701adc93da9dad diff --git a/plugins/devel/check-structures-sanity.cpp b/plugins/devel/check-structures-sanity.cpp index 6b439692f..b5cd1aa5f 100644 --- a/plugins/devel/check-structures-sanity.cpp +++ b/plugins/devel/check-structures-sanity.cpp @@ -33,6 +33,10 @@ static command_result command(color_ostream &, std::vector &); #define UNEXPECTED __asm__ volatile ("int $0x03") #endif +#define MIN_SIZE_FOR_SUGGEST 64 +static std::map> known_types_by_size; +static void build_size_table(); + DFhackCExport command_result plugin_init(color_ostream &, std::vector & commands) { commands.push_back(PluginCommand( @@ -40,18 +44,35 @@ DFhackCExport command_result plugin_init(color_ostream &, std::vectorbyte_size() >= MIN_SIZE_FOR_SUGGEST) + { + known_types_by_size[ident->byte_size()].push_back(ident->getFullName()); + } + } +} + static const char *const *get_enum_item_key(enum_identity *identity, int64_t value) { size_t index; @@ -76,124 +97,6 @@ static const char *const *get_enum_item_key(enum_identity *identity, int64_t val return &identity->getKeys()[index]; } -static const struct_field_info *find_union_tag(const struct_field_info *fields, const struct_field_info *union_field) -{ - if (union_field->mode != struct_field_info::SUBSTRUCT || - !union_field->type || - union_field->type->type() != IDTYPE_UNION) - { - // not a union - return nullptr; - } - - const struct_field_info *tag_field = union_field + 1; - - std::string name(union_field->name); - if (name.length() >= 4 && name.substr(name.length() - 4) == "data") - { - name.erase(name.length() - 4, 4); - name += "type"; - - if (tag_field->mode != struct_field_info::END && tag_field->name == name) - { - // fast path; we already have the correct field - } - else - { - for (auto field = fields; field->mode != struct_field_info::END; field++) - { - if (field->name == name) - { - tag_field = field; - break; - } - } - } - } - else if (name.length() > 7 && name.substr(name.length() - 7) == "_target" && fields != union_field && (union_field - 1)->name == name.substr(0, name.length() - 7)) - { - tag_field = union_field - 1; - } - - if (tag_field->mode != struct_field_info::PRIMITIVE || - !tag_field->type || - tag_field->type->type() != IDTYPE_ENUM) - { - // no tag - return nullptr; - } - - return tag_field; -} - -static const struct_field_info *find_union_vector_tag_vector(const struct_field_info *fields, const struct_field_info *union_field) -{ - if (union_field->mode != struct_field_info::CONTAINER || - !union_field->type || - union_field->type->type() != IDTYPE_CONTAINER) - { - // not a vector - return nullptr; - } - - auto container_type = static_cast(union_field->type); - if (container_type->getFullName(nullptr) != "vector" || - !container_type->getItemType() || - container_type->getItemType()->type() != IDTYPE_UNION) - { - // not a union - return nullptr; - } - - const struct_field_info *tag_field = union_field + 1; - - std::string name(union_field->name); - if (name.length() >= 4 && name.substr(name.length() - 4) == "data") - { - name.erase(name.length() - 4, 4); - name += "type"; - - if (tag_field->mode != struct_field_info::END && tag_field->name == name) - { - // fast path; we already have the correct field - } - else - { - for (auto field = fields; field->mode != struct_field_info::END; field++) - { - if (field->name == name) - { - tag_field = field; - break; - } - } - } - } - else if (name.length() > 7 && name.substr(name.length() - 7) == "_target" && fields != union_field && (union_field - 1)->name == name.substr(0, name.length() - 7)) - { - tag_field = union_field - 1; - } - - if (tag_field->mode != struct_field_info::CONTAINER || - !tag_field->type || - tag_field->type->type() != IDTYPE_CONTAINER) - { - // no tag vector - return nullptr; - } - - auto tag_container_type = static_cast(tag_field->type); - if (tag_container_type->getFullName(nullptr) != "vector" || - !tag_container_type->getItemType() || - tag_container_type->getItemType()->type() != IDTYPE_ENUM) - { - // not an enum - return nullptr; - } - - return tag_field; -} - struct ToCheck { std::vector path; @@ -230,6 +133,8 @@ public: bool enums; bool sizes; bool lowmem; + bool failfast; + size_t maxerrors; private: bool ok; @@ -239,7 +144,7 @@ private: #endif bool check_access(const ToCheck &, void *, type_identity *); bool check_access(const ToCheck &, void *, type_identity *, size_t); - bool check_vtable(const ToCheck &, void *, type_identity *); + const char *check_vtable(const ToCheck &, void *, type_identity *); void queue_field(ToCheck &&, const struct_field_info *); void queue_static_array(const ToCheck &, void *, type_identity *, size_t, bool = false, enum_identity * = nullptr); bool maybe_queue_union(const ToCheck &, const struct_field_info *, const struct_field_info *); @@ -272,9 +177,34 @@ static command_result command(color_ostream & out, std::vector & pa Checker checker(out); + // check parameters with values first +#define VAL_PARAM(name, expr_using_value) \ + auto name ## _idx = std::find(parameters.begin(), parameters.end(), "-" #name); \ + if (name ## _idx != parameters.end()) \ + { \ + if (name ## _idx + 1 == parameters.end()) \ + { \ + return CR_WRONG_USAGE; \ + } \ + try \ + { \ + auto value = std::move(*(name ## _idx + 1)); \ + parameters.erase((name ## _idx + 1)); \ + parameters.erase(name ## _idx); \ + checker.name = (expr_using_value); \ + } \ + catch (std::exception & ex) \ + { \ + out.printerr("check-structures-sanity: argument to -%s: %s\n", #name, ex.what()); \ + return CR_WRONG_USAGE; \ + } \ + } + VAL_PARAM(maxerrors, std::stoul(value)); +#undef VAL_PARAM + #define BOOL_PARAM(name) \ auto name ## _idx = std::find(parameters.begin(), parameters.end(), "-" #name); \ - if (name ## _idx != parameters.cend()) \ + if (name ## _idx != parameters.end()) \ { \ checker.name = true; \ parameters.erase(name ## _idx); \ @@ -282,6 +212,7 @@ static command_result command(color_ostream & out, std::vector & pa BOOL_PARAM(enums); BOOL_PARAM(sizes); BOOL_PARAM(lowmem); + BOOL_PARAM(failfast); #undef BOOL_PARAM if (parameters.size() > 1) @@ -318,7 +249,6 @@ static command_result command(color_ostream & out, std::vector & pa ToCheck ref; ref.path.push_back(parameters.at(0)); - ref.path.push_back(""); // tell check_struct that it is a pointer ref.ptr = get_object_ref(State, -1); lua_getfield(State, -1, "_type"); lua_getfield(State, -1, "_identity"); @@ -342,6 +272,8 @@ Checker::Checker(color_ostream & out) : enums = false; sizes = false; lowmem = false; + failfast = false; + maxerrors = ~size_t(0); } bool Checker::check() @@ -352,6 +284,12 @@ bool Checker::check() while (!queue.empty()) { + if (!maxerrors) + { + out << "hit max error count. bailing out with " << queue.size() << " fields in queue." << std::endl; + break; + } + ToCheck current; if (lowmem) { @@ -388,6 +326,10 @@ bool Checker::check() out << "): "; \ out << COLOR_YELLOW << message; \ out << COLOR_RESET << std::endl; \ + if (maxerrors && maxerrors != ~size_t(0)) \ + maxerrors--; \ + if (failfast) \ + UNEXPECTED; \ } while (false) #define PTR_ADD(base, offset) (reinterpret_cast(reinterpret_cast((base)) + static_cast((offset)))) @@ -521,20 +463,20 @@ bool Checker::check_access(const ToCheck & item, void *base, type_identity *iden #undef FAIL_PTR } -bool Checker::check_vtable(const ToCheck & item, void *vtable, type_identity *identity) +const char *Checker::check_vtable(const ToCheck & item, void *vtable, type_identity *identity) { if (!check_access(item, PTR_ADD(vtable, -ptrdiff_t(sizeof(void *))), identity, sizeof(void *))) - return false; + return nullptr; char **info = *(reinterpret_cast(vtable) - 1); #ifdef WIN32 if (!check_access(item, PTR_ADD(info, 12), identity, 4)) - return false; + return nullptr; #ifdef DFHACK64 void *base; if (!RtlPcToFileHeader(info, &base)) - return false; + return nullptr; char *typeinfo = reinterpret_cast(base) + reinterpret_cast(info)[3]; char *name = typeinfo + 16; @@ -543,7 +485,7 @@ bool Checker::check_vtable(const ToCheck & item, void *vtable, type_identity *id #endif #else if (!check_access(item, info + 1, identity, sizeof(void *))) - return false; + return nullptr; char *name = *(info + 1); #endif @@ -557,7 +499,7 @@ bool Checker::check_vtable(const ToCheck & item, void *vtable, type_identity *id if (!range.valid || !range.read) { FAIL("pointer to invalid memory range"); - return false; + return nullptr; } bool letter = false; @@ -565,7 +507,7 @@ bool Checker::check_vtable(const ToCheck & item, void *vtable, type_identity *id { if (!range.isInRange(p)) { - return false; + return nullptr; } if (*p >= 'a' && *p <= 'z') @@ -574,12 +516,12 @@ bool Checker::check_vtable(const ToCheck & item, void *vtable, type_identity *id } else if (!*p) { - return letter; + return letter ? name : nullptr; } } } - return false; + return nullptr; } void Checker::queue_field(ToCheck && item, const struct_field_info *field) @@ -660,27 +602,18 @@ void Checker::queue_static_array(const ToCheck & array, void *base, type_identit bool Checker::maybe_queue_union(const ToCheck & item, const struct_field_info *fields, const struct_field_info *union_field) { auto tag_field = find_union_tag(fields, union_field); - if (tag_field) - { - ToCheck union_item(item, "." + std::string(union_field->name), PTR_ADD(item.ptr, union_field->offset), union_field->type); - ToCheck tag_item(item, "." + std::string(tag_field->name), PTR_ADD(item.ptr, tag_field->offset), tag_field->type); - queue_union(union_item, tag_item); - - return true; - } - - tag_field = find_union_vector_tag_vector(fields, union_field); - if (tag_field) - { - ToCheck union_vector_item(item, "." + std::string(union_field->name), PTR_ADD(item.ptr, union_field->offset), union_field->type); - ToCheck tag_vector_item(item, "." + std::string(tag_field->name), PTR_ADD(item.ptr, tag_field->offset), tag_field->type); + if (!tag_field) + return false; - queue_union_vector(union_vector_item, tag_vector_item); + ToCheck union_item(item, "." + std::string(union_field->name), PTR_ADD(item.ptr, union_field->offset), union_field->type); + ToCheck tag_item(item, "." + std::string(tag_field->name), PTR_ADD(item.ptr, tag_field->offset), tag_field->type); - return true; - } + if (union_field->mode == struct_field_info::SUBSTRUCT) + queue_union(union_item, tag_item); + else + queue_union_vector(union_item, tag_item); - return false; + return true; } void Checker::queue_union(const ToCheck & item, const ToCheck & tag_item) @@ -813,6 +746,10 @@ void Checker::check_dispatch(ToCheck & item) item.path.push_back(""); item.identity = df::identity_traits::get(); } + else if (allocated_size >= MIN_SIZE_FOR_SUGGEST && known_types_by_size.count(allocated_size)) + { + FAIL("known types of this size: " << join_strings(", ", known_types_by_size.at(allocated_size))); + } } #ifndef WIN32 else if (auto str = check_possible_stl_string_pointer(&item.ptr)) @@ -820,6 +757,10 @@ void Checker::check_dispatch(ToCheck & item) FAIL("untyped pointer is actually stl-string with value \"" << *str << "\" (length " << str->length() << ")"); } #endif + else if (auto vtable_name = check_vtable(item, item.ptr, df::identity_traits::get())) + { + FAIL("pointer to a vtable: " << vtable_name); + } else { FAIL("pointer to memory with no size information"); @@ -1035,7 +976,8 @@ void Checker::check_stl_string(const ToCheck & item) } else { - UNEXPECTED; + FAIL("pointer does not appear to be a string"); + //UNEXPECTED; } } #endif @@ -1369,7 +1311,8 @@ void Checker::check_struct(const ToCheck & item) } else { - UNEXPECTED; + FAIL("unknown allocation size; possibly bad"); + //UNEXPECTED; } } diff --git a/plugins/fastdwarf.cpp b/plugins/fastdwarf.cpp index 09d831e26..975280ea1 100644 --- a/plugins/fastdwarf.cpp +++ b/plugins/fastdwarf.cpp @@ -123,55 +123,55 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) case unit_action_type::None: break; case unit_action_type::Move: - action->data.move.timer = 1; + action->data.Move.timer = 1; break; case unit_action_type::Attack: // Attacks are executed when timer1 reaches zero, which will be // on the following tick. - if (action->data.attack.timer1 > 1) - action->data.attack.timer1 = 1; + if (action->data.Attack.timer1 > 1) + action->data.Attack.timer1 = 1; // Attack actions are completed, and new ones generated, when // timer2 reaches zero. - if (action->data.attack.timer2 > 1) - action->data.attack.timer2 = 1; + if (action->data.Attack.timer2 > 1) + action->data.Attack.timer2 = 1; break; case unit_action_type::HoldTerrain: - action->data.holdterrain.timer = 1; + action->data.HoldTerrain.timer = 1; break; case unit_action_type::Climb: - action->data.climb.timer = 1; + action->data.Climb.timer = 1; break; case unit_action_type::Job: - action->data.job.timer = 1; + action->data.Job.timer = 1; // could also patch the unit->job.current_job->completion_timer break; case unit_action_type::Talk: - action->data.talk.timer = 1; + action->data.Talk.timer = 1; break; case unit_action_type::Unsteady: - action->data.unsteady.timer = 1; + action->data.Unsteady.timer = 1; break; case unit_action_type::Dodge: - action->data.dodge.timer = 1; + action->data.Dodge.timer = 1; break; case unit_action_type::Recover: - action->data.recover.timer = 1; + action->data.Recover.timer = 1; break; case unit_action_type::StandUp: - action->data.standup.timer = 1; + action->data.StandUp.timer = 1; break; case unit_action_type::LieDown: - action->data.liedown.timer = 1; + action->data.LieDown.timer = 1; break; case unit_action_type::Job2: - action->data.job2.timer = 1; + action->data.Job2.timer = 1; // could also patch the unit->job.current_job->completion_timer break; case unit_action_type::PushObject: - action->data.pushobject.timer = 1; + action->data.PushObject.timer = 1; break; case unit_action_type::SuckBlood: - action->data.suckblood.timer = 1; + action->data.SuckBlood.timer = 1; break; case unit_action_type::Jump: case unit_action_type::ReleaseTerrain: diff --git a/plugins/remotefortressreader/remotefortressreader.cpp b/plugins/remotefortressreader/remotefortressreader.cpp index f63ae5b2c..aeb242250 100644 --- a/plugins/remotefortressreader/remotefortressreader.cpp +++ b/plugins/remotefortressreader/remotefortressreader.cpp @@ -1825,17 +1825,17 @@ static command_result GetUnitListInside(color_ostream &stream, const BlockReques case unit_action_type::Move: if (unit->path.path.x.size() > 0) { - send_unit->set_subpos_x(lerp(0, unit->path.path.x[0] - unit->pos.x, (float)(action->data.move.timer_init - action->data.move.timer) / action->data.move.timer_init)); - send_unit->set_subpos_y(lerp(0, unit->path.path.y[0] - unit->pos.y, (float)(action->data.move.timer_init - action->data.move.timer) / action->data.move.timer_init)); - send_unit->set_subpos_z(lerp(0, unit->path.path.z[0] - unit->pos.z, (float)(action->data.move.timer_init - action->data.move.timer) / action->data.move.timer_init)); + send_unit->set_subpos_x(lerp(0, unit->path.path.x[0] - unit->pos.x, (float)(action->data.Move.timer_init - action->data.Move.timer) / action->data.Move.timer_init)); + send_unit->set_subpos_y(lerp(0, unit->path.path.y[0] - unit->pos.y, (float)(action->data.Move.timer_init - action->data.Move.timer) / action->data.Move.timer_init)); + send_unit->set_subpos_z(lerp(0, unit->path.path.z[0] - unit->pos.z, (float)(action->data.Move.timer_init - action->data.Move.timer) / action->data.Move.timer_init)); } break; case unit_action_type::Job: { auto facing = send_unit->mutable_facing(); - facing->set_x(action->data.job.x - unit->pos.x); - facing->set_y(action->data.job.y - unit->pos.y); - facing->set_z(action->data.job.z - unit->pos.z); + facing->set_x(action->data.Job.x - unit->pos.x); + facing->set_y(action->data.Job.y - unit->pos.y); + facing->set_z(action->data.Job.z - unit->pos.z); } default: break; diff --git a/plugins/stocks.cpp b/plugins/stocks.cpp index b5831d159..d9ec97c0d 100644 --- a/plugins/stocks.cpp +++ b/plugins/stocks.cpp @@ -179,8 +179,8 @@ static map items_in_cages; static df::job *get_item_job(df::item *item) { auto ref = Items::getSpecificRef(item, specific_ref_type::JOB); - if (ref && ref->job) - return ref->job; + if (ref && ref->data.JOB) + return ref->data.JOB; return nullptr; } @@ -1008,12 +1008,12 @@ private: if (item->flags.bits.in_job) { auto ref = Items::getSpecificRef(item, specific_ref_type::JOB); - if (ref && ref->job) + if (ref && ref->data.JOB) { - if (ref->job->job_type == job_type::Eat || ref->job->job_type == job_type::Drink) + if (ref->data.JOB->job_type == job_type::Eat || ref->data.JOB->job_type == job_type::Drink) return pos; - auto unit = Job::getWorker(ref->job); + auto unit = Job::getWorker(ref->data.JOB); if (unit) return unit->pos; } diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index 18490039e..3e31d14ab 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -1151,10 +1151,10 @@ static bool itemInRealJob(df::item *item) return false; auto ref = Items::getSpecificRef(item, specific_ref_type::JOB); - if (!ref || !ref->job) + if (!ref || !ref->data.JOB) return true; - return ENUM_ATTR(job_type, type, ref->job->job_type) + return ENUM_ATTR(job_type, type, ref->data.JOB->job_type) != job_type_class::Hauling; } diff --git a/scripts b/scripts index 76d84e1ae..7bab11642 160000 --- a/scripts +++ b/scripts @@ -1 +1 @@ -Subproject commit 76d84e1aed23e3ec1690037ea3454f920b60d86a +Subproject commit 7bab11642bee7a3aa05d69332466f2ea5eaa1a2d