From 89edfd4e15ee79942472a6e763e9607780a9b3f6 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 16 Mar 2012 21:45:52 -0700 Subject: [PATCH 01/25] Initial Autolabor code. --- plugins/CMakeLists.txt | 5 + plugins/autolabor/CMakeLists.txt | 31 ++ plugins/autolabor/autolabor.cpp | 812 +++++++++++++++++++++++++++++++ plugins/autolabor/autolabor.h | 1 + 4 files changed, 849 insertions(+) create mode 100644 plugins/autolabor/CMakeLists.txt create mode 100644 plugins/autolabor/autolabor.cpp create mode 100644 plugins/autolabor/autolabor.h diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 1574b5a28..28ea180f2 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -45,6 +45,11 @@ if (BUILD_DWARFEXPORT) add_subdirectory (dwarfexport) endif() +OPTION(BUILD_AUTOLABOR "Build autolabor." ON) +if (BUILD_AUTOLABOR) + add_subdirectory (autolabor) +endif() + # Protobuf FILE(GLOB PROJECT_PROTOS ${CMAKE_CURRENT_SOURCE_DIR}/proto/*.proto) diff --git a/plugins/autolabor/CMakeLists.txt b/plugins/autolabor/CMakeLists.txt new file mode 100644 index 000000000..124e36bbe --- /dev/null +++ b/plugins/autolabor/CMakeLists.txt @@ -0,0 +1,31 @@ +PROJECT (autolabor) +# A list of source files +SET(PROJECT_SRCS + autolabor.cpp +) +# A list of headers +SET(PROJECT_HDRS + autolabor.h +) +SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE) + +# mash them together (headers are marked as headers and nothing will try to compile them) +LIST(APPEND PROJECT_SRCS ${PROJECT_HDRS}) + +#linux +IF(UNIX) + add_definitions(-DLINUX_BUILD) + SET(PROJECT_LIBS + # add any extra linux libs here + ${PROJECT_LIBS} + ) +# windows +ELSE(UNIX) + SET(PROJECT_LIBS + # add any extra linux libs here + ${PROJECT_LIBS} + $(NOINHERIT) + ) +ENDIF(UNIX) +# this makes sure all the stuff is put in proper places and linked to dfhack +DFHACK_PLUGIN(autolabor ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS}) diff --git a/plugins/autolabor/autolabor.cpp b/plugins/autolabor/autolabor.cpp new file mode 100644 index 000000000..209d0c8f9 --- /dev/null +++ b/plugins/autolabor/autolabor.cpp @@ -0,0 +1,812 @@ +// This is a generic plugin that does nothing useful apart from acting as an example... of a plugin that does nothing :D + +// some headers required for a plugin. Nothing special, just the basics. +#include "Core.h" +#include +#include +#include + +#include +#include + +// DF data structure definition headers +#include "DataDefs.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace DFHack; +using namespace df::enums; +using df::global::ui; +using df::global::world; + +// our own, empty header. +#include "autolabor.h" + + +static int enable_autolabor; + + +// Here go all the command declarations... +// mostly to allow having the mandatory stuff on top of the file and commands on the bottom +command_result autolabor (color_ostream &out, std::vector & parameters); + +// A plugin must be able to return its name and version. +// The name string provided must correspond to the filename - autolabor.plug.so or autolabor.plug.dll in this case +DFHACK_PLUGIN("autolabor"); + +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ + // Fill the command list with your commands. + commands.clear(); + commands.push_back(PluginCommand( + "autolabor", "Do nothing, look pretty.", + autolabor, false, /* true means that the command can't be used from non-interactive user interface */ + // Extended help string. Used by CR_WRONG_USAGE and the help command: + " This command does nothing at all.\n" + "Example:\n" + " autolabor\n" + " Does nothing.\n" + )); + return CR_OK; +} + +// This is called right before the plugin library is removed from memory. +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + // You *MUST* kill all threads you created before this returns. + // If everything fails, just return CR_FAILURE. Your plugin will be + // in a zombie state, but things won't crash. + return CR_OK; +} + +// Called to notify the plugin about important state changes. +// Invoked with DF suspended, and always before the matching plugin_onupdate. +// More event codes may be added in the future. +/* +DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) +{ + switch (event) { + case SC_GAME_LOADED: + // initialize from the world just loaded + break; + case SC_GAME_UNLOADED: + // cleanup + break; + default: + break; + } + return CR_OK; +} +*/ + +enum labor_mode { + EVERYONE, + HAULERS, + AUTOMATIC, + FIXED, +}; + +enum dwarf_state { + // Ready for a new task + IDLE, + + // Busy with a useful task + BUSY, + + // In the military, can't work + MILITARY, + + // Child or noble, can't work + CHILD, + + // Doing something that precludes working, may be busy for a while + OTHER +}; + +static const dwarf_state dwarf_states[] = { + BUSY /* CarveFortification */, + BUSY /* DetailWall */, + BUSY /* DetailFloor */, + BUSY /* Dig */, + BUSY /* CarveUpwardStaircase */, + BUSY /* CarveDownwardStaircase */, + BUSY /* CarveUpDownStaircase */, + BUSY /* CarveRamp */, + BUSY /* DigChannel */, + BUSY /* FellTree */, + BUSY /* GatherPlants */, + BUSY /* RemoveConstruction */, + BUSY /* CollectWebs */, + BUSY /* BringItemToDepot */, + BUSY /* BringItemToShop */, + OTHER /* Eat */, + OTHER /* GetProvisions */, + OTHER /* Drink */, + OTHER /* Drink2 */, + OTHER /* FillWaterskin */, + OTHER /* FillWaterskin2 */, + OTHER /* Sleep */, + BUSY /* CollectSand */, + BUSY /* Fish */, + BUSY /* Hunt */, + OTHER /* HuntVermin */, + BUSY /* Kidnap */, + BUSY /* BeatCriminal */, + BUSY /* StartingFistFight */, + BUSY /* CollectTaxes */, + BUSY /* GuardTaxCollector */, + BUSY /* CatchLiveLandAnimal */, + BUSY /* CatchLiveFish */, + BUSY /* ReturnKill */, + BUSY /* CheckChest */, + BUSY /* StoreOwnedItem */, + BUSY /* PlaceItemInTomb */, + BUSY /* StoreItemInStockpile */, + BUSY /* StoreItemInBag */, + BUSY /* StoreItemInHospital */, + BUSY /* StoreItemInChest */, + BUSY /* StoreItemInCabinet */, + BUSY /* StoreWeapon */, + BUSY /* StoreArmor */, + BUSY /* StoreItemInBarrel */, + BUSY /* StoreItemInBin */, + BUSY /* SeekArtifact */, + BUSY /* SeekInfant */, + OTHER /* AttendParty */, + OTHER /* GoShopping */, + OTHER /* GoShopping2 */, + BUSY /* Clean */, + OTHER /* Rest */, + BUSY /* PickupEquipment */, + BUSY /* DumpItem */, + OTHER /* StrangeMoodCrafter */, + OTHER /* StrangeMoodJeweller */, + OTHER /* StrangeMoodForge */, + OTHER /* StrangeMoodMagmaForge */, + OTHER /* StrangeMoodBrooding */, + OTHER /* StrangeMoodFell */, + OTHER /* StrangeMoodCarpenter */, + OTHER /* StrangeMoodMason */, + OTHER /* StrangeMoodBowyer */, + OTHER /* StrangeMoodTanner */, + OTHER /* StrangeMoodWeaver */, + OTHER /* StrangeMoodGlassmaker */, + OTHER /* StrangeMoodMechanics */, + BUSY /* ConstructBuilding */, + BUSY /* ConstructDoor */, + BUSY /* ConstructFloodgate */, + BUSY /* ConstructBed */, + BUSY /* ConstructThrone */, + BUSY /* ConstructCoffin */, + BUSY /* ConstructTable */, + BUSY /* ConstructChest */, + BUSY /* ConstructBin */, + BUSY /* ConstructArmorStand */, + BUSY /* ConstructWeaponRack */, + BUSY /* ConstructCabinet */, + BUSY /* ConstructStatue */, + BUSY /* ConstructBlocks */, + BUSY /* MakeRawGlass */, + BUSY /* MakeCrafts */, + BUSY /* MintCoins */, + BUSY /* CutGems */, + BUSY /* CutGlass */, + BUSY /* EncrustWithGems */, + BUSY /* EncrustWithGlass */, + BUSY /* DestroyBuilding */, + BUSY /* SmeltOre */, + BUSY /* MeltMetalObject */, + BUSY /* ExtractMetalStrands */, + BUSY /* PlantSeeds */, + BUSY /* HarvestPlants */, + BUSY /* TrainHuntingAnimal */, + BUSY /* TrainWarAnimal */, + BUSY /* MakeWeapon */, + BUSY /* ForgeAnvil */, + BUSY /* ConstructCatapultParts */, + BUSY /* ConstructBallistaParts */, + BUSY /* MakeArmor */, + BUSY /* MakeHelm */, + BUSY /* MakePants */, + BUSY /* StudWith */, + BUSY /* ButcherAnimal */, + BUSY /* PrepareRawFish */, + BUSY /* MillPlants */, + BUSY /* BaitTrap */, + BUSY /* MilkCreature */, + BUSY /* MakeCheese */, + BUSY /* ProcessPlants */, + BUSY /* ProcessPlantsBag */, + BUSY /* ProcessPlantsVial */, + BUSY /* ProcessPlantsBarrel */, + BUSY /* PrepareMeal */, + BUSY /* WeaveCloth */, + BUSY /* MakeGloves */, + BUSY /* MakeShoes */, + BUSY /* MakeShield */, + BUSY /* MakeCage */, + BUSY /* MakeChain */, + BUSY /* MakeFlask */, + BUSY /* MakeGoblet */, + BUSY /* MakeInstrument */, + BUSY /* MakeToy */, + BUSY /* MakeAnimalTrap */, + BUSY /* MakeBarrel */, + BUSY /* MakeBucket */, + BUSY /* MakeWindow */, + BUSY /* MakeTotem */, + BUSY /* MakeAmmo */, + BUSY /* DecorateWith */, + BUSY /* MakeBackpack */, + BUSY /* MakeQuiver */, + BUSY /* MakeBallistaArrowHead */, + BUSY /* AssembleSiegeAmmo */, + BUSY /* LoadCatapult */, + BUSY /* LoadBallista */, + BUSY /* FireCatapult */, + BUSY /* FireBallista */, + BUSY /* ConstructMechanisms */, + BUSY /* MakeTrapComponent */, + BUSY /* LoadCageTrap */, + BUSY /* LoadStoneTrap */, + BUSY /* LoadWeaponTrap */, + BUSY /* CleanTrap */, + BUSY /* CastSpell */, + BUSY /* LinkBuildingToTrigger */, + BUSY /* PullLever */, + BUSY /* BrewDrink */, + BUSY /* ExtractFromPlants */, + BUSY /* ExtractFromRawFish */, + BUSY /* ExtractFromLandAnimal */, + BUSY /* TameVermin */, + BUSY /* TameAnimal */, + BUSY /* ChainAnimal */, + BUSY /* UnchainAnimal */, + BUSY /* UnchainPet */, + BUSY /* ReleaseLargeCreature */, + BUSY /* ReleasePet */, + BUSY /* ReleaseSmallCreature */, + BUSY /* HandleSmallCreature */, + BUSY /* HandleLargeCreature */, + BUSY /* CageLargeCreature */, + BUSY /* CageSmallCreature */, + BUSY /* RecoverWounded */, + BUSY /* DiagnosePatient */, + BUSY /* ImmobilizeBreak */, + BUSY /* DressWound */, + BUSY /* CleanPatient */, + BUSY /* Surgery */, + BUSY /* Suture */, + BUSY /* SetBone */, + BUSY /* PlaceInTraction */, + BUSY /* DrainAquarium */, + BUSY /* FillAquarium */, + BUSY /* FillPond */, + BUSY /* GiveWater */, + BUSY /* GiveFood */, + BUSY /* GiveWater2 */, + BUSY /* GiveFood2 */, + BUSY /* RecoverPet */, + BUSY /* PitLargeAnimal */, + BUSY /* PitSmallAnimal */, + BUSY /* SlaughterAnimal */, + BUSY /* MakeCharcoal */, + BUSY /* MakeAsh */, + BUSY /* MakeLye */, + BUSY /* MakePotashFromLye */, + BUSY /* FertilizeField */, + BUSY /* MakePotashFromAsh */, + BUSY /* DyeThread */, + BUSY /* DyeCloth */, + BUSY /* SewImage */, + BUSY /* MakePipeSection */, + BUSY /* OperatePump */, + OTHER /* ManageWorkOrders */, + OTHER /* UpdateStockpileRecords */, + OTHER /* TradeAtDepot */, + BUSY /* ConstructHatchCover */, + BUSY /* ConstructGrate */, + BUSY /* RemoveStairs */, + BUSY /* ConstructQuern */, + BUSY /* ConstructMillstone */, + BUSY /* ConstructSplint */, + BUSY /* ConstructCrutch */, + BUSY /* ConstructTractionBench */, + BUSY /* CleanSelf */, + BUSY /* BringCrutch */, + BUSY /* ApplyCast */, + BUSY /* CustomReaction */, + BUSY /* ConstructSlab */, + BUSY /* EngraveSlab */, + BUSY /* ShearCreature */, + BUSY /* SpinThread */, + BUSY /* PenLargeAnimal */, + BUSY /* PenSmallAnimal */, + BUSY /* MakeTool */, + BUSY /* CollectClay */, + BUSY /* InstallColonyInHive */, + BUSY /* CollectHiveProducts */, + OTHER /* CauseTrouble */, + OTHER /* DrinkBlood */, + OTHER /* ReportCrime */, + OTHER /* ExecuteCriminal */ +}; + +struct labor_info +{ + labor_mode mode; + bool is_exclusive; + int minimum_dwarfs; +}; + +static const struct labor_info labor_info[] = { + /* MINE */ {AUTOMATIC, true, 2}, + /* HAUL_STONE */ {HAULERS, false, 1}, + /* HAUL_WOOD */ {HAULERS, false, 1}, + /* HAUL_BODY */ {HAULERS, false, 1}, + /* HAUL_FOOD */ {HAULERS, false, 1}, + /* HAUL_REFUSE */ {HAULERS, false, 1}, + /* HAUL_ITEM */ {HAULERS, false, 1}, + /* HAUL_FURNITURE */ {HAULERS, false, 1}, + /* HAUL_ANIMAL */ {HAULERS, false, 1}, + /* CLEAN */ {HAULERS, false, 1}, + /* CUTWOOD */ {AUTOMATIC, true, 1}, + /* CARPENTER */ {AUTOMATIC, false, 1}, + /* DETAIL */ {AUTOMATIC, false, 1}, + /* MASON */ {AUTOMATIC, false, 1}, + /* ARCHITECT */ {AUTOMATIC, false, 1}, + /* ANIMALTRAIN */ {AUTOMATIC, false, 1}, + /* ANIMALCARE */ {AUTOMATIC, false, 1}, + /* DIAGNOSE */ {AUTOMATIC, false, 1}, + /* SURGERY */ {AUTOMATIC, false, 1}, + /* BONE_SETTING */ {AUTOMATIC, false, 1}, + /* SUTURING */ {AUTOMATIC, false, 1}, + /* DRESSING_WOUNDS */ {AUTOMATIC, false, 1}, + /* FEED_WATER_CIVILIANS */ {EVERYONE, false, 1}, + /* RECOVER_WOUNDED */ {HAULERS, false, 1}, + /* BUTCHER */ {AUTOMATIC, false, 1}, + /* TRAPPER */ {AUTOMATIC, false, 1}, + /* DISSECT_VERMIN */ {AUTOMATIC, false, 1}, + /* LEATHER */ {AUTOMATIC, false, 1}, + /* TANNER */ {AUTOMATIC, false, 1}, + /* BREWER */ {AUTOMATIC, false, 1}, + /* ALCHEMIST */ {AUTOMATIC, false, 1}, + /* SOAP_MAKER */ {AUTOMATIC, false, 1}, + /* WEAVER */ {AUTOMATIC, false, 1}, + /* CLOTHESMAKER */ {AUTOMATIC, false, 1}, + /* MILLER */ {AUTOMATIC, false, 1}, + /* PROCESS_PLANT */ {AUTOMATIC, false, 1}, + /* MAKE_CHEESE */ {AUTOMATIC, false, 1}, + /* MILK */ {AUTOMATIC, false, 1}, + /* COOK */ {AUTOMATIC, false, 1}, + /* PLANT */ {AUTOMATIC, false, 1}, + /* HERBALIST */ {AUTOMATIC, false, 1}, + /* FISH */ {FIXED, false, 1}, + /* CLEAN_FISH */ {AUTOMATIC, false, 1}, + /* DISSECT_FISH */ {AUTOMATIC, false, 1}, + /* HUNT */ {FIXED, true, 1}, + /* SMELT */ {AUTOMATIC, false, 1}, + /* FORGE_WEAPON */ {AUTOMATIC, false, 1}, + /* FORGE_ARMOR */ {AUTOMATIC, false, 1}, + /* FORGE_FURNITURE */ {AUTOMATIC, false, 1}, + /* METAL_CRAFT */ {AUTOMATIC, false, 1}, + /* CUT_GEM */ {AUTOMATIC, false, 1}, + /* ENCRUST_GEM */ {AUTOMATIC, false, 1}, + /* WOOD_CRAFT */ {AUTOMATIC, false, 1}, + /* STONE_CRAFT */ {AUTOMATIC, false, 1}, + /* BONE_CARVE */ {AUTOMATIC, false, 1}, + /* GLASSMAKER */ {AUTOMATIC, false, 1}, + /* EXTRACT_STRAND */ {AUTOMATIC, false, 1}, + /* SIEGECRAFT */ {AUTOMATIC, false, 1}, + /* SIEGEOPERATE */ {AUTOMATIC, false, 1}, + /* BOWYER */ {AUTOMATIC, false, 1}, + /* MECHANIC */ {AUTOMATIC, false, 1}, + /* POTASH_MAKING */ {AUTOMATIC, false, 1}, + /* LYE_MAKING */ {AUTOMATIC, false, 1}, + /* DYER */ {AUTOMATIC, false, 1}, + /* BURN_WOOD */ {AUTOMATIC, false, 1}, + /* OPERATE_PUMP */ {AUTOMATIC, false, 1}, + /* SHEARER */ {AUTOMATIC, false, 1}, + /* SPINNER */ {AUTOMATIC, false, 1}, + /* POTTERY */ {AUTOMATIC, false, 1}, + /* GLAZING */ {AUTOMATIC, false, 1}, + /* PRESSING */ {AUTOMATIC, false, 1}, + /* BEEKEEPING */ {AUTOMATIC, false, 1}, + /* WAX_WORKING */ {AUTOMATIC, false, 1}, +}; + +static const df::job_skill noble_skills[] = { + df::enums::job_skill::APPRAISAL, + df::enums::job_skill::ORGANIZATION, + df::enums::job_skill::RECORD_KEEPING, +}; + +struct dwarf_info +{ + int highest_skill; + int total_skill; + bool is_best_noble; + int mastery_penalty; + int assigned_jobs; + dwarf_state state; + bool has_exclusive_labor; +}; + +DFhackCExport command_result plugin_onupdate ( color_ostream &out ) +{ + static int step_count = 0; + + if (!enable_autolabor) + return CR_OK; + + if (++step_count < 60) + return CR_OK; + step_count = 0; + + uint32_t race = ui->race_id; + uint32_t civ = ui->civ_id; + + std::vector dwarfs; + + for (int i = 0; i < world->units.all.size(); ++i) + { + df::unit* cre = world->units.all[i]; + if (cre->race == race && cre->civ_id == civ && !cre->flags1.bits.marauder && !cre->flags1.bits.diplomat && !cre->flags1.bits.merchant) { + dwarfs.push_back(cre); + } + } + + int n_dwarfs = dwarfs.size(); + + if (n_dwarfs == 0) + return CR_OK; + + std::vector dwarf_info(n_dwarfs); + + std::vector best_noble(_countof(noble_skills)); + std::vector highest_noble_skill(_countof(noble_skills)); + std::vector highest_noble_experience(_countof(noble_skills)); + + // Find total skill and highest skill for each dwarf. More skilled dwarves shouldn't be used for minor tasks. + + for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) + { + assert(dwarfs[dwarf]->status.souls.size() > 0); + + for (auto s = dwarfs[dwarf]->status.souls[0]->skills.begin(); s < dwarfs[dwarf]->status.souls[0]->skills.end(); s++) + { + df::job_skill skill = (*s)->id; + + df::job_skill_class skill_class = ENUM_ATTR(job_skill, type, skill); + + int skill_level = (*s)->rating; + int skill_experience = (*s)->experience; + + // Track the dwarfs with the best Appraisal, Organization, and Record Keeping skills. + // They are likely to have appointed noble positions, so should be kept free where possible. + + int noble_skill_id = -1; + for (int i = 0; i < _countof(noble_skills); i++) + { + if (skill == noble_skills[i]) + noble_skill_id = i; + } + + if (noble_skill_id >= 0) + { + assert(noble_skill_id < _countof(noble_skills)); + + if (highest_noble_skill[noble_skill_id] < skill_level || + (highest_noble_skill[noble_skill_id] == skill_level && + highest_noble_experience[noble_skill_id] < skill_experience)) + { + highest_noble_skill[noble_skill_id] = skill_level; + highest_noble_experience[noble_skill_id] = skill_experience; + best_noble[noble_skill_id] = dwarf; + } + } + + // Track total & highest skill among normal/medical skills. (We don't care about personal or social skills.) + + if (skill_class != df::enums::job_skill_class::Normal && skill_class != df::enums::job_skill_class::Medical) + continue; + + if (dwarf_info[dwarf].highest_skill < skill_level) + dwarf_info[dwarf].highest_skill = skill_level; + dwarf_info[dwarf].total_skill += skill_level; + } + } + + // Mark the best nobles, so we try to keep them non-busy. (It would be better to find the actual assigned nobles.) + + for (int i = 0; i < _countof(noble_skills); i++) + { + assert(best_noble[i] >= 0); + assert(best_noble[i] < n_dwarfs); + + dwarf_info[best_noble[i]].is_best_noble = true; + } + + // Calculate a base penalty for using each dwarf for a task he isn't good at. + + for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) + { + dwarf_info[dwarf].mastery_penalty -= 40 * dwarf_info[dwarf].highest_skill; + dwarf_info[dwarf].mastery_penalty -= 10 * dwarf_info[dwarf].total_skill; + if (dwarf_info[dwarf].is_best_noble) + dwarf_info[dwarf].mastery_penalty -= 250; + + for (int labor = ENUM_FIRST_ITEM(unit_labor); labor <= ENUM_LAST_ITEM(unit_labor); labor++) + { + if (labor == df::enums::unit_labor::NONE) + continue; + + assert(labor >= 0); + assert(labor < _countof(labor_info)); + + if (labor_info[labor].is_exclusive && dwarfs[dwarf]->status.labors[labor]) + dwarf_info[dwarf].mastery_penalty -= 100; + } + } + + // Find the activity state for each dwarf. It's important to get this right - a dwarf who we think is IDLE but + // can't work will gum everything up. In the future I might add code to auto-detect slacker dwarves. + + std::vector state_count(5); + + for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) + { + bool is_on_break = false; + + for (auto p = dwarfs[dwarf]->status.misc_traits.begin(); p < dwarfs[dwarf]->status.misc_traits.end(); p++) + { + // 7 / 0x7 = Newly arrived migrant, will not work yet + // 17 / 0x11 = On break + if ((*p)->id == 0x07 || (*p)->id == 0x11) + is_on_break = true; + } + + if (dwarfs[dwarf]->profession == df::enums::profession::BABY || + dwarfs[dwarf]->profession == df::enums::profession::CHILD || + dwarfs[dwarf]->profession == df::enums::profession::DRUNK) + { + dwarf_info[dwarf].state = CHILD; + } + else if (dwarfs[dwarf]->job.current_job == NULL) + { + if (is_on_break) + dwarf_info[dwarf].state = OTHER; + else if (dwarfs[dwarf]->meetings.size() > 0) + dwarf_info[dwarf].state = OTHER; + else + dwarf_info[dwarf].state = IDLE; + } + else + { + int job = dwarfs[dwarf]->job.current_job->job_type; + + assert(job >= 0); + assert(job < _countof(dwarf_states)); + + dwarf_info[dwarf].state = dwarf_states[job]; + } + + state_count[dwarf_info[dwarf].state]++; + } + + // Set about 1/3 of the dwarfs as haulers. The haulers have all HAULER labors enabled. Having a lot of haulers helps + // make sure that hauling jobs are handled quickly rather than building up. + + int num_haulers = state_count[IDLE] + state_count[BUSY] / 3; + if (num_haulers < 1) + num_haulers = 1; + + std::vector hauler_ids; + for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) + { + if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY) + hauler_ids.push_back(dwarf); + } + + // Idle dwarves come first, then we sort from least-skilled to most-skilled. + + std::sort(hauler_ids.begin(), hauler_ids.end(), [&dwarf_info] (int i, int j) -> bool + { + if (dwarf_info[i].state == IDLE && dwarf_info[j].state != IDLE) + return true; + if (dwarf_info[i].state != IDLE && dwarf_info[j].state == IDLE) + return false; + return dwarf_info[i].mastery_penalty > dwarf_info[j].mastery_penalty; + }); + + FOR_ENUM_ITEMS(unit_labor, labor) + { + if (labor == df::enums::unit_labor::NONE) + continue; + + assert(labor >= 0); + assert(labor < _countof(labor_info)); + + if (labor_info[labor].mode != HAULERS) + continue; + + for (int i = 0; i < num_haulers; i++) + { + assert(i < hauler_ids.size()); + + int dwarf = hauler_ids[i]; + + assert(dwarf >= 0); + assert(dwarf < n_dwarfs); + + dwarfs[dwarf]->status.labors[labor] = true; + dwarf_info[dwarf].assigned_jobs++; + } + + for (int i = num_haulers; i < hauler_ids.size(); i++) + { + assert(i < hauler_ids.size()); + + int dwarf = hauler_ids[i]; + + assert(dwarf >= 0); + assert(dwarf < n_dwarfs); + + dwarfs[dwarf]->status.labors[labor] = false; + } + } + + // Generate labor -> skill mapping + + df::job_skill labor_to_skill[ENUM_LAST_ITEM(unit_labor) + 1]; + for (int i = 0; i <= ENUM_LAST_ITEM(unit_labor); i++) + labor_to_skill[i] = df::enums::job_skill::NONE; + + FOR_ENUM_ITEMS(job_skill, skill) + { + int labor = ENUM_ATTR(job_skill, labor, skill); + if (labor != df::enums::unit_labor::NONE) + { + assert(labor >= 0); + assert(labor < _countof(labor_to_skill)); + + labor_to_skill[labor] = skill; + } + } + + // Handle all skills except those marked HAULERS + + FOR_ENUM_ITEMS(unit_labor, labor) + { + if (labor == df::enums::unit_labor::NONE) + continue; + + assert(labor >= 0); + assert(labor < _countof(labor_info)); + + df::job_skill skill = labor_to_skill[labor]; + + if (labor_info[labor].mode == HAULERS) + continue; + + int best_dwarf = 0; + int best_value = -10000; + + std::vector values(n_dwarfs); + std::vector candidates; + + for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) + { + if (labor_info[labor].mode == AUTOMATIC && state_count[IDLE] > 0 && dwarf_info[dwarf].state != IDLE) + continue; + + if (dwarf_info[dwarf].state != IDLE && dwarf_info[dwarf].state != BUSY) + continue; + + if (labor_info[labor].is_exclusive && dwarf_info[dwarf].has_exclusive_labor) + continue; + + candidates.push_back(dwarf); + + int value = dwarf_info[dwarf].mastery_penalty - dwarf_info[dwarf].assigned_jobs; + + if (skill != df::enums::job_skill::NONE) + { + int skill_level = 0; + int skill_experience = 0; + + for (auto s = dwarfs[dwarf]->status.souls[0]->skills.begin(); s < dwarfs[dwarf]->status.souls[0]->skills.end(); s++) + { + if ((*s)->id == skill) + { + skill_level = (*s)->rating; + skill_experience = (*s)->experience; + break; + } + } + + value += skill_level * 100; + value += skill_experience / 20; + if (skill_level > 0 || skill_experience > 0) + value += 200; + if (skill_level >= 15) + value += 1000 * (skill_level - 14); + } + + if (dwarfs[dwarf]->status.labors[labor]) + { + value += 5; + if (labor_info[labor].is_exclusive) + value += 350; + } + + values[dwarf] = value; + } + + if (labor_info[labor].mode != EVERYONE) + std::sort(candidates.begin(), candidates.end(), [&values] (int i, int j) { return values[i] > values[j]; }); + + for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) + { + if (state_count[IDLE] > 0 && dwarf_info[dwarf].state == BUSY && labor_info[labor].mode == AUTOMATIC) + { + if (dwarfs[dwarf]->status.labors[labor]) + dwarf_info[dwarf].assigned_jobs++; + } + else + { + dwarfs[dwarf]->status.labors[labor] = false; + } + } + + int minimum_dwarfs = labor_info[labor].minimum_dwarfs; + + if (labor_info[labor].mode == EVERYONE) + minimum_dwarfs = n_dwarfs; + + for (int i = 0; i < candidates.size() && i < minimum_dwarfs; i++) + { + int dwarf = candidates[i]; + + assert(dwarf >= 0); + assert(dwarf < n_dwarfs); + + if (!dwarfs[dwarf]->status.labors[labor]) + dwarf_info[dwarf].assigned_jobs++; + + dwarfs[dwarf]->status.labors[labor] = true; + + if (labor_info[labor].is_exclusive) + dwarf_info[dwarf].has_exclusive_labor = true; + } + } + + return CR_OK; +} + +// A command! It sits around and looks pretty. And it's nice and friendly. +command_result autolabor (color_ostream &out, std::vector & parameters) +{ + if (parameters.size() == 1 && (parameters[0] == "0" || parameters[0] == "1")) + { + if (parameters[0] == "0") + enable_autolabor = 0; + else + enable_autolabor = 1; + out.print("autolabor %sactivated.\n", (enable_autolabor ? "" : "de")); + } + else + { + out.print("Automatically assigns labors to dwarves.\n" + "Activate with 'autolabor 1', deactivate with 'autolabor 0'.\n" + "Current state: %d.\n", enable_autolabor); + } + + return CR_OK; +} diff --git a/plugins/autolabor/autolabor.h b/plugins/autolabor/autolabor.h new file mode 100644 index 000000000..7b9637ef9 --- /dev/null +++ b/plugins/autolabor/autolabor.h @@ -0,0 +1 @@ +#pragma once \ No newline at end of file From 14ae83ced5440480ca0d44886d9ff82d17943a0d Mon Sep 17 00:00:00 2001 From: RossM Date: Sun, 18 Mar 2012 16:54:47 -0700 Subject: [PATCH 02/25] Correctly handle military dwarves. --- plugins/autolabor/autolabor.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/autolabor/autolabor.cpp b/plugins/autolabor/autolabor.cpp index 209d0c8f9..478c53ee0 100644 --- a/plugins/autolabor/autolabor.cpp +++ b/plugins/autolabor/autolabor.cpp @@ -580,7 +580,9 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) } else if (dwarfs[dwarf]->job.current_job == NULL) { - if (is_on_break) + if (ENUM_ATTR(profession, military, dwarfs[dwarf]->profession)) + dwarf_info[dwarf].state = MILITARY; + else if (is_on_break) dwarf_info[dwarf].state = OTHER; else if (dwarfs[dwarf]->meetings.size() > 0) dwarf_info[dwarf].state = OTHER; From bfb226b92ea9021c199d81e189c35d01c6f1f1a0 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 19 Mar 2012 11:33:30 +0400 Subject: [PATCH 03/25] Add a few comments. --- library/proto/BasicApi.proto | 8 ++++++++ library/proto/CoreProtocol.proto | 6 +++++- plugins/proto/rename.proto | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/library/proto/BasicApi.proto b/library/proto/BasicApi.proto index 7b105ee88..137f25726 100644 --- a/library/proto/BasicApi.proto +++ b/library/proto/BasicApi.proto @@ -4,6 +4,10 @@ option optimize_for = LITE_RUNTIME; import "Basic.proto"; +// RPC GetVersion : EmptyMessage -> StringMessage +// RPC GetDFVersion : EmptyMessage -> StringMessage + +// RPC GetWorldInfo : EmptyMessage -> GetWorldInfoOut message GetWorldInfoOut { enum Mode { MODE_DWARF = 1; @@ -27,6 +31,7 @@ message GetWorldInfoOut { repeated int32 companion_histfig_ids = 10; }; +// RPC ListEnums : EmptyMessage -> ListEnumsOut message ListEnumsOut { repeated EnumItemName material_flags = 1; repeated EnumItemName inorganic_flags = 2; @@ -46,6 +51,7 @@ message ListEnumsOut { repeated EnumItemName profession = 11; }; +// RPC ListMaterials : ListMaterialsIn -> ListMaterialsOut message ListMaterialsIn { optional BasicMaterialInfoMask mask = 1; @@ -62,6 +68,7 @@ message ListMaterialsOut { repeated BasicMaterialInfo value = 1; }; +// RPC ListUnits : ListUnitsIn -> ListUnitsOut message ListUnitsIn { optional BasicUnitInfoMask mask = 1; @@ -81,6 +88,7 @@ message ListUnitsOut { repeated BasicUnitInfo value = 1; }; +// RPC ListSquads : ListSquadsIn -> ListSquadsOut message ListSquadsIn {} message ListSquadsOut { repeated BasicSquadInfo value = 1; diff --git a/library/proto/CoreProtocol.proto b/library/proto/CoreProtocol.proto index 6f3b84510..92d7c48d9 100644 --- a/library/proto/CoreProtocol.proto +++ b/library/proto/CoreProtocol.proto @@ -62,18 +62,22 @@ message StringListMessage { repeated string value = 1; } +// RPC BindMethod : CoreBindRequest -> CoreBindReply message CoreBindRequest { required string method = 1; required string input_msg = 2; required string output_msg = 3; optional string plugin = 4; } - message CoreBindReply { required int32 assigned_id = 1; } +// RPC RunCommand : CoreRunCommandRequest -> EmptyMessage message CoreRunCommandRequest { required string command = 1; repeated string arguments = 2; } + +// RPC CoreSuspend : EmptyMessage -> IntMessage +// RPC CoreResume : EmptyMessage -> IntMessage diff --git a/plugins/proto/rename.proto b/plugins/proto/rename.proto index a6c3b055a..aa1e95f48 100644 --- a/plugins/proto/rename.proto +++ b/plugins/proto/rename.proto @@ -2,6 +2,7 @@ package dfproto; option optimize_for = LITE_RUNTIME; +// RPC RenameSquad : RenameSquadIn -> EmptyMessage message RenameSquadIn { required int32 squad_id = 1; @@ -9,6 +10,7 @@ message RenameSquadIn { optional string alias = 3; } +// RPC RenameUnit : RenameUnitIn -> EmptyMessage message RenameUnitIn { required int32 unit_id = 1; From 296d3a0af306a5247048ed4f38b2bc72eb582d89 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 19 Mar 2012 16:59:11 +0400 Subject: [PATCH 04/25] Skeleton type metadata for future use in lua bindings. --- library/CMakeLists.txt | 9 ++ library/DataDefs.cpp | 145 +++++++++++++------- library/DataStatics.cpp | 2 + library/DataStaticsFields.cpp | 23 ++++ library/include/DataDefs.h | 235 +++++++++++++++++++++++++++------ library/include/DataIdentity.h | 175 ++++++++++++++++++++++++ library/xml | 2 +- 7 files changed, 502 insertions(+), 89 deletions(-) create mode 100644 library/DataStaticsFields.cpp create mode 100644 library/include/DataIdentity.h diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 63107456a..7d3c4d6bb 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -57,6 +57,7 @@ ColorText.cpp DataDefs.cpp DataStatics.cpp DataStaticsCtor.cpp +DataStaticsFields.cpp MiscUtils.cpp PluginManager.cpp TileTypes.cpp @@ -195,6 +196,14 @@ ADD_CUSTOM_COMMAND( ADD_CUSTOM_TARGET(generate_headers DEPENDS ${dfapi_SOURCE_DIR}/include/df/static.inc) +IF(UNIX) + # Don't produce debug info for generated stubs + SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp + PROPERTIES COMPILE_FLAGS "-g0") +ELSE(WIN32) +ENDIF() + + # Compilation ADD_DEFINITIONS(-DBUILD_DFHACK_LIB) diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index 30a4da271..c7b0d29dd 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.cpp @@ -44,18 +44,107 @@ using namespace DFHack; * this list has to be plain data, so that it gets * initialized by the loader in the initial mmap. */ -virtual_identity *virtual_identity::list = NULL; +compound_identity *compound_identity::list = NULL; +std::vector compound_identity::top_scope; -virtual_identity::virtual_identity(const char *dfhack_name, const char *original_name, virtual_identity *parent) - : dfhack_name(dfhack_name), original_name(original_name), parent(parent), - prev(NULL), vtable_ptr(NULL), has_children(true) +compound_identity::compound_identity(size_t size, TAllocateFn alloc, + compound_identity *scope_parent, const char *dfhack_name) + : constructed_identity(size, alloc), scope_parent(scope_parent), dfhack_name(dfhack_name) { - // Link into the static list. Nothing else can be safely done at this point. - next = list; list = this; + next = list; list = this; +} + +void compound_identity::doInit(Core *) +{ + if (scope_parent) + scope_parent->scope_children.push_back(this); + else + top_scope.push_back(this); } -/* Vtable to identity lookup. */ static tthread::mutex *known_mutex = NULL; + +void compound_identity::Init(Core *core) +{ + if (!known_mutex) + known_mutex = new tthread::mutex(); + + // This cannot be done in the constructors, because + // they are called in an undefined order. + for (compound_identity *p = list; p; p = p->next) + p->doInit(core); + + //FIXME: ... nuked. the group was empty... +/* + // Read pre-filled vtable ptrs + OffsetGroup *ptr_table = core->vinfo->getGroup("vtable"); + for (virtual_identity *p = list; p; p = p->next) { + void * tmp; + if (ptr_table->getSafeAddress(p->getName(),tmp)) + p->vtable_ptr = tmp; + } + */ +} + +bitfield_identity::bitfield_identity(size_t size, TAllocateFn alloc, + compound_identity *scope_parent, const char *dfhack_name, + int num_bits, const bitfield_item_info *bits) + : compound_identity(size, alloc, scope_parent, dfhack_name), bits(bits), num_bits(num_bits) +{ +} + +enum_identity::enum_identity(size_t size, TAllocateFn alloc, + compound_identity *scope_parent, const char *dfhack_name, + int64_t first_item_value, int64_t last_item_value, + const char *const *keys) + : compound_identity(size, alloc, scope_parent, dfhack_name), + first_item_value(first_item_value), last_item_value(last_item_value), keys(keys) +{ +} + +struct_identity::struct_identity(size_t size, TAllocateFn alloc, + compound_identity *scope_parent, const char *dfhack_name, + struct_identity *parent, const struct_field_info *fields) + : compound_identity(size, alloc, scope_parent, dfhack_name), parent(parent), has_children(false) +{ +} + +void struct_identity::doInit(Core *core) +{ + compound_identity::doInit(core); + + if (parent) { + parent->children.push_back(this); + parent->has_children = true; + } +} + +bool struct_identity::is_subclass(struct_identity *actual) +{ + for (; actual; actual = actual->getParent()) + if (actual == this) return true; + + return false; +} + +virtual_identity::virtual_identity(size_t size, TAllocateFn alloc, + const char *dfhack_name, const char *original_name, + virtual_identity *parent, const struct_field_info *fields) + : struct_identity(size, alloc, NULL, dfhack_name, parent, fields), original_name(original_name), + vtable_ptr(NULL) +{ +} + +static std::map name_lookup; + +void virtual_identity::doInit(Core *core) +{ + struct_identity::doInit(core); + + name_lookup[getOriginalName()] = this; +} + +/* Vtable to identity lookup. */ std::map virtual_identity::known; virtual_identity *virtual_identity::get(virtual_ptr instance_ptr) @@ -78,8 +167,9 @@ virtual_identity *virtual_identity::get(virtual_ptr instance_ptr) virtual_identity *actual = NULL; - for (virtual_identity *p = list; p; p = p->next) { - if (strcmp(name.c_str(), p->getOriginalName()) != 0) continue; + auto name_it = name_lookup.find(name); + if (name_it != name_lookup.end()) { + virtual_identity *p = name_it->second; if (p->vtable_ptr && p->vtable_ptr != vtable) { std::cerr << "Conflicting vtable ptr for class '" << p->getName() @@ -103,14 +193,6 @@ virtual_identity *virtual_identity::get(virtual_ptr instance_ptr) return NULL; } -bool virtual_identity::is_subclass(virtual_identity *actual) -{ - for (; actual; actual = actual->parent) - if (actual == this) return true; - - return false; -} - void virtual_identity::adjust_vtable(virtual_ptr obj, virtual_identity *main) { if (vtable_ptr) { @@ -135,35 +217,6 @@ virtual_ptr virtual_identity::clone(virtual_ptr obj) return copy; } -void virtual_identity::Init(Core *core) -{ - if (!known_mutex) - known_mutex = new tthread::mutex(); - - // This cannot be done in the constructors, because - // they are called in an undefined order. - for (virtual_identity *p = list; p; p = p->next) { - p->has_children = false; - p->children.clear(); - } - for (virtual_identity *p = list; p; p = p->next) { - if (p->parent) { - p->parent->children.push_back(p); - p->parent->has_children = true; - } - } - //FIXME: ... nuked. the group was empty... -/* - // Read pre-filled vtable ptrs - OffsetGroup *ptr_table = core->vinfo->getGroup("vtable"); - for (virtual_identity *p = list; p; p = p->next) { - void * tmp; - if (ptr_table->getSafeAddress(p->getName(),tmp)) - p->vtable_ptr = tmp; - } - */ -} - bool DFHack::findBitfieldField(unsigned *idx, const std::string &name, unsigned size, const bitfield_item_info *items) { diff --git a/library/DataStatics.cpp b/library/DataStatics.cpp index 5ccabb7e1..4a77b3b1d 100644 --- a/library/DataStatics.cpp +++ b/library/DataStatics.cpp @@ -21,6 +21,8 @@ namespace { #define INIT_GLOBAL_FUNCTION_ITEM(type,name) \ if (global_table_->getAddress(#name,tmp_)) name = (type*)tmp_; +#define TID(type) (&identity_traits< type >::identity) + // Instantiate all the static objects #include "df/static.inc" #include "df/static.enums.inc" diff --git a/library/DataStaticsFields.cpp b/library/DataStaticsFields.cpp new file mode 100644 index 000000000..0e6e9957e --- /dev/null +++ b/library/DataStaticsFields.cpp @@ -0,0 +1,23 @@ +#include "Internal.h" +#include "DataDefs.h" +#include "MiscUtils.h" +#include "VersionInfo.h" + +#include "df/world.h" +#include "df/world_data.h" +#include "df/ui.h" + +#include "DataIdentity.h" + +#include + +#pragma GCC diagnostic ignored "-Winvalid-offsetof" + +#define TID(type) (&identity_traits< type >::identity) + +#define FLD(mode, name) struct_field_info::mode, #name, offsetof(CUR_STRUCT, name) +#define GFLD(mode, name) struct_field_info::mode, #name, 0 +#define FLD_END struct_field_info::END + +// Field definitions +#include "df/static.fields.inc" diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 99694d949..2a9567182 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -45,47 +45,205 @@ namespace DFHack { class virtual_class {}; + enum identity_type { + IDTYPE_GLOBAL, + IDTYPE_PRIMITIVE, + IDTYPE_CONTAINER, + IDTYPE_BITFIELD, + IDTYPE_ENUM, + IDTYPE_STRUCT, + IDTYPE_CLASS + }; + + typedef void *(*TAllocateFn)(void*,const void*); + + class DFHACK_EXPORT type_identity { + size_t size; + + protected: + type_identity(size_t size) : size(size) {}; + + virtual void *do_instantiate() { + void *p = malloc(size); + memset(p, 0, size); + return p; + } + virtual void do_copy(void *tgt, const void *src) { + memmove(tgt, src, size); + }; + + public: + virtual ~type_identity() {} + + size_t byte_size() { return size; } + + virtual identity_type type() = 0; + }; + + class DFHACK_EXPORT constructed_identity : public type_identity { + TAllocateFn allocator; + + protected: + constructed_identity(size_t size, TAllocateFn alloc) + : type_identity(size), allocator(alloc) {}; + + virtual void *do_instantiate() { + return allocator ? allocator(NULL,NULL) : type_identity::do_instantiate(); + } + virtual void do_copy(void *tgt, const void *src) { + if (allocator) allocator(tgt,src); + else type_identity::do_copy(tgt, src); + }; + }; + + class DFHACK_EXPORT compound_identity : public constructed_identity { + static compound_identity *list; + compound_identity *next; + + const char *dfhack_name; + compound_identity *scope_parent; + std::vector scope_children; + static std::vector top_scope; + + protected: + compound_identity(size_t size, TAllocateFn alloc, + compound_identity *scope_parent, const char *dfhack_name); + + virtual void doInit(Core *core); + + public: + const char *getName() { return dfhack_name; } + + compound_identity *getScopeParent() { return scope_parent; } + const std::vector &getScopeChildren() { return scope_children; } + static const std::vector &getTopScope() { return top_scope; } + + static void Init(Core *core); + }; + + // Bitfields + struct bitfield_item_info { + const char *name; + int size; + }; + + class DFHACK_EXPORT bitfield_identity : public compound_identity { + const bitfield_item_info *bits; + int num_bits; + + public: + bitfield_identity(size_t size, TAllocateFn alloc, + compound_identity *scope_parent, const char *dfhack_name, + int num_bits, const bitfield_item_info *bits); + + virtual identity_type type() { return IDTYPE_BITFIELD; } + + int getNumBits() { return num_bits; } + const bitfield_item_info *getBits() { return bits; } + }; + + class DFHACK_EXPORT enum_identity : public compound_identity { + const char *const *keys; + int64_t first_item_value; + int64_t last_item_value; + + public: + enum_identity(size_t size, TAllocateFn alloc, + compound_identity *scope_parent, const char *dfhack_name, + int64_t first_item_value, int64_t last_item_value, + const char *const *keys); + + virtual identity_type type() { return IDTYPE_ENUM; } + + int getCount() { return int(last_item_value-first_item_value+1); } + const char *const *getKeys() { return keys; } + }; + + struct struct_field_info { + enum Mode { + END, + PRIMITIVE, + STATIC_STRING, + POINTER, + STATIC_ARRAY, + SUBSTRUCT, + CONTAINER, + STL_VECTOR_PTR + }; + Mode mode; + const char *name; + size_t offset; + type_identity *type; + size_t count; + enum_identity *eid; + }; + + class DFHACK_EXPORT struct_identity : public compound_identity { + struct_identity *parent; + std::vector children; + bool has_children; + + protected: + virtual void doInit(Core *core); + + public: + struct_identity(size_t size, TAllocateFn alloc, + compound_identity *scope_parent, const char *dfhack_name, + struct_identity *parent, const struct_field_info *fields); + + virtual identity_type type() { return IDTYPE_STRUCT; } + + struct_identity *getParent() { return parent; } + const std::vector &getChildren() { return children; } + bool hasChildren() { return has_children; } + + bool is_subclass(struct_identity *subtype); + }; + + class DFHACK_EXPORT global_identity : public struct_identity { + public: + global_identity(const struct_field_info *fields) + : struct_identity(0,NULL,NULL,"global",NULL,fields) {} + + virtual identity_type type() { return IDTYPE_GLOBAL; } + }; + #ifdef _MSC_VER typedef void *virtual_ptr; #else typedef virtual_class *virtual_ptr; #endif - class DFHACK_EXPORT virtual_identity { - static virtual_identity *list; + class DFHACK_EXPORT virtual_identity : public struct_identity { static std::map known; - - virtual_identity *prev, *next; - const char *dfhack_name; + const char *original_name; - virtual_identity *parent; - std::vector children; - + void *vtable_ptr; - bool has_children; protected: - virtual_identity(const char *dfhack_name, const char *original_name, virtual_identity *parent); + virtual void doInit(Core *core); static void *get_vtable(virtual_ptr instance_ptr) { return *(void**)instance_ptr; } public: - const char *getName() { return dfhack_name; } - const char *getOriginalName() { return original_name ? original_name : dfhack_name; } + virtual_identity(size_t size, TAllocateFn alloc, + const char *dfhack_name, const char *original_name, + virtual_identity *parent, const struct_field_info *fields); + + virtual identity_type type() { return IDTYPE_CLASS; } - virtual_identity *getParent() { return parent; } - const std::vector &getChildren() { return children; } + const char *getOriginalName() { return original_name ? original_name : getName(); } public: static virtual_identity *get(virtual_ptr instance_ptr); - - bool is_subclass(virtual_identity *subtype); + bool is_instance(virtual_ptr instance_ptr) { if (!instance_ptr) return false; if (vtable_ptr) { void *vtable = get_vtable(instance_ptr); if (vtable == vtable_ptr) return true; - if (!has_children) return false; + if (!hasChildren()) return false; } return is_subclass(get(instance_ptr)); } @@ -98,15 +256,10 @@ namespace DFHack public: bool can_instantiate() { return (vtable_ptr != NULL); } - virtual_ptr instantiate() { return can_instantiate() ? do_instantiate() : NULL; } + virtual_ptr instantiate() { return can_instantiate() ? (virtual_ptr)do_instantiate() : NULL; } static virtual_ptr clone(virtual_ptr obj); - protected: - virtual virtual_ptr do_instantiate() = 0; - virtual void do_copy(virtual_ptr tgt, virtual_ptr src) = 0; public: - static void Init(Core *core); - // Strictly for use in virtual class constructors void adjust_vtable(virtual_ptr obj, virtual_identity *main); }; @@ -135,12 +288,6 @@ namespace DFHack size_t size; const T *items; }; - - // Bitfields - struct bitfield_item_info { - const char *name; - int size; - }; } template @@ -164,33 +311,37 @@ inline int linear_index(const DFHack::enum_list_attr &lst, const st namespace df { + using DFHack::type_identity; + using DFHack::compound_identity; using DFHack::virtual_ptr; using DFHack::virtual_identity; using DFHack::virtual_class; + using DFHack::global_identity; + using DFHack::struct_identity; + using DFHack::struct_field_info; using DFHack::bitfield_item_info; + using DFHack::bitfield_identity; + using DFHack::enum_identity; using DFHack::enum_list_attr; using DFHack::BitArray; using DFHack::DfArray; template - struct enum_traits {}; + void *allocator_fn(void *out, const void *in) { + if (out) { *(T*)out = *(const T*)in; return out; } + else return new T(); + } template - struct bitfield_traits {}; + struct identity_traits { + static compound_identity *get() { return &T::_identity; } + }; template - class class_virtual_identity : public virtual_identity { - public: - class_virtual_identity(const char *dfhack_name, const char *original_name, virtual_identity *parent) - : virtual_identity(dfhack_name, original_name, parent) {}; - - T *instantiate() { return static_cast(virtual_identity::instantiate()); } - T *clone(T* obj) { return static_cast(virtual_identity::clone(obj)); } + struct enum_traits {}; - protected: - virtual virtual_ptr do_instantiate() { return new T(); } - virtual void do_copy(virtual_ptr tgt, virtual_ptr src) { *static_cast(tgt) = *static_cast(src); } - }; + template + struct bitfield_traits {}; template struct enum_field { diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h new file mode 100644 index 000000000..679023fcf --- /dev/null +++ b/library/include/DataIdentity.h @@ -0,0 +1,175 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2011 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 + +#include +#include +#include +#include + +#include "DataDefs.h" + +/* + * Definitions of DFHack namespace structs used by generated headers. + */ + +namespace DFHack +{ + class DFHACK_EXPORT primitive_identity : public type_identity { + public: + primitive_identity(size_t size) : type_identity(size) {}; + + virtual identity_type type() { return IDTYPE_PRIMITIVE; } + }; + + class DFHACK_EXPORT container_identity : public constructed_identity { + type_identity *item; + enum_identity *ienum; + + public: + container_identity(size_t size, TAllocateFn alloc, type_identity *item, enum_identity *ienum = NULL) + : constructed_identity(size, alloc), item(item), ienum(ienum) {}; + + virtual identity_type type() { return IDTYPE_CONTAINER; } + }; +} + +namespace df +{ + using DFHack::primitive_identity; + using DFHack::container_identity; + +#define ATOM_IDENTITY_TRAITS(type) \ + template<> struct identity_traits { \ + static primitive_identity identity; \ + static primitive_identity *get() { return &identity; } \ + }; \ + primitive_identity identity_traits::identity(sizeof(type)); + + ATOM_IDENTITY_TRAITS(char); + ATOM_IDENTITY_TRAITS(int8_t); + ATOM_IDENTITY_TRAITS(uint8_t); + ATOM_IDENTITY_TRAITS(int16_t); + ATOM_IDENTITY_TRAITS(uint16_t); + ATOM_IDENTITY_TRAITS(int32_t); + ATOM_IDENTITY_TRAITS(uint32_t); + ATOM_IDENTITY_TRAITS(int64_t); + ATOM_IDENTITY_TRAITS(uint64_t); + ATOM_IDENTITY_TRAITS(bool); + ATOM_IDENTITY_TRAITS(float); + ATOM_IDENTITY_TRAITS(std::string); + ATOM_IDENTITY_TRAITS(void*); + +#undef ATOM_IDENTITY_TRAITS + + // Container declarations + + template struct identity_traits > { + static primitive_identity *get(); + }; + + template struct identity_traits { + static container_identity *get(); + }; + + template struct identity_traits { + static container_identity *get(); + }; + + template struct identity_traits > { + static container_identity *get(); + }; + + template struct identity_traits > { + static container_identity *get(); + }; + + template struct identity_traits > { + static container_identity *get(); + }; + + template struct identity_traits > { + static container_identity *get(); + }; + + // Container definitions + + template + primitive_identity *identity_traits >::get() { + static primitive_identity identity(sizeof(FT)); + return &identity; + } + + template + container_identity *identity_traits::get() { + typedef T * container; + static container_identity identity(sizeof(container), &allocator_fn, + identity_traits::get()); + return &identity; + } + + template + container_identity *identity_traits::get() { + typedef T container[sz]; + static container_identity identity(sizeof(container), NULL, + identity_traits::get()); + return &identity; + } + + template + container_identity *identity_traits >::get() { + typedef std::vector container; + static container_identity identity(sizeof(container), &allocator_fn, + identity_traits::get()); + return &identity; + } + + template + container_identity *identity_traits >::get() { + typedef std::deque container; + static container_identity identity(sizeof(container), &allocator_fn, + identity_traits::get()); + return &identity; + } + + template + container_identity *identity_traits >::get() { + typedef BitArray container; + static type_identity *eid = identity_traits::get(); + static enum_identity *reid = eid->type() == DFHack::IDTYPE_ENUM ? (enum_identity*)eid : NULL; + static container_identity identity(sizeof(container), &allocator_fn, + &identity_traits::identity, reid); + return &identity; + } + + template + container_identity *identity_traits >::get() { + typedef DfArray container; + static container_identity identity(sizeof(container), &allocator_fn, + identity_traits::get()); + return &identity; + } +} + diff --git a/library/xml b/library/xml index 08e1f71e8..70eb6b5f3 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 08e1f71e89c1af6b3bef940914ed7f3d8fed89b0 +Subproject commit 70eb6b5f35680655d04d9fda79ff7251e21b45ae From dbbd9acfad8d5994f321840e3d695fe8a67ac315 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Mon, 19 Mar 2012 20:12:27 +0400 Subject: [PATCH 05/25] Export the type tree with enum keys to lua. --- CMakeLists.txt | 1 + depends/lua/CMakeLists.txt | 7 +- depends/lua/include/luaconf.h | 15 ++- library/CMakeLists.txt | 3 +- library/DataStaticsFields.cpp | 21 ++++ library/LuaWrapper.cpp | 218 +++++++++++++++++++++++++++++++++ library/include/DataDefs.h | 6 + library/include/DataIdentity.h | 3 +- 8 files changed, 267 insertions(+), 7 deletions(-) create mode 100644 library/LuaWrapper.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d39d940e2..1c538e246 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,6 +103,7 @@ ENDIF() # use shared libraries for protobuf ADD_DEFINITIONS(-DPROTOBUF_USE_DLLS) +ADD_DEFINITIONS(-DLUA_BUILD_AS_DLL) if(UNIX) add_definitions(-D_LINUX) diff --git a/depends/lua/CMakeLists.txt b/depends/lua/CMakeLists.txt index b135f221d..6a97bd434 100644 --- a/depends/lua/CMakeLists.txt +++ b/depends/lua/CMakeLists.txt @@ -76,8 +76,13 @@ src/lzio.c ) LIST(APPEND SRC_LIBLUA ${HDR_LIBLUA}) -ADD_LIBRARY ( lua STATIC EXCLUDE_FROM_ALL ${SRC_LIBLUA} ) +ADD_LIBRARY ( lua SHARED EXCLUDE_FROM_ALL ${SRC_LIBLUA} ) TARGET_LINK_LIBRARIES ( lua ${LIBS}) + +install(TARGETS lua + LIBRARY DESTINATION ${DFHACK_LIBRARY_DESTINATION} + RUNTIME DESTINATION ${DFHACK_LIBRARY_DESTINATION}) + IDE_FOLDER(lua "Depends") #SET ( SRC_LUA src/lua.c ) diff --git a/depends/lua/include/luaconf.h b/depends/lua/include/luaconf.h index 660793356..b202967b3 100644 --- a/depends/lua/include/luaconf.h +++ b/depends/lua/include/luaconf.h @@ -151,11 +151,20 @@ ** the libraries, you may want to use the following definition (define ** LUA_BUILD_AS_DLL to get it). */ +#ifdef __cplusplus + #define LUA_API_EXTERN extern "C" +#else + #define LUA_API_EXTERN extern +#endif #if defined(LUA_BUILD_AS_DLL) - #if defined(LUA_CORE) || defined(LUA_LIB) - #define LUA_API __declspec(dllexport) + #if defined(_MSC_VER) + #if defined(LUA_CORE) || defined(LUA_LIB) + #define LUA_API __declspec(dllexport) LUA_API_EXTERN + #else + #define LUA_API __declspec(dllimport) LUA_API_EXTERN + #endif #else - #define LUA_API __declspec(dllimport) + #define LUA_API LUA_API_EXTERN __attribute__ ((visibility("default"))) #endif #else #ifdef __cplusplus diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 7d3c4d6bb..3b1c6e7bf 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -55,6 +55,7 @@ SET(MAIN_SOURCES Core.cpp ColorText.cpp DataDefs.cpp +LuaWrapper.cpp DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp @@ -251,7 +252,7 @@ ENDIF() #effectively disables debug builds... SET_TARGET_PROPERTIES(dfhack PROPERTIES DEBUG_POSTFIX "-debug" ) -TARGET_LINK_LIBRARIES(dfhack protobuf-lite clsocket ${PROJECT_LIBS}) +TARGET_LINK_LIBRARIES(dfhack protobuf-lite clsocket lua ${PROJECT_LIBS}) SET_TARGET_PROPERTIES(dfhack PROPERTIES LINK_INTERFACE_LIBRARIES "") TARGET_LINK_LIBRARIES(dfhack-client protobuf-lite clsocket) diff --git a/library/DataStaticsFields.cpp b/library/DataStaticsFields.cpp index 0e6e9957e..a9d2f3121 100644 --- a/library/DataStaticsFields.cpp +++ b/library/DataStaticsFields.cpp @@ -13,6 +13,27 @@ #pragma GCC diagnostic ignored "-Winvalid-offsetof" +namespace df { +#define ATOM_IDENTITY_TRAITS(type) \ + primitive_identity identity_traits::identity(sizeof(type)); + + ATOM_IDENTITY_TRAITS(char); + ATOM_IDENTITY_TRAITS(int8_t); + ATOM_IDENTITY_TRAITS(uint8_t); + ATOM_IDENTITY_TRAITS(int16_t); + ATOM_IDENTITY_TRAITS(uint16_t); + ATOM_IDENTITY_TRAITS(int32_t); + ATOM_IDENTITY_TRAITS(uint32_t); + ATOM_IDENTITY_TRAITS(int64_t); + ATOM_IDENTITY_TRAITS(uint64_t); + ATOM_IDENTITY_TRAITS(bool); + ATOM_IDENTITY_TRAITS(float); + ATOM_IDENTITY_TRAITS(std::string); + ATOM_IDENTITY_TRAITS(void*); + +#undef ATOM_IDENTITY_TRAITS +} + #define TID(type) (&identity_traits< type >::identity) #define FLD(mode, name) struct_field_info::mode, #name, offsetof(CUR_STRUCT, name) diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp new file mode 100644 index 000000000..3ed4a35cd --- /dev/null +++ b/library/LuaWrapper.cpp @@ -0,0 +1,218 @@ +/* +https://github.com/peterix/dfhack +Copyright (c) 2009-2011 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 + +#include "MemAccess.h" +#include "Core.h" +#include "VersionInfo.h" +#include "tinythread.h" +// must be last due to MS stupidity +#include "DataDefs.h" +#include "DataIdentity.h" + +#include "MiscUtils.h" + +#include +#include + +using namespace DFHack; + +static luaL_Reg no_functions[] = { { NULL, NULL } }; + +inline void lua_dup(lua_State *state) { lua_pushvalue(state, -1); } +inline void lua_swap(lua_State *state) { lua_insert(state, -2); } + +static int change_error(lua_State *state) +{ + luaL_error(state, "Attempt to change a read-only table.\n"); +} + +static void freeze_table(lua_State *state, bool leave_metatable = false, const char *name = NULL) +{ + // rv = {}; setmetatable(rv, { __index = in, __newindex = change_error, __metatable = name }) + int base = lua_gettop(state); + lua_newtable(state); + lua_swap(state); + lua_setfield(state, base, "__index"); + lua_getfield(state, LUA_REGISTRYINDEX, "DFHack.ChangeError"); + lua_setfield(state, base, "__newindex"); + lua_newtable(state); + lua_swap(state); + lua_dup(state); + lua_setmetatable(state, base); + if (name) + { + lua_pushstring(state, name); + lua_setfield(state, -2, "__metatable"); + } + // result: [frozen table] [metatable] + if (!leave_metatable) + lua_pop(state, 1); +} + +static void SaveTypeInfo(lua_State *state, void *node) +{ + lua_getfield(state, LUA_REGISTRYINDEX, "DFHack.DFTypes"); + lua_pushlightuserdata(state, node); + lua_pushvalue(state, -3); + lua_settable(state, -3); + lua_pop(state, 1); +} + +static bool RegisterTypeInfo(lua_State *state, void *node) +{ + lua_getfield(state, LUA_REGISTRYINDEX, "DFHack.DFTypes"); + int base = lua_gettop(state); + + lua_pushlightuserdata(state, node); + lua_rawget(state, base); + + bool added = false; + + if (lua_isnil(state, -1)) + { + lua_pop(state, 1); + + lua_newtable(state); + lua_pushlightuserdata(state, node); + lua_pushvalue(state, -2); + lua_rawset(state, base); + + added = true; + } + + lua_remove(state, -2); + return added; +} + +static void RenderTypeChildren(lua_State *state, const std::vector &children); + +static void RenderType(lua_State *state, compound_identity *node) +{ + assert(node->getName()); + + lua_newtable(state); + if (!lua_checkstack(state, 20)) + return; + + int base = lua_gettop(state); + + lua_pushlightuserdata(state, node); + lua_setfield(state, base, "_identity"); + + switch (node->type()) + { + case IDTYPE_ENUM: + { + enum_identity *eid = (enum_identity*)node; + const char *const *keys = eid->getKeys(); + + // For enums, set mapping between keys and values + for (int64_t i = eid->getFirstItem(), j = 0; i <= eid->getLastItem(); i++, j++) + { + if (!keys[j]) + continue; + + lua_pushinteger(state, i); + lua_pushstring(state, keys[j]); + lua_dup(state); + lua_pushinteger(state, i); + + lua_settable(state, base); + lua_settable(state, base); + } + + if (eid->getFirstItem() <= eid->getLastItem()) + { + lua_pushstring(state, "_first_item"); + lua_pushinteger(state, eid->getFirstItem()); + lua_settable(state, base); + + lua_pushstring(state, "_last_item"); + lua_pushinteger(state, eid->getLastItem()); + lua_settable(state, base); + } + + SaveTypeInfo(state, node); + } + break; + + default: + break; + } + + RenderTypeChildren(state, node->getScopeChildren()); + + assert(base == lua_gettop(state)); + + freeze_table(state, false, node->getName()); +} + +static void RenderTypeChildren(lua_State *state, const std::vector &children) +{ + for (size_t i = 0; i < children.size(); i++) + { + RenderType(state, children[i]); + lua_pushstring(state, children[i]->getName()); + lua_swap(state); + lua_settable(state, -3); + } +} + +static void DoAttach(lua_State *state) +{ + int base = lua_gettop(state); + + lua_pushcfunction(state, change_error); + lua_setfield(state, LUA_REGISTRYINDEX, "DFHack.ChangeError"); + + luaL_register(state, "df", no_functions); + + { + // Assign df a metatable with read-only contents + lua_newtable(state); + + // Render the type structure + RenderTypeChildren(state, compound_identity::getTopScope()); + + freeze_table(state, true, "df"); + lua_remove(state, -2); + lua_setmetatable(state, -2); + } + + lua_pop(state, 1); +} + +void DFHack::AttachDFGlobals(lua_State *state) +{ + if (luaL_newmetatable(state, "DFHack.DFTypes")) + DoAttach(state); + + lua_pop(state, 1); +} diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 2a9567182..3a7679b65 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -37,6 +37,8 @@ distribution. #undef interface #endif +typedef struct lua_State lua_State; + /* * Definitions of DFHack namespace structs used by generated headers. */ @@ -155,6 +157,8 @@ namespace DFHack virtual identity_type type() { return IDTYPE_ENUM; } + int64_t getFirstItem() { return first_item_value; } + int64_t getLastItem() { return last_item_value; } int getCount() { return int(last_item_value-first_item_value+1); } const char *const *getKeys() { return keys; } }; @@ -280,6 +284,8 @@ namespace DFHack void InitDataDefGlobals(Core *core); + DFHACK_EXPORT void AttachDFGlobals(lua_State *state); + template T *ifnull(T *a, T *b) { return a ? a : b; } diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index 679023fcf..d354beb03 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -65,8 +65,7 @@ namespace df template<> struct identity_traits { \ static primitive_identity identity; \ static primitive_identity *get() { return &identity; } \ - }; \ - primitive_identity identity_traits::identity(sizeof(type)); + }; ATOM_IDENTITY_TRAITS(char); ATOM_IDENTITY_TRAITS(int8_t); From 6c661bcaa907952f3ffdc4ff546ec88cfcc36d72 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 20 Mar 2012 13:56:29 +0400 Subject: [PATCH 06/25] Add support for primitive type fields in lua wrapper. --- library/DataDefs.cpp | 7 +- library/DataStatics.cpp | 2 + library/DataStaticsFields.cpp | 40 ++-- library/LuaWrapper.cpp | 339 ++++++++++++++++++++++++++++++++- library/include/DataDefs.h | 25 ++- library/include/DataIdentity.h | 142 +++++++++++--- library/xml | 2 +- 7 files changed, 502 insertions(+), 55 deletions(-) diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index c7b0d29dd..e8ee14712 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.cpp @@ -95,17 +95,20 @@ bitfield_identity::bitfield_identity(size_t size, TAllocateFn alloc, enum_identity::enum_identity(size_t size, TAllocateFn alloc, compound_identity *scope_parent, const char *dfhack_name, + type_identity *base_type, int64_t first_item_value, int64_t last_item_value, const char *const *keys) : compound_identity(size, alloc, scope_parent, dfhack_name), - first_item_value(first_item_value), last_item_value(last_item_value), keys(keys) + first_item_value(first_item_value), last_item_value(last_item_value), + keys(keys), base_type(base_type) { } struct_identity::struct_identity(size_t size, TAllocateFn alloc, compound_identity *scope_parent, const char *dfhack_name, struct_identity *parent, const struct_field_info *fields) - : compound_identity(size, alloc, scope_parent, dfhack_name), parent(parent), has_children(false) + : compound_identity(size, alloc, scope_parent, dfhack_name), + parent(parent), has_children(false), fields(fields) { } diff --git a/library/DataStatics.cpp b/library/DataStatics.cpp index 4a77b3b1d..1e4b21be9 100644 --- a/library/DataStatics.cpp +++ b/library/DataStatics.cpp @@ -7,6 +7,8 @@ #include "df/world_data.h" #include "df/ui.h" +#include "DataIdentity.h" + namespace { template inline T &_toref(T &r) { return r; } diff --git a/library/DataStaticsFields.cpp b/library/DataStaticsFields.cpp index a9d2f3121..22ce9b6df 100644 --- a/library/DataStaticsFields.cpp +++ b/library/DataStaticsFields.cpp @@ -14,30 +14,32 @@ #pragma GCC diagnostic ignored "-Winvalid-offsetof" namespace df { -#define ATOM_IDENTITY_TRAITS(type) \ - primitive_identity identity_traits::identity(sizeof(type)); - - ATOM_IDENTITY_TRAITS(char); - ATOM_IDENTITY_TRAITS(int8_t); - ATOM_IDENTITY_TRAITS(uint8_t); - ATOM_IDENTITY_TRAITS(int16_t); - ATOM_IDENTITY_TRAITS(uint16_t); - ATOM_IDENTITY_TRAITS(int32_t); - ATOM_IDENTITY_TRAITS(uint32_t); - ATOM_IDENTITY_TRAITS(int64_t); - ATOM_IDENTITY_TRAITS(uint64_t); - ATOM_IDENTITY_TRAITS(bool); - ATOM_IDENTITY_TRAITS(float); - ATOM_IDENTITY_TRAITS(std::string); - ATOM_IDENTITY_TRAITS(void*); - -#undef ATOM_IDENTITY_TRAITS +#define NUMBER_IDENTITY_TRAITS(type) \ + number_identity identity_traits::identity; + + NUMBER_IDENTITY_TRAITS(char); + NUMBER_IDENTITY_TRAITS(int8_t); + NUMBER_IDENTITY_TRAITS(uint8_t); + NUMBER_IDENTITY_TRAITS(int16_t); + NUMBER_IDENTITY_TRAITS(uint16_t); + NUMBER_IDENTITY_TRAITS(int32_t); + NUMBER_IDENTITY_TRAITS(uint32_t); + NUMBER_IDENTITY_TRAITS(int64_t); + NUMBER_IDENTITY_TRAITS(uint64_t); + NUMBER_IDENTITY_TRAITS(float); + + bool_identity identity_traits::identity; + stl_string_identity identity_traits::identity; + pointer_identity identity_traits::identity; + stl_ptr_vector_identity identity_traits >::identity; + +#undef NUMBER_IDENTITY_TRAITS } #define TID(type) (&identity_traits< type >::identity) #define FLD(mode, name) struct_field_info::mode, #name, offsetof(CUR_STRUCT, name) -#define GFLD(mode, name) struct_field_info::mode, #name, 0 +#define GFLD(mode, name) struct_field_info::mode, #name, (size_t)&df::global::name #define FLD_END struct_field_info::END // Field definitions diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 3ed4a35cd..27935ae50 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -48,6 +48,155 @@ static luaL_Reg no_functions[] = { { NULL, NULL } }; inline void lua_dup(lua_State *state) { lua_pushvalue(state, -1); } inline void lua_swap(lua_State *state) { lua_insert(state, -2); } +#define UPVAL_TYPETABLE lua_upvalueindex(1) +#define UPVAL_METATABLE lua_upvalueindex(2) +#define UPVAL_FIELDTABLE lua_upvalueindex(3) + +namespace { + struct DFRefHeader { + void *ptr; + }; + + inline bool is_self_contained(DFRefHeader *ptr) { + void **pp = &ptr->ptr; + return **(void****)pp == (pp + 1); + } +} + +static void field_error(lua_State *state, int index, const char *err, const char *mode = "read") +{ + lua_getfield(state, UPVAL_METATABLE, "__metatable"); + const char *cname = lua_tostring(state, -1); + const char *fname = lua_tostring(state, index); + luaL_error(state, "Cannot %s field %s.%s: %s.", + mode, (cname ? cname : "?"), (fname ? fname : "?"), err); +} + +static int push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method = true); +static void *get_object_internal(lua_State *state, type_identity *type, int val_index, bool in_method = true); + +int DFHack::PushDFObject(lua_State *state, type_identity *type, void *ptr) +{ + return push_object_internal(state, type, ptr, false); +} + +void *DFHack::GetDFObject(lua_State *state, type_identity *type, int val_index) +{ + return get_object_internal(state, type, val_index, false); +} + +/* Primitive identity methods */ + +int constructed_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + return push_object_internal(state, this, ptr); +} + +void constructed_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) +{ + field_error(state, fname_idx, "complex object", "write"); +} + +int enum_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + return base_type->lua_read(state, fname_idx, ptr); +} + +void enum_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) +{ + base_type->lua_write(state, fname_idx, ptr, val_index); +} + +int df::number_identity_base::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + lua_pushnumber(state, read(ptr)); + return 1; +} + +void df::number_identity_base::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) +{ + if (!lua_isnumber(state, val_index)) + field_error(state, fname_idx, "number expected", "write"); + + write(ptr, lua_tonumber(state, val_index)); +} + +int df::bool_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + lua_pushboolean(state, *(bool*)ptr); + return 1; +} + +void df::bool_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) +{ + char *pb = (char*)ptr; + + if (lua_isboolean(state, val_index)) + *pb = lua_toboolean(state, val_index); + else if (lua_isnumber(state, val_index)) + *pb = lua_tonumber(state, val_index); + else + field_error(state, fname_idx, "boolean or number expected", "write"); +} + +int df::stl_string_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + auto pstr = (std::string*)ptr; + lua_pushlstring(state, pstr->data(), pstr->size()); + return 1; +} + +void df::stl_string_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) +{ + size_t size; + const char *bytes = lua_tolstring(state, val_index, &size); + if (!bytes) + field_error(state, fname_idx, "string expected", "write"); + + *(std::string*)ptr = std::string(bytes, size); +} + +static int do_read_pointer(lua_State *state, int, void *ptr, type_identity *target) +{ + void *val = *(void**)ptr; + + if (val == NULL) + { + lua_pushnil(state); + return 1; + } + else + return push_object_internal(state, target, val); +} + +int df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +{ + return do_read_pointer(state, fname_idx, ptr, target); +} + +static void do_write_pointer(lua_State *state, int fname_idx, void *ptr, type_identity *target, int val_index) +{ + auto pptr = (void**)ptr; + + if (lua_isnil(state, val_index)) + *pptr = NULL; + else + { + void *nval = get_object_internal(state, target, val_index); + if (nval) + *pptr = nval; + else + field_error(state, fname_idx, "incompatible pointer type", "write"); + } +} + +void df::pointer_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) +{ + do_write_pointer(state, fname_idx, ptr, target, val_index); +} + +/* */ + static int change_error(lua_State *state) { luaL_error(state, "Attempt to change a read-only table.\n"); @@ -111,6 +260,169 @@ static bool RegisterTypeInfo(lua_State *state, void *node) return added; } +static const struct_field_info *find_field(lua_State *state, int index, const char *mode = "read") +{ + lua_pushvalue(state, index); + lua_rawget(state, UPVAL_FIELDTABLE); + + if (!lua_islightuserdata(state, -1)) + field_error(state, index, "not found"); + + void *p = lua_touserdata(state, -1); + lua_pop(state, 1); + return (struct_field_info*)p; +} + +static int read_field(lua_State *state, const struct_field_info *field, void *ptr) +{ + switch (field->mode) + { + case struct_field_info::STATIC_STRING: + { + int len = strnlen((char*)ptr, field->count); + lua_pushlstring(state, (char*)ptr, len); + return 1; + } + + case struct_field_info::PRIMITIVE: + case struct_field_info::SUBSTRUCT: + case struct_field_info::CONTAINER: + return field->type->lua_read(state, 2, ptr); + + case struct_field_info::POINTER: + return do_read_pointer(state, 2, ptr, field->type); + + case struct_field_info::STATIC_ARRAY: + case struct_field_info::STL_VECTOR_PTR: + + case struct_field_info::END: + return 0; + } +} + +static void write_field(lua_State *state, const struct_field_info *field, void *ptr) +{ + switch (field->mode) + { + case struct_field_info::STATIC_STRING: + { + size_t size; + const char *str = lua_tolstring(state, -1, &size); + if (!str) + field_error(state, 2, "string expected", "write"); + memcpy(ptr, str, std::min(size+1, size_t(field->count))); + return; + } + + case struct_field_info::PRIMITIVE: + case struct_field_info::SUBSTRUCT: + case struct_field_info::CONTAINER: + field->type->lua_write(state, 2, ptr, 3); + return; + + case struct_field_info::POINTER: + do_write_pointer(state, 2, ptr, field->type, 3); + + case struct_field_info::STATIC_ARRAY: + case struct_field_info::STL_VECTOR_PTR: + field_error(state, 2, "complex object", "write"); + + case struct_field_info::END: + return; + } +} + +static int meta_global_index(lua_State *state) +{ + const struct_field_info *field = find_field(state, 2); + void *ptr = *(void**)field->offset; + if (!ptr) + field_error(state, 2, "global address not known"); + return read_field(state, field, ptr); +} + +static int meta_global_newindex(lua_State *state) +{ + const struct_field_info *field = find_field(state, 2); + void *ptr = *(void**)field->offset; + if (!ptr) + field_error(state, 2, "global address not known", "write"); + write_field(state, field, ptr); + return 0; +} + +static void IndexFields(lua_State *state, const struct_field_info *fields) +{ + int base = lua_gettop(state); + lua_newtable(state); // read + lua_newtable(state); // write + + for (; fields->mode != struct_field_info::END; ++fields) + { + switch (fields->mode) + { + case struct_field_info::END: + break; + + case struct_field_info::PRIMITIVE: + case struct_field_info::STATIC_STRING: + case struct_field_info::POINTER: + lua_pushstring(state,fields->name); + lua_pushlightuserdata(state,(void*)fields); + lua_settable(state,base+2); + // fallthrough + + case struct_field_info::STATIC_ARRAY: + case struct_field_info::SUBSTRUCT: + case struct_field_info::CONTAINER: + case struct_field_info::STL_VECTOR_PTR: + lua_pushstring(state,fields->name); + lua_pushlightuserdata(state,(void*)fields); + lua_settable(state,base+1); + break; + } + } +} + +static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct, + lua_CFunction reader, lua_CFunction writer) +{ + int base = lua_gettop(state); + + lua_newtable(state); // metatable + IndexFields(state, pstruct->getFields()); // read, write + + lua_pushstring(state, pstruct->getName()); + lua_setfield(state, base+1, "__metatable"); + + lua_pushlightuserdata(state, pstruct); + lua_setfield(state, base+1, "_identity"); + + lua_getfield(state, LUA_REGISTRYINDEX, "DFHack::DFTypes"); + lua_pushvalue(state, base+1); + lua_pushvalue(state, base+2); + lua_pushcclosure(state, reader, 3); + lua_setfield(state, base+1, "__index"); + + lua_getfield(state, LUA_REGISTRYINDEX, "DFHack::DFTypes"); + lua_pushvalue(state, base+1); + lua_pushvalue(state, base+3); + lua_pushcclosure(state, writer, 3); + lua_setfield(state, base+1, "__newindex"); + + // returns: [metatable readfields writefields]; +} + +static int push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method) +{ + return 0; +} + +static void *get_object_internal(lua_State *state, type_identity *type, int val_index, bool in_method) +{ + return NULL; +} + static void RenderTypeChildren(lua_State *state, const std::vector &children); static void RenderType(lua_State *state, compound_identity *node) @@ -150,13 +462,11 @@ static void RenderType(lua_State *state, compound_identity *node) if (eid->getFirstItem() <= eid->getLastItem()) { - lua_pushstring(state, "_first_item"); lua_pushinteger(state, eid->getFirstItem()); - lua_settable(state, base); + lua_setfield(state, base, "_first_item"); - lua_pushstring(state, "_last_item"); lua_pushinteger(state, eid->getLastItem()); - lua_settable(state, base); + lua_setfield(state, base, "_last_item"); } SaveTypeInfo(state, node); @@ -171,7 +481,26 @@ static void RenderType(lua_State *state, compound_identity *node) assert(base == lua_gettop(state)); - freeze_table(state, false, node->getName()); + if (node->type() == IDTYPE_GLOBAL) + { + auto gid = (global_identity*)node; + + MakeFieldMetatable(state, gid, meta_global_index, meta_global_newindex); + lua_pop(state, 2); + + lua_dup(state); + lua_setmetatable(state, base); + lua_swap(state); // -> meta curtable + + freeze_table(state, true, "global"); + lua_getfield(state, base, "__newindex"); + lua_setfield(state, -2, "__newindex"); + lua_pop(state, 1); + + lua_remove(state, base); + } + else + freeze_table(state, false, node->getName()); } static void RenderTypeChildren(lua_State *state, const std::vector &children) diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 3a7679b65..df3af1bb4 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -50,11 +50,13 @@ namespace DFHack enum identity_type { IDTYPE_GLOBAL, IDTYPE_PRIMITIVE, + IDTYPE_POINTER, IDTYPE_CONTAINER, IDTYPE_BITFIELD, IDTYPE_ENUM, IDTYPE_STRUCT, - IDTYPE_CLASS + IDTYPE_CLASS, + IDTYPE_STL_PTR_VECTOR }; typedef void *(*TAllocateFn)(void*,const void*); @@ -80,6 +82,9 @@ namespace DFHack size_t byte_size() { return size; } virtual identity_type type() = 0; + + virtual int lua_read(lua_State *state, int fname_idx, void *ptr) = 0; + virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) = 0; }; class DFHACK_EXPORT constructed_identity : public type_identity { @@ -96,6 +101,9 @@ namespace DFHack if (allocator) allocator(tgt,src); else type_identity::do_copy(tgt, src); }; + + virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); }; class DFHACK_EXPORT compound_identity : public constructed_identity { @@ -149,9 +157,12 @@ namespace DFHack int64_t first_item_value; int64_t last_item_value; + type_identity *base_type; + public: enum_identity(size_t size, TAllocateFn alloc, compound_identity *scope_parent, const char *dfhack_name, + type_identity *base_type, int64_t first_item_value, int64_t last_item_value, const char *const *keys); @@ -161,6 +172,11 @@ namespace DFHack int64_t getLastItem() { return last_item_value; } int getCount() { return int(last_item_value-first_item_value+1); } const char *const *getKeys() { return keys; } + + type_identity *getBaseType() { return base_type; } + + virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); }; struct struct_field_info { @@ -187,6 +203,8 @@ namespace DFHack std::vector children; bool has_children; + const struct_field_info *fields; + protected: virtual void doInit(Core *core); @@ -201,6 +219,8 @@ namespace DFHack const std::vector &getChildren() { return children; } bool hasChildren() { return has_children; } + const struct_field_info *getFields() { return fields; } + bool is_subclass(struct_identity *subtype); }; @@ -286,6 +306,9 @@ namespace DFHack DFHACK_EXPORT void AttachDFGlobals(lua_State *state); + DFHACK_EXPORT int PushDFObject(lua_State *state, type_identity *type, void *ptr); + DFHACK_EXPORT void *GetDFObject(lua_State *state, type_identity *type, int val_index); + template T *ifnull(T *a, T *b) { return a ? a : b; } diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index d354beb03..4443518ab 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -44,6 +44,21 @@ namespace DFHack virtual identity_type type() { return IDTYPE_PRIMITIVE; } }; + class DFHACK_EXPORT pointer_identity : public primitive_identity { + type_identity *target; + + public: + pointer_identity(type_identity *target = NULL) + : primitive_identity(sizeof(void*)), target(target) {}; + + virtual identity_type type() { return IDTYPE_POINTER; } + + type_identity *getTarget() { return target; } + + virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); + }; + class DFHACK_EXPORT container_identity : public constructed_identity { type_identity *item; enum_identity *ienum; @@ -53,35 +68,101 @@ namespace DFHack : constructed_identity(size, alloc), item(item), ienum(ienum) {}; virtual identity_type type() { return IDTYPE_CONTAINER; } + + type_identity *getItemType() { return item; } }; } namespace df { using DFHack::primitive_identity; + using DFHack::pointer_identity; using DFHack::container_identity; -#define ATOM_IDENTITY_TRAITS(type) \ + class number_identity_base : public primitive_identity { + public: + number_identity_base(size_t size) : primitive_identity(size) {}; + + virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); + + protected: + virtual double read(void *ptr) = 0; + virtual void write(void *ptr, double val) = 0; + }; + + template + class number_identity : public number_identity_base { + public: + number_identity() : number_identity_base(sizeof(T)) {} + protected: + virtual double read(void *ptr) { return double(*(T*)ptr); } + virtual void write(void *ptr, double val) { *(T*)ptr = T(val); } + }; + + class bool_identity : public primitive_identity { + public: + bool_identity() : primitive_identity(sizeof(bool)) {}; + + virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); + }; + + class stl_string_identity : public primitive_identity { + public: + stl_string_identity() : primitive_identity(sizeof(std::string)) {}; + + virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); + }; + + class stl_ptr_vector_identity : public container_identity { + public: + stl_ptr_vector_identity(type_identity *item = NULL, enum_identity *ienum = NULL) + : container_identity(sizeof(std::vector),allocator_fn >,item, ienum) + {}; + + virtual DFHack::identity_type type() { return DFHack::IDTYPE_STL_PTR_VECTOR; } + }; + +#define NUMBER_IDENTITY_TRAITS(type) \ template<> struct identity_traits { \ - static primitive_identity identity; \ - static primitive_identity *get() { return &identity; } \ - }; - - ATOM_IDENTITY_TRAITS(char); - ATOM_IDENTITY_TRAITS(int8_t); - ATOM_IDENTITY_TRAITS(uint8_t); - ATOM_IDENTITY_TRAITS(int16_t); - ATOM_IDENTITY_TRAITS(uint16_t); - ATOM_IDENTITY_TRAITS(int32_t); - ATOM_IDENTITY_TRAITS(uint32_t); - ATOM_IDENTITY_TRAITS(int64_t); - ATOM_IDENTITY_TRAITS(uint64_t); - ATOM_IDENTITY_TRAITS(bool); - ATOM_IDENTITY_TRAITS(float); - ATOM_IDENTITY_TRAITS(std::string); - ATOM_IDENTITY_TRAITS(void*); - -#undef ATOM_IDENTITY_TRAITS + static number_identity identity; \ + static number_identity_base *get() { return &identity; } \ + }; + + NUMBER_IDENTITY_TRAITS(char); + NUMBER_IDENTITY_TRAITS(int8_t); + NUMBER_IDENTITY_TRAITS(uint8_t); + NUMBER_IDENTITY_TRAITS(int16_t); + NUMBER_IDENTITY_TRAITS(uint16_t); + NUMBER_IDENTITY_TRAITS(int32_t); + NUMBER_IDENTITY_TRAITS(uint32_t); + NUMBER_IDENTITY_TRAITS(int64_t); + NUMBER_IDENTITY_TRAITS(uint64_t); + NUMBER_IDENTITY_TRAITS(float); + + template<> struct identity_traits { + static bool_identity identity; + static bool_identity *get() { return &identity; } + }; + + template<> struct identity_traits { + static stl_string_identity identity; + static stl_string_identity *get() { return &identity; } + }; + + template<> struct identity_traits { + static pointer_identity identity; + static pointer_identity *get() { return &identity; } + }; + + template<> struct identity_traits > { + static stl_ptr_vector_identity identity; + static stl_ptr_vector_identity *get() { return &identity; } + }; + +#undef NUMBER_IDENTITY_TRAITS // Container declarations @@ -90,7 +171,7 @@ namespace df }; template struct identity_traits { - static container_identity *get(); + static pointer_identity *get(); }; template struct identity_traits { @@ -101,6 +182,10 @@ namespace df static container_identity *get(); }; + template struct identity_traits > { + static stl_ptr_vector_identity *get(); + }; + template struct identity_traits > { static container_identity *get(); }; @@ -117,15 +202,12 @@ namespace df template primitive_identity *identity_traits >::get() { - static primitive_identity identity(sizeof(FT)); - return &identity; + return identity_traits::get(); } template - container_identity *identity_traits::get() { - typedef T * container; - static container_identity identity(sizeof(container), &allocator_fn, - identity_traits::get()); + pointer_identity *identity_traits::get() { + static pointer_identity identity(identity_traits::get()); return &identity; } @@ -145,6 +227,12 @@ namespace df return &identity; } + template + stl_ptr_vector_identity *identity_traits >::get() { + static stl_ptr_vector_identity identity(identity_traits::get()); + return &identity; + } + template container_identity *identity_traits >::get() { typedef std::deque container; diff --git a/library/xml b/library/xml index 70eb6b5f3..c90a2d499 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 70eb6b5f35680655d04d9fda79ff7251e21b45ae +Subproject commit c90a2d499024319ea9aa4f98b3b61df7bba2fc62 From 73e138c9fdf64cab23330fc13b73383d8648324d Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Tue, 20 Mar 2012 21:34:27 +0400 Subject: [PATCH 07/25] Support ordinary struct and class fields. --- library/CMakeLists.txt | 2 +- library/LuaWrapper.cpp | 389 +++++++++++++++++++++++++++------ library/include/DataDefs.h | 2 +- library/include/DataIdentity.h | 3 + 4 files changed, 322 insertions(+), 74 deletions(-) diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 3b1c6e7bf..72bf91e62 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -200,7 +200,7 @@ ADD_CUSTOM_TARGET(generate_headers DEPENDS ${dfapi_SOURCE_DIR}/include/df/static IF(UNIX) # Don't produce debug info for generated stubs SET_SOURCE_FILES_PROPERTIES(DataStatics.cpp DataStaticsCtor.cpp DataStaticsFields.cpp - PROPERTIES COMPILE_FLAGS "-g0") + PROPERTIES COMPILE_FLAGS "-g0 -O1") ELSE(WIN32) ENDIF() diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 27935ae50..7081cd259 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -48,6 +48,12 @@ static luaL_Reg no_functions[] = { { NULL, NULL } }; inline void lua_dup(lua_State *state) { lua_pushvalue(state, -1); } inline void lua_swap(lua_State *state) { lua_insert(state, -2); } +#define DFHACK_TYPETABLE_NAME "DFHack::DFTypes" +#define DFHACK_TYPEID_TABLE_NAME "DFHack::DFTypeIds" +#define DFHACK_CHANGEERROR_NAME "DFHack::ChangeError" +#define DFHACK_COMPARE_NAME "DFHack::ComparePtrs" +#define DFHACK_TYPE_TOSTRING_NAME "DFHack::TypeToString" + #define UPVAL_TYPETABLE lua_upvalueindex(1) #define UPVAL_METATABLE lua_upvalueindex(2) #define UPVAL_FIELDTABLE lua_upvalueindex(3) @@ -63,7 +69,7 @@ namespace { } } -static void field_error(lua_State *state, int index, const char *err, const char *mode = "read") +static void field_error(lua_State *state, int index, const char *err, const char *mode) { lua_getfield(state, UPVAL_METATABLE, "__metatable"); const char *cname = lua_tostring(state, -1); @@ -72,12 +78,12 @@ static void field_error(lua_State *state, int index, const char *err, const char mode, (cname ? cname : "?"), (fname ? fname : "?"), err); } -static int push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method = true); +static void push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method = true); static void *get_object_internal(lua_State *state, type_identity *type, int val_index, bool in_method = true); -int DFHack::PushDFObject(lua_State *state, type_identity *type, void *ptr) +void DFHack::PushDFObject(lua_State *state, type_identity *type, void *ptr) { - return push_object_internal(state, type, ptr, false); + push_object_internal(state, type, ptr, false); } void *DFHack::GetDFObject(lua_State *state, type_identity *type, int val_index) @@ -89,7 +95,8 @@ void *DFHack::GetDFObject(lua_State *state, type_identity *type, int val_index) int constructed_identity::lua_read(lua_State *state, int fname_idx, void *ptr) { - return push_object_internal(state, this, ptr); + push_object_internal(state, this, ptr); + return 1; } void constructed_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) @@ -156,25 +163,19 @@ void df::stl_string_identity::lua_write(lua_State *state, int fname_idx, void *p *(std::string*)ptr = std::string(bytes, size); } -static int do_read_pointer(lua_State *state, int, void *ptr, type_identity *target) +int df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target) { - void *val = *(void**)ptr; - - if (val == NULL) - { - lua_pushnil(state); - return 1; - } - else - return push_object_internal(state, target, val); + push_object_internal(state, target, *(void**)ptr); + return 1; } int df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr) { - return do_read_pointer(state, fname_idx, ptr, target); + return lua_read(state, fname_idx, ptr, target); } -static void do_write_pointer(lua_State *state, int fname_idx, void *ptr, type_identity *target, int val_index) +void df::pointer_identity::lua_write(lua_State *state, int fname_idx, void *ptr, + type_identity *target, int val_index) { auto pptr = (void**)ptr; @@ -192,7 +193,7 @@ static void do_write_pointer(lua_State *state, int fname_idx, void *ptr, type_id void df::pointer_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) { - do_write_pointer(state, fname_idx, ptr, target, val_index); + lua_write(state, fname_idx, ptr, target, val_index); } /* */ @@ -209,7 +210,7 @@ static void freeze_table(lua_State *state, bool leave_metatable = false, const c lua_newtable(state); lua_swap(state); lua_setfield(state, base, "__index"); - lua_getfield(state, LUA_REGISTRYINDEX, "DFHack.ChangeError"); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_CHANGEERROR_NAME); lua_setfield(state, base, "__newindex"); lua_newtable(state); lua_swap(state); @@ -225,54 +226,179 @@ static void freeze_table(lua_State *state, bool leave_metatable = false, const c lua_pop(state, 1); } -static void SaveTypeInfo(lua_State *state, void *node) +static bool LookupTypeInfo(lua_State *state, bool in_method) +{ + // stack: [lookup key] + + if (in_method) + { + lua_rawget(state, UPVAL_TYPETABLE); + } + else + { + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME); + lua_swap(state); + lua_rawget(state, -2); + lua_remove(state, -2); + } + + // stack: [info] + + if (lua_isnil(state, -1)) + { + lua_pop(state, 1); + return false; + } + else + return true; +} + +static void SaveInTable(lua_State *state, void *node, const char *tname) { - lua_getfield(state, LUA_REGISTRYINDEX, "DFHack.DFTypes"); + // stack: [info] + lua_getfield(state, LUA_REGISTRYINDEX, tname); + lua_pushlightuserdata(state, node); lua_pushvalue(state, -3); - lua_settable(state, -3); + lua_rawset(state, -3); + + lua_pushvalue(state, -2); + lua_pushlightuserdata(state, node); + lua_rawset(state, -3); + lua_pop(state, 1); + // stack: [info] } -static bool RegisterTypeInfo(lua_State *state, void *node) +static void SaveTypeInfo(lua_State *state, void *node) { - lua_getfield(state, LUA_REGISTRYINDEX, "DFHack.DFTypes"); - int base = lua_gettop(state); + SaveInTable(state, node, DFHACK_TYPETABLE_NAME); +} - lua_pushlightuserdata(state, node); - lua_rawget(state, base); +static void BuildTypeMetatable(lua_State *state, type_identity *type); - bool added = false; +static void push_object_ref(lua_State *state, void *ptr) +{ + // stack: [metatable] + auto ref = (DFRefHeader*)lua_newuserdata(state, sizeof(DFRefHeader)); + ref->ptr = ptr; - if (lua_isnil(state, -1)) + lua_swap(state); + lua_setmetatable(state, -2); + // stack: [userdata] +} + +static void push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method) +{ + if (!ptr || !type) { - lua_pop(state, 1); + if (!ptr) + lua_pushnil(state); + else + lua_pushlightuserdata(state, ptr); - lua_newtable(state); - lua_pushlightuserdata(state, node); - lua_pushvalue(state, -2); - lua_rawset(state, base); + return; + } - added = true; + // Resolve actual class using vtable + if (type->type() == IDTYPE_CLASS) + { + virtual_identity *class_vid = virtual_identity::get(virtual_ptr(ptr)); + if (class_vid) + type = class_vid; } - lua_remove(state, -2); - return added; + lua_pushlightuserdata(state, type); // () -> type + + if (!LookupTypeInfo(state, in_method)) // type -> metatable? + { + BuildTypeMetatable(state, type); // () -> metatable + SaveTypeInfo(state, type); + } + + push_object_ref(state, ptr); // metatable -> userdata } -static const struct_field_info *find_field(lua_State *state, int index, const char *mode = "read") +static void *get_object_internal(lua_State *state, type_identity *type, int val_index, bool in_method) +{ + if (!lua_isuserdata(state, val_index)) + return NULL; + + if (!lua_getmetatable(state, val_index)) // () -> metatable? + { + if (!type && lua_islightuserdata(state, val_index)) + return lua_touserdata(state, val_index); + + return NULL; + } + + // Verify known metatable and correct type + if (!LookupTypeInfo(state, in_method)) // metatable -> type? + return NULL; + + bool ok = lua_islightuserdata(state, -1) && + (!type || lua_touserdata(state, -1) == type); + + lua_pop(state, 1); // type -> () + + if (!ok) + return NULL; + + auto ref = (DFRefHeader*)lua_touserdata(state, val_index); + return ref->ptr; +} + + +static int meta_ptr_compare(lua_State *state) +{ + if (!lua_isuserdata(state, 1) || !lua_isuserdata(state, 2) || + !lua_getmetatable(state, 1) || !lua_getmetatable(state, 2)) + { + lua_pushboolean(state, false); + return 1; + } + + if (!lua_equal(state, -1, -2)) + { + // todo: nonidentical type comparison + lua_pushboolean(state, false); + return 1; + } + + auto ref1 = (DFRefHeader*)lua_touserdata(state, 1); + auto ref2 = (DFRefHeader*)lua_touserdata(state, 2); + lua_pushboolean(state, ref1->ptr == ref2->ptr); + return 1; +} + +static const struct_field_info *find_field(lua_State *state, int index, const char *mode) { lua_pushvalue(state, index); lua_rawget(state, UPVAL_FIELDTABLE); if (!lua_islightuserdata(state, -1)) - field_error(state, index, "not found"); + field_error(state, index, "not found", mode); void *p = lua_touserdata(state, -1); lua_pop(state, 1); return (struct_field_info*)p; } +static uint8_t *get_object_addr(lua_State *state, int obj, int field, const char *mode) +{ + if (!lua_isuserdata(state, obj) || + !lua_getmetatable(state, obj)) + field_error(state, field, "invalid object", mode); + + if (!lua_equal(state, -1, UPVAL_METATABLE)) + field_error(state, field, "invalid object metatable", mode); + + lua_pop(state, 1); + + auto ref = (DFRefHeader*)lua_touserdata(state, obj); + return (uint8_t*)ref->ptr; +} + static int read_field(lua_State *state, const struct_field_info *field, void *ptr) { switch (field->mode) @@ -290,7 +416,7 @@ static int read_field(lua_State *state, const struct_field_info *field, void *pt return field->type->lua_read(state, 2, ptr); case struct_field_info::POINTER: - return do_read_pointer(state, 2, ptr, field->type); + return df::pointer_identity::lua_read(state, 2, ptr, field->type); case struct_field_info::STATIC_ARRAY: case struct_field_info::STL_VECTOR_PTR: @@ -300,14 +426,14 @@ static int read_field(lua_State *state, const struct_field_info *field, void *pt } } -static void write_field(lua_State *state, const struct_field_info *field, void *ptr) +static void write_field(lua_State *state, const struct_field_info *field, void *ptr, int value_idx) { switch (field->mode) { case struct_field_info::STATIC_STRING: { size_t size; - const char *str = lua_tolstring(state, -1, &size); + const char *str = lua_tolstring(state, value_idx, &size); if (!str) field_error(state, 2, "string expected", "write"); memcpy(ptr, str, std::min(size+1, size_t(field->count))); @@ -317,11 +443,11 @@ static void write_field(lua_State *state, const struct_field_info *field, void * case struct_field_info::PRIMITIVE: case struct_field_info::SUBSTRUCT: case struct_field_info::CONTAINER: - field->type->lua_write(state, 2, ptr, 3); + field->type->lua_write(state, 2, ptr, value_idx); return; case struct_field_info::POINTER: - do_write_pointer(state, 2, ptr, field->type, 3); + df::pointer_identity::lua_write(state, 2, ptr, field->type, value_idx); case struct_field_info::STATIC_ARRAY: case struct_field_info::STL_VECTOR_PTR: @@ -332,22 +458,70 @@ static void write_field(lua_State *state, const struct_field_info *field, void * } } +static int meta_type_tostring(lua_State *state) +{ + if (!lua_getmetatable(state, 1)) + return 0; + + lua_getfield(state, -1, "__metatable"); + const char *cname = lua_tostring(state, -1); + + lua_pushstring(state, stl_sprintf("", cname).c_str()); + return 1; +} + +static int meta_ptr_tostring(lua_State *state) +{ + uint8_t *ptr = get_object_addr(state, 1, 0, "access"); + + lua_getfield(state, UPVAL_METATABLE, "__metatable"); + const char *cname = lua_tostring(state, -1); + + lua_pushstring(state, stl_sprintf("<%s: 0x%08x>", cname, (unsigned)ptr).c_str()); + return 1; +} + +static int get_metafield(lua_State *state) +{ + lua_rawget(state, UPVAL_METATABLE); + return 1; +} + +static int meta_struct_index(lua_State *state) +{ + uint8_t *ptr = get_object_addr(state, 1, 2, "read"); + const struct_field_info *field = find_field(state, 2, "read"); + if (!field) + return get_metafield(state); + return read_field(state, field, ptr + field->offset); +} + +static int meta_struct_newindex(lua_State *state) +{ + uint8_t *ptr = get_object_addr(state, 1, 2, "write"); + const struct_field_info *field = find_field(state, 2, "write"); + write_field(state, field, ptr + field->offset, 3); + return 0; +} + static int meta_global_index(lua_State *state) { - const struct_field_info *field = find_field(state, 2); + const struct_field_info *field = find_field(state, 2, "read"); + if (!field) + return get_metafield(state); void *ptr = *(void**)field->offset; if (!ptr) - field_error(state, 2, "global address not known"); + field_error(state, 2, "global address not known", "read"); return read_field(state, field, ptr); } static int meta_global_newindex(lua_State *state) { - const struct_field_info *field = find_field(state, 2); + const struct_field_info *field = find_field(state, 2, "write"); void *ptr = *(void**)field->offset; if (!ptr) field_error(state, 2, "global address not known", "write"); - write_field(state, field, ptr); + write_field(state, field, ptr, 3); return 0; } @@ -369,7 +543,7 @@ static void IndexFields(lua_State *state, const struct_field_info *fields) case struct_field_info::POINTER: lua_pushstring(state,fields->name); lua_pushlightuserdata(state,(void*)fields); - lua_settable(state,base+2); + lua_rawset(state,base+2); // fallthrough case struct_field_info::STATIC_ARRAY: @@ -378,12 +552,40 @@ static void IndexFields(lua_State *state, const struct_field_info *fields) case struct_field_info::STL_VECTOR_PTR: lua_pushstring(state,fields->name); lua_pushlightuserdata(state,(void*)fields); - lua_settable(state,base+1); + lua_rawset(state,base+1); break; } } } +static void SetPtrMethods(lua_State *state, int meta_idx, void *node) +{ + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_COMPARE_NAME); + lua_setfield(state, meta_idx, "__eq"); + + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME); + lua_pushvalue(state, meta_idx); + lua_pushcclosure(state, meta_ptr_tostring, 2); + lua_setfield(state, meta_idx, "__tostring"); + + // type field + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPEID_TABLE_NAME); + lua_pushlightuserdata(state, node); + lua_rawget(state, -2); + lua_setfield(state, meta_idx, "_type"); + lua_pop(state, 1); +} + +static void SetStructMethod(lua_State *state, int meta_idx, int ftable_idx, + lua_CFunction function, const char *name) +{ + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME); + lua_pushvalue(state, meta_idx); + lua_pushvalue(state, ftable_idx); + lua_pushcclosure(state, function, 3); + lua_setfield(state, meta_idx, name); +} + static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct, lua_CFunction reader, lua_CFunction writer) { @@ -395,32 +597,52 @@ static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct, lua_pushstring(state, pstruct->getName()); lua_setfield(state, base+1, "__metatable"); - lua_pushlightuserdata(state, pstruct); - lua_setfield(state, base+1, "_identity"); + SetStructMethod(state, base+1, base+2, reader, "__index"); + SetStructMethod(state, base+1, base+3, writer, "__newindex"); - lua_getfield(state, LUA_REGISTRYINDEX, "DFHack::DFTypes"); - lua_pushvalue(state, base+1); - lua_pushvalue(state, base+2); - lua_pushcclosure(state, reader, 3); - lua_setfield(state, base+1, "__index"); + // Custom fields - lua_getfield(state, LUA_REGISTRYINDEX, "DFHack::DFTypes"); - lua_pushvalue(state, base+1); - lua_pushvalue(state, base+3); - lua_pushcclosure(state, writer, 3); - lua_setfield(state, base+1, "__newindex"); + lua_pushlightuserdata(state, pstruct); + lua_setfield(state, base+1, "_identity"); // returns: [metatable readfields writefields]; } -static int push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method) +static void EnableMetaField(lua_State *state, int ftable_idx, const char *name) { - return 0; + lua_pushlightuserdata(state, NULL); + lua_setfield(state, ftable_idx, name); } -static void *get_object_internal(lua_State *state, type_identity *type, int val_index, bool in_method) +static void BuildTypeMetatable(lua_State *state, type_identity *type) { - return NULL; + int base = lua_gettop(state); + + switch (type->type()) + { + case IDTYPE_GLOBAL: + assert(false); + + case IDTYPE_STRUCT: + case IDTYPE_CLASS: + MakeFieldMetatable(state, (struct_identity*)type, meta_struct_index, meta_struct_newindex); + SetPtrMethods(state, base+1, type); + EnableMetaField(state, base+2, "_type"); + lua_pop(state, 2); + return; + + case IDTYPE_PRIMITIVE: + case IDTYPE_ENUM: + case IDTYPE_POINTER: + luaL_error(state, "primitive not implemented"); + + case IDTYPE_BITFIELD: + luaL_error(state, "bitfield not implemented"); + + case IDTYPE_CONTAINER: + case IDTYPE_STL_PTR_VECTOR: + luaL_error(state, "container not implemented"); + } } static void RenderTypeChildren(lua_State *state, const std::vector &children); @@ -456,8 +678,8 @@ static void RenderType(lua_State *state, compound_identity *node) lua_dup(state); lua_pushinteger(state, i); - lua_settable(state, base); - lua_settable(state, base); + lua_rawset(state, base); + lua_rawset(state, base); } if (eid->getFirstItem() <= eid->getLastItem()) @@ -493,14 +715,28 @@ static void RenderType(lua_State *state, compound_identity *node) lua_swap(state); // -> meta curtable freeze_table(state, true, "global"); + lua_getfield(state, base, "__newindex"); lua_setfield(state, -2, "__newindex"); + + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME); + lua_setfield(state, -2, "__tostring"); + lua_pop(state, 1); lua_remove(state, base); } else - freeze_table(state, false, node->getName()); + { + freeze_table(state, true, node->getName()); + + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME); + lua_setfield(state, -2, "__tostring"); + + lua_pop(state, 1); + } + + SaveInTable(state, node, DFHACK_TYPEID_TABLE_NAME); } static void RenderTypeChildren(lua_State *state, const std::vector &children) @@ -510,7 +746,7 @@ static void RenderTypeChildren(lua_State *state, const std::vectorgetName()); lua_swap(state); - lua_settable(state, -3); + lua_rawset(state, -3); } } @@ -518,8 +754,17 @@ static void DoAttach(lua_State *state) { int base = lua_gettop(state); + lua_newtable(state); + lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_TYPEID_TABLE_NAME); + lua_pushcfunction(state, change_error); - lua_setfield(state, LUA_REGISTRYINDEX, "DFHack.ChangeError"); + lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_CHANGEERROR_NAME); + + lua_pushcfunction(state, meta_ptr_compare); + lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_COMPARE_NAME); + + lua_pushcfunction(state, meta_type_tostring); + lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME); luaL_register(state, "df", no_functions); @@ -540,7 +785,7 @@ static void DoAttach(lua_State *state) void DFHack::AttachDFGlobals(lua_State *state) { - if (luaL_newmetatable(state, "DFHack.DFTypes")) + if (luaL_newmetatable(state, DFHACK_TYPETABLE_NAME)) DoAttach(state); lua_pop(state, 1); diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index df3af1bb4..8562a18d6 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -306,7 +306,7 @@ namespace DFHack DFHACK_EXPORT void AttachDFGlobals(lua_State *state); - DFHACK_EXPORT int PushDFObject(lua_State *state, type_identity *type, void *ptr); + DFHACK_EXPORT void PushDFObject(lua_State *state, type_identity *type, void *ptr); DFHACK_EXPORT void *GetDFObject(lua_State *state, type_identity *type, int val_index); template diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index 4443518ab..28426c58a 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -55,6 +55,9 @@ namespace DFHack type_identity *getTarget() { return target; } + static int lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target); + static void lua_write(lua_State *state, int fname_idx, void *ptr, type_identity *target, int val_index); + virtual int lua_read(lua_State *state, int fname_idx, void *ptr); virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); }; From 9b78fffe9255e0fd14b6e446652598a36372b934 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 21 Mar 2012 13:26:53 +0400 Subject: [PATCH 08/25] Support containers in the lua wrapper. --- library/DataDefs.cpp | 35 +++ library/DataStaticsFields.cpp | 5 +- library/LuaWrapper.cpp | 444 +++++++++++++++++++++++++++------ library/include/DataDefs.h | 13 + library/include/DataIdentity.h | 197 +++++++++++++-- 5 files changed, 602 insertions(+), 92 deletions(-) diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index e8ee14712..9576afb7b 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.cpp @@ -34,6 +34,7 @@ distribution. #include "tinythread.h" // must be last due to MS stupidity #include "DataDefs.h" +#include "DataIdentity.h" #include "MiscUtils.h" @@ -62,6 +63,14 @@ void compound_identity::doInit(Core *) top_scope.push_back(this); } +std::string compound_identity::getFullName() +{ + if (scope_parent) + return scope_parent->getFullName() + "." + getName(); + else + return getName(); +} + static tthread::mutex *known_mutex = NULL; void compound_identity::Init(Core *core) @@ -130,6 +139,32 @@ bool struct_identity::is_subclass(struct_identity *actual) return false; } +std::string pointer_identity::getFullName() +{ + return (target ? target->getFullName() : std::string("void")) + "*"; +} + +std::string container_identity::getFullName(type_identity *item) +{ + return "<" + (item ? item->getFullName() : std::string("void")) + ">"; +} + +std::string ptr_container_identity::getFullName(type_identity *item) +{ + return "<" + (item ? item->getFullName() : std::string("void")) + "*>"; +} + +std::string bit_container_identity::getFullName(type_identity *) +{ + return ""; +} + +std::string df::buffer_container_identity::getFullName(type_identity *item) +{ + return (item ? item->getFullName() : std::string("void")) + + (size > 0 ? stl_sprintf("[%d]", size) : std::string("[]")); +} + virtual_identity::virtual_identity(size_t size, TAllocateFn alloc, const char *dfhack_name, const char *original_name, virtual_identity *parent, const struct_field_info *fields) diff --git a/library/DataStaticsFields.cpp b/library/DataStaticsFields.cpp index 22ce9b6df..89736369f 100644 --- a/library/DataStaticsFields.cpp +++ b/library/DataStaticsFields.cpp @@ -15,7 +15,7 @@ namespace df { #define NUMBER_IDENTITY_TRAITS(type) \ - number_identity identity_traits::identity; + number_identity identity_traits::identity(#type); NUMBER_IDENTITY_TRAITS(char); NUMBER_IDENTITY_TRAITS(int8_t); @@ -32,6 +32,9 @@ namespace df { stl_string_identity identity_traits::identity; pointer_identity identity_traits::identity; stl_ptr_vector_identity identity_traits >::identity; + stl_bit_vector_identity identity_traits >::identity; + + buffer_container_identity buffer_container_identity::base_instance; #undef NUMBER_IDENTITY_TRAITS } diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 7081cd259..d4755bc0f 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -50,6 +50,7 @@ inline void lua_swap(lua_State *state) { lua_insert(state, -2); } #define DFHACK_TYPETABLE_NAME "DFHack::DFTypes" #define DFHACK_TYPEID_TABLE_NAME "DFHack::DFTypeIds" +#define DFHACK_ENUM_TABLE_NAME "DFHack::DFEnums" #define DFHACK_CHANGEERROR_NAME "DFHack::ChangeError" #define DFHACK_COMPARE_NAME "DFHack::ComparePtrs" #define DFHACK_TYPE_TOSTRING_NAME "DFHack::TypeToString" @@ -58,6 +59,10 @@ inline void lua_swap(lua_State *state) { lua_insert(state, -2); } #define UPVAL_METATABLE lua_upvalueindex(2) #define UPVAL_FIELDTABLE lua_upvalueindex(3) +#define UPVAL_CONTAINER_ID lua_upvalueindex(4) +#define UPVAL_ITEM_ID lua_upvalueindex(5) +#define UPVAL_ITEM_COUNT lua_upvalueindex(6) + namespace { struct DFRefHeader { void *ptr; @@ -138,10 +143,10 @@ void df::bool_identity::lua_write(lua_State *state, int fname_idx, void *ptr, in { char *pb = (char*)ptr; - if (lua_isboolean(state, val_index)) + if (lua_isboolean(state, val_index) || lua_isnil(state, val_index)) *pb = lua_toboolean(state, val_index); else if (lua_isnumber(state, val_index)) - *pb = lua_tonumber(state, val_index); + *pb = lua_tointeger(state, val_index); else field_error(state, fname_idx, "boolean or number expected", "write"); } @@ -196,6 +201,72 @@ void df::pointer_identity::lua_write(lua_State *state, int fname_idx, void *ptr, lua_write(state, fname_idx, ptr, target, val_index); } +int container_identity::lua_item_count(lua_State *state, void *ptr) +{ + if (lua_isnumber(state, UPVAL_ITEM_COUNT)) + return lua_tointeger(state, UPVAL_ITEM_COUNT); + else + return item_count(ptr); +} + +int container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) +{ + void *pitem = item_pointer(ptr, idx); + auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + return id->lua_read(state, fname_idx, pitem); +} + +void container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index) +{ + void *pitem = item_pointer(ptr, idx); + auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + id->lua_write(state, fname_idx, pitem, val_index); +} + +int ptr_container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) +{ + void *pitem = item_pointer(ptr, idx); + auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + return df::pointer_identity::lua_read(state, fname_idx, pitem, id); +} + +void ptr_container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index) +{ + void *pitem = item_pointer(ptr, idx); + auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + df::pointer_identity::lua_write(state, fname_idx, pitem, id, val_index); +} + +int bit_container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) +{ + lua_pushboolean(state, get_item(ptr, idx)); + return 1; +} + +void bit_container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index) +{ + if (lua_isboolean(state, val_index) || lua_isnil(state, val_index)) + set_item(ptr, idx, lua_toboolean(state, val_index)); + else if (lua_isnumber(state, val_index)) + set_item(ptr, idx, lua_tointeger(state, val_index) != 0); + else + field_error(state, fname_idx, "boolean or number expected", "write"); +} + +int df::buffer_container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) +{ + auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + void *pitem = ((uint8_t*)ptr) + idx * id->byte_size(); + return id->lua_read(state, fname_idx, pitem); +} + +void df::buffer_container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index) +{ + auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + void *pitem = ((uint8_t*)ptr) + idx * id->byte_size(); + id->lua_write(state, fname_idx, pitem, val_index); +} + /* */ static int change_error(lua_State *state) @@ -253,6 +324,14 @@ static bool LookupTypeInfo(lua_State *state, bool in_method) return true; } +static void LookupInTable(lua_State *state, void *id, const char *tname) +{ + lua_getfield(state, LUA_REGISTRYINDEX, tname); + lua_pushlightuserdata(state, id); + lua_rawget(state, -2); + lua_remove(state, -2); +} + static void SaveInTable(lua_State *state, void *node, const char *tname) { // stack: [info] @@ -311,10 +390,7 @@ static void push_object_internal(lua_State *state, type_identity *type, void *pt lua_pushlightuserdata(state, type); // () -> type if (!LookupTypeInfo(state, in_method)) // type -> metatable? - { BuildTypeMetatable(state, type); // () -> metatable - SaveTypeInfo(state, type); - } push_object_ref(state, ptr); // metatable -> userdata } @@ -371,17 +447,22 @@ static int meta_ptr_compare(lua_State *state) return 1; } -static const struct_field_info *find_field(lua_State *state, int index, const char *mode) +static void lookup_field(lua_State *state, int index, const char *mode) { lua_pushvalue(state, index); - lua_rawget(state, UPVAL_FIELDTABLE); + lua_gettable(state, UPVAL_FIELDTABLE); // uses metatable with enum keys - if (!lua_islightuserdata(state, -1)) + if (lua_isnil(state, -1)) field_error(state, index, "not found", mode); +} + +static void *find_field(lua_State *state, int index, const char *mode) +{ + lookup_field(state, index, mode); void *p = lua_touserdata(state, -1); lua_pop(state, 1); - return (struct_field_info*)p; + return p; } static uint8_t *get_object_addr(lua_State *state, int obj, int field, const char *mode) @@ -399,6 +480,8 @@ static uint8_t *get_object_addr(lua_State *state, int obj, int field, const char return (uint8_t*)ref->ptr; } +static void GetAdHocMetatable(lua_State *state, const struct_field_info *field); + static int read_field(lua_State *state, const struct_field_info *field, void *ptr) { switch (field->mode) @@ -412,14 +495,21 @@ static int read_field(lua_State *state, const struct_field_info *field, void *pt case struct_field_info::PRIMITIVE: case struct_field_info::SUBSTRUCT: - case struct_field_info::CONTAINER: return field->type->lua_read(state, 2, ptr); case struct_field_info::POINTER: return df::pointer_identity::lua_read(state, 2, ptr, field->type); + case struct_field_info::CONTAINER: + if (!field->eid || !field->type->isContainer() || + field->eid == ((container_identity*)field->type)->getIndexEnumType()) + return field->type->lua_read(state, 2, ptr); + case struct_field_info::STATIC_ARRAY: case struct_field_info::STL_VECTOR_PTR: + GetAdHocMetatable(state, field); + push_object_ref(state, ptr); + return 1; case struct_field_info::END: return 0; @@ -490,7 +580,7 @@ static int get_metafield(lua_State *state) static int meta_struct_index(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "read"); - const struct_field_info *field = find_field(state, 2, "read"); + auto field = (struct_field_info*)find_field(state, 2, "read"); if (!field) return get_metafield(state); return read_field(state, field, ptr + field->offset); @@ -499,14 +589,88 @@ static int meta_struct_index(lua_State *state) static int meta_struct_newindex(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "write"); - const struct_field_info *field = find_field(state, 2, "write"); + auto field = (struct_field_info*)find_field(state, 2, "write"); write_field(state, field, ptr + field->offset, 3); return 0; } +static int meta_primitive_index(lua_State *state) +{ + uint8_t *ptr = get_object_addr(state, 1, 2, "read"); + auto type = (type_identity*)find_field(state, 2, "read"); + if (!type) + return get_metafield(state); + return type->lua_read(state, 2, ptr); +} + +static int meta_primitive_newindex(lua_State *state) +{ + uint8_t *ptr = get_object_addr(state, 1, 2, "write"); + auto type = (type_identity*)find_field(state, 2, "write"); + type->lua_write(state, 2, ptr, 3); + return 0; +} + +static int meta_container_len(lua_State *state) +{ + uint8_t *ptr = get_object_addr(state, 1, 0, "get length"); + auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + int len = id->lua_item_count(state, ptr); + lua_pushinteger(state, len); + return 1; +} + +static int lookup_container_field(lua_State *state, int field, const char *mode) +{ + if (lua_type(state, field) == LUA_TNUMBER) + return field; + + lookup_field(state, field, mode); + return -1; +} + +static int check_container_index(lua_State *state, container_identity *container, void *ptr, + int fidx, int iidx, const char *mode) +{ + if (!lua_isnumber(state, iidx)) + field_error(state, fidx, "invalid index", mode); + + int idx = lua_tointeger(state, iidx); + int len = container->lua_item_count(state, ptr); + if (idx < 0 || idx >= len) + field_error(state, fidx, "index out of bounds", mode); + + return idx; +} + +static int meta_container_index(lua_State *state) +{ + uint8_t *ptr = get_object_addr(state, 1, 2, "read"); + int iidx = lookup_container_field(state, 2, "read"); + if (lua_isuserdata(state, iidx)) + { + lua_pop(state, 1); + return get_metafield(state); + } + + auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + int idx = check_container_index(state, id, ptr, 2, iidx, "read"); + return id->lua_item_read(state, 2, ptr, idx); +} + +static int meta_container_newindex(lua_State *state) +{ + uint8_t *ptr = get_object_addr(state, 1, 2, "write"); + int iidx = lookup_container_field(state, 2, "write"); + auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + int idx = check_container_index(state, id, ptr, 2, iidx, "write"); + id->lua_item_write(state, 2, ptr, idx, 3); + return 0; +} + static int meta_global_index(lua_State *state) { - const struct_field_info *field = find_field(state, 2, "read"); + auto field = (struct_field_info*)find_field(state, 2, "read"); if (!field) return get_metafield(state); void *ptr = *(void**)field->offset; @@ -517,7 +681,7 @@ static int meta_global_index(lua_State *state) static int meta_global_newindex(lua_State *state) { - const struct_field_info *field = find_field(state, 2, "write"); + auto field = (struct_field_info*)find_field(state, 2, "write"); void *ptr = *(void**)field->offset; if (!ptr) field_error(state, 2, "global address not known", "write"); @@ -527,16 +691,16 @@ static int meta_global_newindex(lua_State *state) static void IndexFields(lua_State *state, const struct_field_info *fields) { - int base = lua_gettop(state); - lua_newtable(state); // read - lua_newtable(state); // write + // stack: read write - for (; fields->mode != struct_field_info::END; ++fields) + int base = lua_gettop(state) - 2; + + for (; fields; ++fields) { switch (fields->mode) { case struct_field_info::END: - break; + return; case struct_field_info::PRIMITIVE: case struct_field_info::STATIC_STRING: @@ -558,7 +722,13 @@ static void IndexFields(lua_State *state, const struct_field_info *fields) } } -static void SetPtrMethods(lua_State *state, int meta_idx, void *node) +static void EnableMetaField(lua_State *state, int ftable_idx, const char *name, void *id = NULL) +{ + lua_pushlightuserdata(state, id); + lua_setfield(state, ftable_idx, name); +} + +static void SetPtrMethods(lua_State *state, int meta_idx, int read_idx) { lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_COMPARE_NAME); lua_setfield(state, meta_idx, "__eq"); @@ -568,12 +738,7 @@ static void SetPtrMethods(lua_State *state, int meta_idx, void *node) lua_pushcclosure(state, meta_ptr_tostring, 2); lua_setfield(state, meta_idx, "__tostring"); - // type field - lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPEID_TABLE_NAME); - lua_pushlightuserdata(state, node); - lua_rawget(state, -2); - lua_setfield(state, meta_idx, "_type"); - lua_pop(state, 1); + EnableMetaField(state, read_idx, "_type"); } static void SetStructMethod(lua_State *state, int meta_idx, int ftable_idx, @@ -586,62 +751,199 @@ static void SetStructMethod(lua_State *state, int meta_idx, int ftable_idx, lua_setfield(state, meta_idx, name); } +static void MakeMetatable(lua_State *state, type_identity *type) +{ + int base = lua_gettop(state); + lua_newtable(state); // metatable + + lua_pushstring(state, type->getFullName().c_str()); + lua_setfield(state, base+1, "__metatable"); + + lua_pushlightuserdata(state, type); + lua_setfield(state, base+1, "_identity"); + + LookupInTable(state, type, DFHACK_TYPEID_TABLE_NAME); + if (lua_isnil(state, -1)) + { + lua_pop(state, 1); + lua_getfield(state, base+1, "__metatable"); + } + lua_setfield(state, base+1, "_type"); + + lua_newtable(state); // read + lua_newtable(state); // write +} + static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct, lua_CFunction reader, lua_CFunction writer) { int base = lua_gettop(state); - lua_newtable(state); // metatable - IndexFields(state, pstruct->getFields()); // read, write + MakeMetatable(state, pstruct); // meta, read, write - lua_pushstring(state, pstruct->getName()); - lua_setfield(state, base+1, "__metatable"); + for (struct_identity *p = pstruct; p; p = p->getParent()) + { + IndexFields(state, p->getFields()); + } SetStructMethod(state, base+1, base+2, reader, "__index"); SetStructMethod(state, base+1, base+3, writer, "__newindex"); - // Custom fields + // returns: [metatable readfields writefields]; +} - lua_pushlightuserdata(state, pstruct); - lua_setfield(state, base+1, "_identity"); +static void MakePrimitiveMetatable(lua_State *state, type_identity *type) +{ + int base = lua_gettop(state); - // returns: [metatable readfields writefields]; + MakeMetatable(state, type); + SetPtrMethods(state, base+1, base+2); + + EnableMetaField(state, base+2, "value", type); + EnableMetaField(state, base+3, "value", type); + + SetStructMethod(state, base+1, base+2, meta_primitive_index, "__index"); + SetStructMethod(state, base+1, base+3, meta_primitive_newindex, "__newindex"); } -static void EnableMetaField(lua_State *state, int ftable_idx, const char *name) +static void SetContainerMethod(lua_State *state, int meta_idx, int ftable_idx, + lua_CFunction function, const char *name, + type_identity *container, type_identity *item, int count) { - lua_pushlightuserdata(state, NULL); - lua_setfield(state, ftable_idx, name); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME); + lua_pushvalue(state, meta_idx); + lua_pushvalue(state, ftable_idx); + + lua_pushlightuserdata(state, container); + lua_pushlightuserdata(state, item); + if (count < 0) + lua_pushnil(state); + else + lua_pushinteger(state, count); + + lua_pushcclosure(state, function, 6); + lua_setfield(state, meta_idx, name); } -static void BuildTypeMetatable(lua_State *state, type_identity *type) +static void AttachEnumKeys(lua_State *state, int base, type_identity *ienum) +{ + LookupInTable(state, ienum, DFHACK_ENUM_TABLE_NAME); + + if (!lua_isnil(state, -1)) + { + lua_newtable(state); + lua_swap(state); + lua_setfield(state, -2, "__index"); + lua_dup(state); + lua_setmetatable(state, base+2); + lua_setmetatable(state, base+3); + } + else + lua_pop(state, 1); +} + +static void MakeContainerMetatable(lua_State *state, container_identity *type, + type_identity *item, int count, type_identity *ienum) { int base = lua_gettop(state); - switch (type->type()) + MakeMetatable(state, type); + SetPtrMethods(state, base+1, base+2); + + lua_pushstring(state, type->getFullName(item).c_str()); + lua_dup(state); + lua_setfield(state, base+1, "__metatable"); + lua_setfield(state, base+1, "_type"); + + lua_pushlightuserdata(state, item); + lua_setfield(state, base+1, "_field_identity"); + + if (count >= 0) { - case IDTYPE_GLOBAL: - assert(false); - - case IDTYPE_STRUCT: - case IDTYPE_CLASS: - MakeFieldMetatable(state, (struct_identity*)type, meta_struct_index, meta_struct_newindex); - SetPtrMethods(state, base+1, type); - EnableMetaField(state, base+2, "_type"); - lua_pop(state, 2); - return; + lua_pushinteger(state, count); + lua_setfield(state, base+1, "_count"); + } - case IDTYPE_PRIMITIVE: - case IDTYPE_ENUM: - case IDTYPE_POINTER: - luaL_error(state, "primitive not implemented"); + SetContainerMethod(state, base+1, base+2, meta_container_len, "__len", type, item, count); + SetContainerMethod(state, base+1, base+2, meta_container_index, "__index", type, item, count); + SetContainerMethod(state, base+1, base+3, meta_container_newindex, "__newindex", type, item, count); + + AttachEnumKeys(state, base, ienum); +} + +void type_identity::build_metatable(lua_State *state) +{ + MakePrimitiveMetatable(state, this); +} + +void container_identity::build_metatable(lua_State *state) +{ + MakeContainerMetatable(state, this, getItemType(), -1, getIndexEnumType()); +} + +void pointer_identity::build_metatable(lua_State *state) +{ + int base = lua_gettop(state); + + primitive_identity::build_metatable(state); + + EnableMetaField(state, base+2, "target", this); + EnableMetaField(state, base+3, "target", this); +} + +void struct_identity::build_metatable(lua_State *state) +{ + int base = lua_gettop(state); + MakeFieldMetatable(state, this, meta_struct_index, meta_struct_newindex); + SetPtrMethods(state, base+1, base+2); +} + +void global_identity::build_metatable(lua_State *state) +{ + MakeFieldMetatable(state, this, meta_global_index, meta_global_newindex); +} + +static void BuildTypeMetatable(lua_State *state, type_identity *type) +{ + type->build_metatable(state); + + lua_pop(state, 2); + + SaveTypeInfo(state, type); +} - case IDTYPE_BITFIELD: - luaL_error(state, "bitfield not implemented"); +static void GetAdHocMetatable(lua_State *state, const struct_field_info *field) +{ + lua_pushlightuserdata(state, (void*)field); + + if (!LookupTypeInfo(state, true)) + { + switch (field->mode) + { + case struct_field_info::CONTAINER: + { + auto ctype = (container_identity*)field->type; + MakeContainerMetatable(state, ctype, ctype->getItemType(), -1, field->eid); + break; + } + + case struct_field_info::STATIC_ARRAY: + MakeContainerMetatable(state, &df::buffer_container_identity::base_instance, + field->type, field->count, field->eid); + break; + + case struct_field_info::STL_VECTOR_PTR: + MakeContainerMetatable(state, &df::identity_traits >::identity, + field->type, -1, field->eid); + break; + + default: + luaL_error(state, "Invalid ad-hoc field: %d", field->mode); + } - case IDTYPE_CONTAINER: - case IDTYPE_STL_PTR_VECTOR: - luaL_error(state, "container not implemented"); + lua_pop(state, 2); + + SaveTypeInfo(state, (void*)field); } } @@ -650,6 +952,7 @@ static void RenderTypeChildren(lua_State *state, const std::vectorgetName()); + std::string name = node->getFullName(); lua_newtable(state); if (!lua_checkstack(state, 20)) @@ -691,7 +994,7 @@ static void RenderType(lua_State *state, compound_identity *node) lua_setfield(state, base, "_last_item"); } - SaveTypeInfo(state, node); + SaveInTable(state, node, DFHACK_ENUM_TABLE_NAME); } break; @@ -705,10 +1008,7 @@ static void RenderType(lua_State *state, compound_identity *node) if (node->type() == IDTYPE_GLOBAL) { - auto gid = (global_identity*)node; - - MakeFieldMetatable(state, gid, meta_global_index, meta_global_newindex); - lua_pop(state, 2); + BuildTypeMetatable(state, node); lua_dup(state); lua_setmetatable(state, base); @@ -719,22 +1019,17 @@ static void RenderType(lua_State *state, compound_identity *node) lua_getfield(state, base, "__newindex"); lua_setfield(state, -2, "__newindex"); - lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME); - lua_setfield(state, -2, "__tostring"); - - lua_pop(state, 1); - lua_remove(state, base); } else { - freeze_table(state, true, node->getName()); + freeze_table(state, true, name.c_str()); + } - lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME); - lua_setfield(state, -2, "__tostring"); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME); + lua_setfield(state, -2, "__tostring"); - lua_pop(state, 1); - } + lua_pop(state, 1); SaveInTable(state, node, DFHACK_TYPEID_TABLE_NAME); } @@ -757,6 +1052,9 @@ static void DoAttach(lua_State *state) lua_newtable(state); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_TYPEID_TABLE_NAME); + lua_newtable(state); + lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_ENUM_TABLE_NAME); + lua_pushcfunction(state, change_error); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_CHANGEERROR_NAME); diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 8562a18d6..a466ea9d8 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -52,6 +52,8 @@ namespace DFHack IDTYPE_PRIMITIVE, IDTYPE_POINTER, IDTYPE_CONTAINER, + IDTYPE_PTR_CONTAINER, + IDTYPE_BIT_CONTAINER, IDTYPE_BITFIELD, IDTYPE_ENUM, IDTYPE_STRUCT, @@ -85,6 +87,11 @@ namespace DFHack virtual int lua_read(lua_State *state, int fname_idx, void *ptr) = 0; virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) = 0; + + virtual std::string getFullName() = 0; + virtual void build_metatable(lua_State *state); + + virtual bool isContainer() { return false; } }; class DFHACK_EXPORT constructed_identity : public type_identity { @@ -124,6 +131,8 @@ namespace DFHack public: const char *getName() { return dfhack_name; } + virtual std::string getFullName(); + compound_identity *getScopeParent() { return scope_parent; } const std::vector &getScopeChildren() { return scope_children; } static const std::vector &getTopScope() { return top_scope; } @@ -222,6 +231,8 @@ namespace DFHack const struct_field_info *getFields() { return fields; } bool is_subclass(struct_identity *subtype); + + virtual void build_metatable(lua_State *state); }; class DFHACK_EXPORT global_identity : public struct_identity { @@ -230,6 +241,8 @@ namespace DFHack : struct_identity(0,NULL,NULL,"global",NULL,fields) {} virtual identity_type type() { return IDTYPE_GLOBAL; } + + virtual void build_metatable(lua_State *state); }; #ifdef _MSC_VER diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index 28426c58a..ac5903beb 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -55,6 +55,9 @@ namespace DFHack type_identity *getTarget() { return target; } + std::string getFullName(); + virtual void build_metatable(lua_State *state); + static int lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target); static void lua_write(lua_State *state, int fname_idx, void *ptr, type_identity *target, int val_index); @@ -72,7 +75,57 @@ namespace DFHack virtual identity_type type() { return IDTYPE_CONTAINER; } + std::string getFullName() { return getFullName(item); } + + virtual void build_metatable(lua_State *state); + virtual bool isContainer() { return true; } + type_identity *getItemType() { return item; } + type_identity *getIndexEnumType() { return ienum; } + + virtual std::string getFullName(type_identity *item); + + int lua_item_count(lua_State *state, void *ptr); + + virtual int lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); + virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); + + protected: + virtual int item_count(void *ptr) = 0; + virtual void *item_pointer(void *ptr, int idx) = 0; + }; + + class DFHACK_EXPORT ptr_container_identity : public container_identity { + public: + ptr_container_identity(size_t size, TAllocateFn alloc, + type_identity *item, enum_identity *ienum = NULL) + : container_identity(size, alloc, item, ienum) {}; + + virtual identity_type type() { return IDTYPE_PTR_CONTAINER; } + + std::string getFullName(type_identity *item); + + virtual int lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); + virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); + }; + + class DFHACK_EXPORT bit_container_identity : public container_identity { + public: + bit_container_identity(size_t size, TAllocateFn alloc, enum_identity *ienum = NULL) + : container_identity(size, alloc, NULL, ienum) {}; + + virtual identity_type type() { return IDTYPE_BIT_CONTAINER; } + + std::string getFullName(type_identity *item); + + virtual int lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); + virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); + + protected: + virtual void *item_pointer(void *, int) { return NULL; } + + virtual bool get_item(void *ptr, int idx) = 0; + virtual void set_item(void *ptr, int idx, bool val) = 0; }; } @@ -81,10 +134,17 @@ namespace df using DFHack::primitive_identity; using DFHack::pointer_identity; using DFHack::container_identity; + using DFHack::ptr_container_identity; + using DFHack::bit_container_identity; class number_identity_base : public primitive_identity { + const char *name; + public: - number_identity_base(size_t size) : primitive_identity(size) {}; + number_identity_base(size_t size, const char *name) + : primitive_identity(size), name(name) {}; + + std::string getFullName() { return name; } virtual int lua_read(lua_State *state, int fname_idx, void *ptr); virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); @@ -97,7 +157,7 @@ namespace df template class number_identity : public number_identity_base { public: - number_identity() : number_identity_base(sizeof(T)) {} + number_identity(const char *name) : number_identity_base(sizeof(T), name) {} protected: virtual double read(void *ptr) { return double(*(T*)ptr); } virtual void write(void *ptr, double val) { *(T*)ptr = T(val); } @@ -107,6 +167,8 @@ namespace df public: bool_identity() : primitive_identity(sizeof(bool)) {}; + std::string getFullName() { return "bool"; } + virtual int lua_read(lua_State *state, int fname_idx, void *ptr); virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); }; @@ -115,17 +177,118 @@ namespace df public: stl_string_identity() : primitive_identity(sizeof(std::string)) {}; + std::string getFullName() { return "string"; } + virtual int lua_read(lua_State *state, int fname_idx, void *ptr); virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); }; - class stl_ptr_vector_identity : public container_identity { + class stl_ptr_vector_identity : public ptr_container_identity { public: stl_ptr_vector_identity(type_identity *item = NULL, enum_identity *ienum = NULL) - : container_identity(sizeof(std::vector),allocator_fn >,item, ienum) + : ptr_container_identity(sizeof(std::vector),allocator_fn >,item, ienum) {}; + std::string getFullName(type_identity *item) { + return "vector" + ptr_container_identity::getFullName(item); + } + virtual DFHack::identity_type type() { return DFHack::IDTYPE_STL_PTR_VECTOR; } + + protected: + virtual int item_count(void *ptr) { + return ((std::vector*)ptr)->size(); + }; + virtual void *item_pointer(void *ptr, int idx) { + return &(*(std::vector*)ptr)[idx]; + } + }; + + class buffer_container_identity : public container_identity { + int size; + + public: + buffer_container_identity() + : container_identity(0, NULL, NULL, NULL), size(0) + {} + + buffer_container_identity(int size, type_identity *item, enum_identity *ienum = NULL) + : container_identity(item->byte_size()*size, NULL, item, ienum), size(size) + {} + + std::string getFullName(type_identity *item); + + static buffer_container_identity base_instance; + + virtual int lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); + virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); + + protected: + virtual int item_count(void *ptr) { return size; } + virtual void *item_pointer(void *ptr, int idx) { return NULL; } + }; + + template + class stl_container_identity : public container_identity { + const char *name; + + public: + stl_container_identity(const char *name, type_identity *item, enum_identity *ienum = NULL) + : container_identity(sizeof(T), &allocator_fn, item, ienum), name(name) + {} + + std::string getFullName(type_identity *item) { + return name + container_identity::getFullName(item); + } + + protected: + virtual int item_count(void *ptr) { return ((T*)ptr)->size(); } + virtual void *item_pointer(void *ptr, int idx) { return &(*(T*)ptr)[idx]; } + }; + + template + class bit_array_identity : public bit_container_identity { + public: + typedef BitArray container; + + bit_array_identity(enum_identity *ienum = NULL) + : bit_container_identity(sizeof(container), &allocator_fn, ienum) + {} + + std::string getFullName(type_identity *item) { + return "BitArray" + bit_container_identity::getFullName(item); + } + + protected: + virtual int item_count(void *ptr) { return ((container*)ptr)->size * 8; } + virtual bool get_item(void *ptr, int idx) { + return ((container*)ptr)->is_set(T(idx)); + } + virtual void set_item(void *ptr, int idx, bool val) { + ((container*)ptr)->set(T(idx), val); + } + }; + + class stl_bit_vector_identity : public bit_container_identity { + public: + typedef std::vector container; + + stl_bit_vector_identity(enum_identity *ienum = NULL) + : bit_container_identity(sizeof(container), &allocator_fn, ienum) + {} + + std::string getFullName(type_identity *item) { + return "vector" + bit_container_identity::getFullName(item); + } + + protected: + virtual int item_count(void *ptr) { return ((container*)ptr)->size(); } + virtual bool get_item(void *ptr, int idx) { + return (*(container*)ptr)[idx]; + } + virtual void set_item(void *ptr, int idx, bool val) { + (*(container*)ptr)[idx] = val; + } }; #define NUMBER_IDENTITY_TRAITS(type) \ @@ -165,6 +328,11 @@ namespace df static stl_ptr_vector_identity *get() { return &identity; } }; + template<> struct identity_traits > { + static stl_bit_vector_identity identity; + static stl_bit_vector_identity *get() { return &identity; } + }; + #undef NUMBER_IDENTITY_TRAITS // Container declarations @@ -194,7 +362,7 @@ namespace df }; template struct identity_traits > { - static container_identity *get(); + static bit_container_identity *get(); }; template struct identity_traits > { @@ -216,17 +384,14 @@ namespace df template container_identity *identity_traits::get() { - typedef T container[sz]; - static container_identity identity(sizeof(container), NULL, - identity_traits::get()); + static buffer_container_identity identity(sz, identity_traits::get()); return &identity; } template container_identity *identity_traits >::get() { typedef std::vector container; - static container_identity identity(sizeof(container), &allocator_fn, - identity_traits::get()); + static stl_container_identity identity("vector", identity_traits::get()); return &identity; } @@ -239,26 +404,22 @@ namespace df template container_identity *identity_traits >::get() { typedef std::deque container; - static container_identity identity(sizeof(container), &allocator_fn, - identity_traits::get()); + static stl_container_identity identity("deque", identity_traits::get()); return &identity; } template - container_identity *identity_traits >::get() { - typedef BitArray container; + bit_container_identity *identity_traits >::get() { static type_identity *eid = identity_traits::get(); static enum_identity *reid = eid->type() == DFHack::IDTYPE_ENUM ? (enum_identity*)eid : NULL; - static container_identity identity(sizeof(container), &allocator_fn, - &identity_traits::identity, reid); + static bit_array_identity identity(reid); return &identity; } template container_identity *identity_traits >::get() { typedef DfArray container; - static container_identity identity(sizeof(container), &allocator_fn, - identity_traits::get()); + static stl_container_identity identity("DfArray", identity_traits::get()); return &identity; } } From ad10303cecd1e60a2e50b85a5f6f08656ff1bbf9 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Wed, 21 Mar 2012 20:04:37 +0400 Subject: [PATCH 09/25] Implement bitfields and add a _kind metadata field to types and objects. --- library/LuaWrapper.cpp | 399 ++++++++++++++++++++++++++++++++++--- library/include/DataDefs.h | 37 +++- 2 files changed, 407 insertions(+), 29 deletions(-) diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index d4755bc0f..a7d830474 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -48,32 +48,81 @@ static luaL_Reg no_functions[] = { { NULL, NULL } }; inline void lua_dup(lua_State *state) { lua_pushvalue(state, -1); } inline void lua_swap(lua_State *state) { lua_insert(state, -2); } +/* + * Registry name: hash of type metatables <-> type identities. + */ #define DFHACK_TYPETABLE_NAME "DFHack::DFTypes" + +/* + * Registry name: hash of type identity -> node in df.etc... + */ #define DFHACK_TYPEID_TABLE_NAME "DFHack::DFTypeIds" + +/* + * Registry name: hash of enum/bitfield identity -> index lookup table + */ #define DFHACK_ENUM_TABLE_NAME "DFHack::DFEnums" + +// Function registry names #define DFHACK_CHANGEERROR_NAME "DFHack::ChangeError" #define DFHACK_COMPARE_NAME "DFHack::ComparePtrs" #define DFHACK_TYPE_TOSTRING_NAME "DFHack::TypeToString" +/* + * Upvalue: contents of DFHACK_TYPETABLE_NAME + */ #define UPVAL_TYPETABLE lua_upvalueindex(1) + +/* + * Expected metatable of the current object. + */ #define UPVAL_METATABLE lua_upvalueindex(2) + +/* + * Table mapping field names to indices or data structure pointers. + * Enum index table is linked into here via getmetatable($).__index. + * Fields that are actually in UPVAL_METATABLE are marked with NULL light udata. + */ #define UPVAL_FIELDTABLE lua_upvalueindex(3) +/* + * Only for containers: light udata with container identity. + */ #define UPVAL_CONTAINER_ID lua_upvalueindex(4) + +/* + * Only for containers: light udata with item identity. + */ #define UPVAL_ITEM_ID lua_upvalueindex(5) + +/* + * Only for containers: if not nil, overrides the item count. + */ #define UPVAL_ITEM_COUNT lua_upvalueindex(6) namespace { + /** + * Object references are represented as userdata instances + * with an appropriate metatable; the payload of userdata is + * this structure: + */ struct DFRefHeader { void *ptr; }; + /* + * The system might be extended to carry some simple + * objects inline inside the reference buffer. + */ inline bool is_self_contained(DFRefHeader *ptr) { void **pp = &ptr->ptr; return **(void****)pp == (pp + 1); } } +/** + * Report an error while accessing a field (index = field name). + */ static void field_error(lua_State *state, int index, const char *err, const char *mode) { lua_getfield(state, UPVAL_METATABLE, "__metatable"); @@ -83,6 +132,9 @@ static void field_error(lua_State *state, int index, const char *err, const char mode, (cname ? cname : "?"), (fname ? fname : "?"), err); } +/* + * If is_method is true, these use UPVAL_TYPETABLE to save a hash lookup. + */ static void push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method = true); static void *get_object_internal(lua_State *state, type_identity *type, int val_index, bool in_method = true); @@ -96,7 +148,9 @@ void *DFHack::GetDFObject(lua_State *state, type_identity *type, int val_index) return get_object_internal(state, type, val_index, false); } -/* Primitive identity methods */ +/************************************** + * Identity object read/write methods * + **************************************/ int constructed_identity::lua_read(lua_State *state, int fname_idx, void *ptr) { @@ -274,6 +328,9 @@ static int change_error(lua_State *state) luaL_error(state, "Attempt to change a read-only table.\n"); } +/** + * Wrap a table so that it can't be modified. + */ static void freeze_table(lua_State *state, bool leave_metatable = false, const char *name = NULL) { // rv = {}; setmetatable(rv, { __index = in, __newindex = change_error, __metatable = name }) @@ -297,6 +354,10 @@ static void freeze_table(lua_State *state, bool leave_metatable = false, const c lua_pop(state, 1); } +/** + * Look up the key on the stack in DFHACK_TYPETABLE; + * if found, put result on the stack and return true. + */ static bool LookupTypeInfo(lua_State *state, bool in_method) { // stack: [lookup key] @@ -356,6 +417,9 @@ static void SaveTypeInfo(lua_State *state, void *node) static void BuildTypeMetatable(lua_State *state, type_identity *type); +/** + * Push the pointer as DF object ref using metatable on the stack. + */ static void push_object_ref(lua_State *state, void *ptr) { // stack: [metatable] @@ -367,8 +431,15 @@ static void push_object_ref(lua_State *state, void *ptr) // stack: [userdata] } +/** + * Push the pointer using given identity. + */ static void push_object_internal(lua_State *state, type_identity *type, void *ptr, bool in_method) { + /* + * If NULL pointer or no type, push something simple + */ + if (!ptr || !type) { if (!ptr) @@ -379,7 +450,10 @@ static void push_object_internal(lua_State *state, type_identity *type, void *pt return; } - // Resolve actual class using vtable + /* + * Resolve actual class using vtable + */ + if (type->type() == IDTYPE_CLASS) { virtual_identity *class_vid = virtual_identity::get(virtual_ptr(ptr)); @@ -387,6 +461,10 @@ static void push_object_internal(lua_State *state, type_identity *type, void *pt type = class_vid; } + /* + * Resolve metatable by identity, and push the object + */ + lua_pushlightuserdata(state, type); // () -> type if (!LookupTypeInfo(state, in_method)) // type -> metatable? @@ -395,11 +473,20 @@ static void push_object_internal(lua_State *state, type_identity *type, void *pt push_object_ref(state, ptr); // metatable -> userdata } +/** + * Verify that the value matches the identity, and return ptr if so. + */ static void *get_object_internal(lua_State *state, type_identity *type, int val_index, bool in_method) { + /* + * Non-userdata results in NULL; nil for NULL gets handled here too. + */ if (!lua_isuserdata(state, val_index)) return NULL; + /* + * Light user data is allowed with null type; otherwise bail out. + */ if (!lua_getmetatable(state, val_index)) // () -> metatable? { if (!type && lua_islightuserdata(state, val_index)) @@ -408,7 +495,10 @@ static void *get_object_internal(lua_State *state, type_identity *type, int val_ return NULL; } - // Verify known metatable and correct type + /* + * Verify that the metatable is known, and refers to the correct type. + * Here doing reverse lookup of identity by metatable. + */ if (!LookupTypeInfo(state, in_method)) // metatable -> type? return NULL; @@ -420,11 +510,18 @@ static void *get_object_internal(lua_State *state, type_identity *type, int val_ if (!ok) return NULL; + /* + * Finally decode the reference. + */ auto ref = (DFRefHeader*)lua_touserdata(state, val_index); return ref->ptr; } - +/** + * Metamethod: compare two DF object references. + * + * Equal if same pointer and same metatable. + */ static int meta_ptr_compare(lua_State *state) { if (!lua_isuserdata(state, 1) || !lua_isuserdata(state, 2) || @@ -447,6 +544,9 @@ static int meta_ptr_compare(lua_State *state) return 1; } +/** + * Resolve the field name in UPVAL_FIELDTABLE, die if not found. + */ static void lookup_field(lua_State *state, int index, const char *mode) { lua_pushvalue(state, index); @@ -465,6 +565,10 @@ static void *find_field(lua_State *state, int index, const char *mode) return p; } +/** + * Verify that the object is a DF ref with UPVAL_METATABLE. + * If everything ok, extract the address. + */ static uint8_t *get_object_addr(lua_State *state, int obj, int field, const char *mode) { if (!lua_isuserdata(state, obj) || @@ -548,6 +652,9 @@ static void write_field(lua_State *state, const struct_field_info *field, void * } } +/** + * Metamethod: represent a type node as string. + */ static int meta_type_tostring(lua_State *state) { if (!lua_getmetatable(state, 1)) @@ -560,6 +667,9 @@ static int meta_type_tostring(lua_State *state) return 1; } +/** + * Metamethod: represent a DF object reference as string. + */ static int meta_ptr_tostring(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 0, "access"); @@ -571,12 +681,16 @@ static int meta_ptr_tostring(lua_State *state) return 1; } +// Resolve the field in the metatable and return static int get_metafield(lua_State *state) { lua_rawget(state, UPVAL_METATABLE); return 1; } +/** + * Metamethod: __index for structures. + */ static int meta_struct_index(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "read"); @@ -586,6 +700,9 @@ static int meta_struct_index(lua_State *state) return read_field(state, field, ptr + field->offset); } +/** + * Metamethod: __newindex for structures. + */ static int meta_struct_newindex(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "write"); @@ -594,6 +711,10 @@ static int meta_struct_newindex(lua_State *state) return 0; } +/** + * Metamethod: __index for primitives, i.e. simple object references. + * Fields point to identity, or NULL for metafields. + */ static int meta_primitive_index(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "read"); @@ -603,6 +724,9 @@ static int meta_primitive_index(lua_State *state) return type->lua_read(state, 2, ptr); } +/** + * Metamethod: __newindex for primitives. + */ static int meta_primitive_newindex(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "write"); @@ -611,6 +735,9 @@ static int meta_primitive_newindex(lua_State *state) return 0; } +/** + * Metamethod: __len for containers. + */ static int meta_container_len(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 0, "get length"); @@ -620,54 +747,155 @@ static int meta_container_len(lua_State *state) return 1; } +/** + * Field lookup for containers: + * + * - Numbers are indices and handled directly. + * - NULL userdata are metafields; push and exit; + */ static int lookup_container_field(lua_State *state, int field, const char *mode) { if (lua_type(state, field) == LUA_TNUMBER) return field; lookup_field(state, field, mode); + + if (lua_isuserdata(state, -1) && !lua_touserdata(state, -1)) + { + lua_pop(state, 1); + get_metafield(state); + return 0; + } + return -1; } -static int check_container_index(lua_State *state, container_identity *container, void *ptr, +/** + * Index verification: number and in range. + */ +static int check_container_index(lua_State *state, int len, int fidx, int iidx, const char *mode) { if (!lua_isnumber(state, iidx)) field_error(state, fidx, "invalid index", mode); int idx = lua_tointeger(state, iidx); - int len = container->lua_item_count(state, ptr); if (idx < 0 || idx >= len) field_error(state, fidx, "index out of bounds", mode); return idx; } +/** + * Metamethod: __index for containers. + */ static int meta_container_index(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "read"); int iidx = lookup_container_field(state, 2, "read"); - if (lua_isuserdata(state, iidx)) - { - lua_pop(state, 1); - return get_metafield(state); - } + if (!iidx) + return 1; auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); - int idx = check_container_index(state, id, ptr, 2, iidx, "read"); + int len = id->lua_item_count(state, ptr); + int idx = check_container_index(state, len, 2, iidx, "read"); return id->lua_item_read(state, 2, ptr, idx); } +/** + * Metamethod: __index for containers. + */ static int meta_container_newindex(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "write"); int iidx = lookup_container_field(state, 2, "write"); + auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); - int idx = check_container_index(state, id, ptr, 2, iidx, "write"); + int len = id->lua_item_count(state, ptr); + int idx = check_container_index(state, len, 2, iidx, "write"); id->lua_item_write(state, 2, ptr, idx, 3); return 0; } +/** + * Metamethod: __len for bitfields. + */ +static int meta_bitfield_len(lua_State *state) +{ + uint8_t *ptr = get_object_addr(state, 1, 0, "get size"); + auto id = (bitfield_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + lua_pushinteger(state, id->getNumBits()); + return 1; +} + +/** + * Metamethod: __index for bitfields. + */ +static int meta_bitfield_index(lua_State *state) +{ + uint8_t *ptr = get_object_addr(state, 1, 2, "read"); + int iidx = lookup_container_field(state, 2, "read"); + if (!iidx) + return 1; + + auto id = (bitfield_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + + // whole + if (lua_isuserdata(state, iidx) && lua_touserdata(state, iidx) == id) + { + lua_Integer intv = 0; + memcpy(&intv, ptr, std::min(sizeof(intv), size_t(id->byte_size()))); + lua_pushinteger(state, intv); + return 1; + } + + int idx = check_container_index(state, id->getNumBits(), 2, iidx, "read"); + int size = id->getBits()[idx].size; + + int value = getBitfieldField(ptr, idx, size); + if (size <= 1) + lua_pushboolean(state, value != 0); + else + lua_pushinteger(state, value); + return 1; +} + +/** + * Metamethod: __newindex for bitfields. + */ +static int meta_bitfield_newindex(lua_State *state) +{ + uint8_t *ptr = get_object_addr(state, 1, 2, "write"); + int iidx = lookup_container_field(state, 2, "write"); + + auto id = (bitfield_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + + // whole + if (lua_isuserdata(state, iidx) && lua_touserdata(state, iidx) == id) + { + if (!lua_isnumber(state, 3)) + field_error(state, 2, "number expected", "write"); + + lua_Integer intv = lua_tointeger(state, 3); + memcpy(ptr, &intv, std::min(sizeof(intv), size_t(id->byte_size()))); + return 0; + } + + int idx = check_container_index(state, id->getNumBits(), 2, iidx, "write"); + int size = id->getBits()[idx].size; + + if (lua_isboolean(state, 3) || lua_isnil(state, 3)) + setBitfieldField(ptr, idx, size, lua_toboolean(state, 3)); + else if (lua_isnumber(state, 3)) + setBitfieldField(ptr, idx, size, lua_tointeger(state, 3)); + else + field_error(state, 2, "number or boolean expected", "write"); + return 0; +} + +/** + * Metamethod: __index for df.global + */ static int meta_global_index(lua_State *state) { auto field = (struct_field_info*)find_field(state, 2, "read"); @@ -679,6 +907,9 @@ static int meta_global_index(lua_State *state) return read_field(state, field, ptr); } +/** + * Metamethod: __newindex for df.global + */ static int meta_global_newindex(lua_State *state) { auto field = (struct_field_info*)find_field(state, 2, "write"); @@ -689,6 +920,9 @@ static int meta_global_newindex(lua_State *state) return 0; } +/** + * Add fields in the array to the UPVAL_FIELDTABLE candidates on the stack. + */ static void IndexFields(lua_State *state, const struct_field_info *fields) { // stack: read write @@ -722,12 +956,18 @@ static void IndexFields(lua_State *state, const struct_field_info *fields) } } +/** + * Enable a metafield by injecting an entry into a UPVAL_FIELDTABLE. + */ static void EnableMetaField(lua_State *state, int ftable_idx, const char *name, void *id = NULL) { lua_pushlightuserdata(state, id); lua_setfield(state, ftable_idx, name); } +/** + * Set metatable properties common to all actual DF object references. + */ static void SetPtrMethods(lua_State *state, int meta_idx, int read_idx) { lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_COMPARE_NAME); @@ -739,8 +979,12 @@ static void SetPtrMethods(lua_State *state, int meta_idx, int read_idx) lua_setfield(state, meta_idx, "__tostring"); EnableMetaField(state, read_idx, "_type"); + EnableMetaField(state, read_idx, "_kind"); } +/** + * Add a struct-style (3 upvalues) metamethod to the metatable. + */ static void SetStructMethod(lua_State *state, int meta_idx, int ftable_idx, lua_CFunction function, const char *name) { @@ -751,7 +995,10 @@ static void SetStructMethod(lua_State *state, int meta_idx, int ftable_idx, lua_setfield(state, meta_idx, name); } -static void MakeMetatable(lua_State *state, type_identity *type) +/** + * Make a metatable with most common fields, and two empty tables for UPVAL_FIELDTABLE. + */ +static void MakeMetatable(lua_State *state, type_identity *type, const char *kind) { int base = lua_gettop(state); lua_newtable(state); // metatable @@ -770,16 +1017,22 @@ static void MakeMetatable(lua_State *state, type_identity *type) } lua_setfield(state, base+1, "_type"); + lua_pushstring(state, kind); + lua_setfield(state, base+1, "_kind"); + lua_newtable(state); // read lua_newtable(state); // write } +/** + * Make a struct-style object metatable. + */ static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct, lua_CFunction reader, lua_CFunction writer) { int base = lua_gettop(state); - MakeMetatable(state, pstruct); // meta, read, write + MakeMetatable(state, pstruct, "struct"); // meta, read, write for (struct_identity *p = pstruct; p; p = p->getParent()) { @@ -792,11 +1045,14 @@ static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct, // returns: [metatable readfields writefields]; } +/** + * Make a primitive-style metatable + */ static void MakePrimitiveMetatable(lua_State *state, type_identity *type) { int base = lua_gettop(state); - MakeMetatable(state, type); + MakeMetatable(state, type, "primitive"); SetPtrMethods(state, base+1, base+2); EnableMetaField(state, base+2, "value", type); @@ -806,6 +1062,9 @@ static void MakePrimitiveMetatable(lua_State *state, type_identity *type) SetStructMethod(state, base+1, base+3, meta_primitive_newindex, "__newindex"); } +/** + * Add a 6 upvalue metamethod to the metatable. + */ static void SetContainerMethod(lua_State *state, int meta_idx, int ftable_idx, lua_CFunction function, const char *name, type_identity *container, type_identity *item, int count) @@ -825,6 +1084,10 @@ static void SetContainerMethod(lua_State *state, int meta_idx, int ftable_idx, lua_setfield(state, meta_idx, name); } +/** + * If ienum refers to a valid enum, attach its keys to UPVAL_FIELDTABLE, + * and the enum itself to the _enum metafield. + */ static void AttachEnumKeys(lua_State *state, int base, type_identity *ienum) { LookupInTable(state, ienum, DFHACK_ENUM_TABLE_NAME); @@ -840,14 +1103,22 @@ static void AttachEnumKeys(lua_State *state, int base, type_identity *ienum) } else lua_pop(state, 1); + + LookupInTable(state, ienum, DFHACK_TYPEID_TABLE_NAME); + lua_setfield(state, base+1, "_enum"); + + EnableMetaField(state, base+2, "_enum"); } +/** + * Make a container-style object metatable. + */ static void MakeContainerMetatable(lua_State *state, container_identity *type, type_identity *item, int count, type_identity *ienum) { int base = lua_gettop(state); - MakeMetatable(state, type); + MakeMetatable(state, type, "container"); SetPtrMethods(state, base+1, base+2); lua_pushstring(state, type->getFullName(item).c_str()); @@ -871,6 +1142,9 @@ static void MakeContainerMetatable(lua_State *state, container_identity *type, AttachEnumKeys(state, base, ienum); } +/* + * Metatable construction identity methods. + */ void type_identity::build_metatable(lua_State *state) { MakePrimitiveMetatable(state, this); @@ -891,6 +1165,23 @@ void pointer_identity::build_metatable(lua_State *state) EnableMetaField(state, base+3, "target", this); } +void bitfield_identity::build_metatable(lua_State *state) +{ + int base = lua_gettop(state); + + MakeMetatable(state, this, "bitfield"); + SetPtrMethods(state, base+1, base+2); + + SetContainerMethod(state, base+1, base+2, meta_bitfield_len, "__len", this, NULL, -1); + SetContainerMethod(state, base+1, base+2, meta_bitfield_index, "__index", this, NULL, -1); + SetContainerMethod(state, base+1, base+3, meta_bitfield_newindex, "__newindex", this, NULL, -1); + + AttachEnumKeys(state, base, this); + + EnableMetaField(state, base+2, "whole", this); + EnableMetaField(state, base+3, "whole", this); +} + void struct_identity::build_metatable(lua_State *state) { int base = lua_gettop(state); @@ -912,6 +1203,10 @@ static void BuildTypeMetatable(lua_State *state, type_identity *type) SaveTypeInfo(state, type); } +/** + * Construct a metatable for an object type folded into the field descriptor. + * This is done to reduce compile-time symbol table bloat due to templates. + */ static void GetAdHocMetatable(lua_State *state, const struct_field_info *field) { lua_pushlightuserdata(state, (void*)field); @@ -947,8 +1242,23 @@ static void GetAdHocMetatable(lua_State *state, const struct_field_info *field) } } +/* + * Recursive walk of scopes to construct the df... tree. + */ + static void RenderTypeChildren(lua_State *state, const std::vector &children); +static void AssociateId(lua_State *state, int table, int val, const char *name) +{ + lua_pushinteger(state, val); + lua_pushstring(state, name); + lua_dup(state); + lua_pushinteger(state, val); + + lua_rawset(state, table); + lua_rawset(state, table); +} + static void RenderType(lua_State *state, compound_identity *node) { assert(node->getName()); @@ -965,24 +1275,29 @@ static void RenderType(lua_State *state, compound_identity *node) switch (node->type()) { + case IDTYPE_STRUCT: + lua_pushstring(state, "struct-type"); + lua_setfield(state, base, "_kind"); + break; + + case IDTYPE_CLASS: + lua_pushstring(state, "class-type"); + lua_setfield(state, base, "_kind"); + break; + case IDTYPE_ENUM: { + lua_pushstring(state, "enum-type"); + lua_setfield(state, base, "_kind"); + enum_identity *eid = (enum_identity*)node; const char *const *keys = eid->getKeys(); // For enums, set mapping between keys and values for (int64_t i = eid->getFirstItem(), j = 0; i <= eid->getLastItem(); i++, j++) { - if (!keys[j]) - continue; - - lua_pushinteger(state, i); - lua_pushstring(state, keys[j]); - lua_dup(state); - lua_pushinteger(state, i); - - lua_rawset(state, base); - lua_rawset(state, base); + if (keys[j]) + AssociateId(state, base, i, keys[j]); } if (eid->getFirstItem() <= eid->getLastItem()) @@ -998,6 +1313,32 @@ static void RenderType(lua_State *state, compound_identity *node) } break; + case IDTYPE_BITFIELD: + { + lua_pushstring(state, "bitfield-type"); + lua_setfield(state, base, "_kind"); + + bitfield_identity *eid = (bitfield_identity*)node; + auto bits = eid->getBits(); + + for (int i = 0; i < eid->getNumBits(); i++) + { + if (bits[i].name) + AssociateId(state, base, i, bits[i].name); + if (bits[i].size > 1) + i += bits[i].size-1; + } + + lua_pushinteger(state, 0); + lua_setfield(state, base, "_first_item"); + + lua_pushinteger(state, eid->getNumBits()-1); + lua_setfield(state, base, "_last_item"); + + SaveInTable(state, node, DFHACK_ENUM_TABLE_NAME); + } + break; + default: break; } @@ -1081,6 +1422,10 @@ static void DoAttach(lua_State *state) lua_pop(state, 1); } +/** + * Initialize access to DF objects from the interpreter + * context, unless it has already been done. + */ void DFHack::AttachDFGlobals(lua_State *state) { if (luaL_newmetatable(state, DFHACK_TYPETABLE_NAME)) diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index a466ea9d8..9ec266fab 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -85,10 +85,11 @@ namespace DFHack virtual identity_type type() = 0; + virtual std::string getFullName() = 0; + + // For internal use in the lua wrapper virtual int lua_read(lua_State *state, int fname_idx, void *ptr) = 0; virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) = 0; - - virtual std::string getFullName() = 0; virtual void build_metatable(lua_State *state); virtual bool isContainer() { return false; } @@ -159,6 +160,8 @@ namespace DFHack int getNumBits() { return num_bits; } const bitfield_item_info *getBits() { return bits; } + + virtual void build_metatable(lua_State *state); }; class DFHACK_EXPORT enum_identity : public compound_identity { @@ -317,9 +320,21 @@ namespace DFHack void InitDataDefGlobals(Core *core); + // LUA wrapper + + /** + * Make DF objects available to the given interpreter. + */ DFHACK_EXPORT void AttachDFGlobals(lua_State *state); + /** + * Push the pointer onto the stack as a wrapped DF object of the given type. + */ DFHACK_EXPORT void PushDFObject(lua_State *state, type_identity *type, void *ptr); + + /** + * Check that the value is a wrapped DF object of the given type, and if so return the pointer. + */ DFHACK_EXPORT void *GetDFObject(lua_State *state, type_identity *type, int val_index); template @@ -620,6 +635,24 @@ namespace DFHack { flagarray_to_string(&tmp, val); return join_strings(sep, tmp); } + + // LUA wrapper + + /** + * Push the pointer onto the stack as a wrapped DF object of a specific type. + */ + template + void PushDFObject(lua_State *state, T *ptr) { + PushDFObject(state, df::identity_traits::get(), ptr); + } + + /** + * Check that the value is a wrapped DF object of the correct type, and if so return the pointer. + */ + template + T *GetDFObject(lua_State *state, int val_index) { + return GetDFObject(state, df::identity_traits::get(), val_index); + } } #define ENUM_ATTR(enum,attr,val) (df::enum_traits::attrs(val).attr) From 1311f4fbf99ef66b6f2ad10e7eb43e7cbe6eb876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Wed, 21 Mar 2012 17:55:49 +0100 Subject: [PATCH 10/25] bump version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d39d940e2..1199f2897 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,7 @@ set(DF_VERSION_MINOR "34") set(DF_VERSION_PATCH "05") set(DF_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}") -set(DFHACK_RELEASE "1e") +set(DFHACK_RELEASE "1f") set(DFHACK_VERSION "${DF_VERSION_MAJOR}.${DF_VERSION_MINOR}.${DF_VERSION_PATCH}-r${DFHACK_RELEASE}") add_definitions(-DDFHACK_VERSION="${DFHACK_VERSION}") From 27824642d90a8bca445af4e6f5d51a89d8317708 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Thu, 22 Mar 2012 10:56:32 +0400 Subject: [PATCH 11/25] Minor refactoring of container indexing and object allocation. --- library/DataDefs.cpp | 39 +++++++++++++++++++++++++--- library/LuaWrapper.cpp | 25 +++++------------- library/include/DataDefs.h | 47 ++++++++++++++++++++-------------- library/include/DataIdentity.h | 38 +++++++++++++++++---------- library/xml | 2 +- 5 files changed, 95 insertions(+), 56 deletions(-) diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index 9576afb7b..6fcd1141a 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.cpp @@ -40,6 +40,37 @@ distribution. using namespace DFHack; + +void *type_identity::do_allocate_pod() { + void *p = malloc(size); + memset(p, 0, size); + return p; +} + +void type_identity::do_copy_pod(void *tgt, const void *src) { + memmove(tgt, src, size); +}; + +void *type_identity::allocate() { + if (can_allocate()) + return do_allocate(); + else + return NULL; +} + +bool type_identity::copy(void *tgt, const void *src) { + if (can_allocate() && tgt && src) + do_copy(tgt, src); + else + return false; +} + +void *enum_identity::do_allocate() { + void *p = malloc(byte_size()); + memcpy(p, &first_item_value, std::min(byte_size(), sizeof(int64_t))); + return p; +} + /* The order of global object constructor calls is * undefined between compilation units. Therefore, * this list has to be plain data, so that it gets @@ -95,19 +126,19 @@ void compound_identity::Init(Core *core) */ } -bitfield_identity::bitfield_identity(size_t size, TAllocateFn alloc, +bitfield_identity::bitfield_identity(size_t size, compound_identity *scope_parent, const char *dfhack_name, int num_bits, const bitfield_item_info *bits) - : compound_identity(size, alloc, scope_parent, dfhack_name), bits(bits), num_bits(num_bits) + : compound_identity(size, NULL, scope_parent, dfhack_name), bits(bits), num_bits(num_bits) { } -enum_identity::enum_identity(size_t size, TAllocateFn alloc, +enum_identity::enum_identity(size_t size, compound_identity *scope_parent, const char *dfhack_name, type_identity *base_type, int64_t first_item_value, int64_t last_item_value, const char *const *keys) - : compound_identity(size, alloc, scope_parent, dfhack_name), + : compound_identity(size, NULL, scope_parent, dfhack_name), first_item_value(first_item_value), last_item_value(last_item_value), keys(keys), base_type(base_type) { diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index a7d830474..770f8f5af 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -265,29 +265,29 @@ int container_identity::lua_item_count(lua_State *state, void *ptr) int container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) { - void *pitem = item_pointer(ptr, idx); auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + void *pitem = item_pointer(id, ptr, idx); return id->lua_read(state, fname_idx, pitem); } void container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index) { - void *pitem = item_pointer(ptr, idx); auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + void *pitem = item_pointer(id, ptr, idx); id->lua_write(state, fname_idx, pitem, val_index); } int ptr_container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) { - void *pitem = item_pointer(ptr, idx); auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + void *pitem = item_pointer(&df::identity_traits::identity, ptr, idx); return df::pointer_identity::lua_read(state, fname_idx, pitem, id); } void ptr_container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index) { - void *pitem = item_pointer(ptr, idx); auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + void *pitem = item_pointer(&df::identity_traits::identity, ptr, idx); df::pointer_identity::lua_write(state, fname_idx, pitem, id, val_index); } @@ -307,25 +307,12 @@ void bit_container_identity::lua_item_write(lua_State *state, int fname_idx, voi field_error(state, fname_idx, "boolean or number expected", "write"); } -int df::buffer_container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) -{ - auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); - void *pitem = ((uint8_t*)ptr) + idx * id->byte_size(); - return id->lua_read(state, fname_idx, pitem); -} - -void df::buffer_container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index) -{ - auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); - void *pitem = ((uint8_t*)ptr) + idx * id->byte_size(); - id->lua_write(state, fname_idx, pitem, val_index); -} - /* */ static int change_error(lua_State *state) { luaL_error(state, "Attempt to change a read-only table.\n"); + return 0; } /** @@ -618,6 +605,8 @@ static int read_field(lua_State *state, const struct_field_info *field, void *pt case struct_field_info::END: return 0; } + + return 0; } static void write_field(lua_State *state, const struct_field_info *field, void *ptr, int value_idx) diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 9ec266fab..da9ffd4b5 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -69,14 +69,12 @@ namespace DFHack protected: type_identity(size_t size) : size(size) {}; - virtual void *do_instantiate() { - void *p = malloc(size); - memset(p, 0, size); - return p; - } - virtual void do_copy(void *tgt, const void *src) { - memmove(tgt, src, size); - }; + void *do_allocate_pod(); + void do_copy_pod(void *tgt, const void *src); + + virtual bool can_allocate() { return true; } + virtual void *do_allocate() { return do_allocate_pod(); } + virtual void do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); } public: virtual ~type_identity() {} @@ -93,6 +91,9 @@ namespace DFHack virtual void build_metatable(lua_State *state); virtual bool isContainer() { return false; } + + void *allocate(); + bool copy(void *tgt, const void *src); }; class DFHACK_EXPORT constructed_identity : public type_identity { @@ -102,13 +103,9 @@ namespace DFHack constructed_identity(size_t size, TAllocateFn alloc) : type_identity(size), allocator(alloc) {}; - virtual void *do_instantiate() { - return allocator ? allocator(NULL,NULL) : type_identity::do_instantiate(); - } - virtual void do_copy(void *tgt, const void *src) { - if (allocator) allocator(tgt,src); - else type_identity::do_copy(tgt, src); - }; + virtual bool can_allocate() { return (allocator != NULL); } + virtual void *do_allocate() { return allocator(NULL,NULL); } + virtual void do_copy(void *tgt, const void *src) { allocator(tgt,src); } virtual int lua_read(lua_State *state, int fname_idx, void *ptr); virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); @@ -151,8 +148,13 @@ namespace DFHack const bitfield_item_info *bits; int num_bits; + protected: + virtual bool can_allocate() { return true; } + virtual void *do_allocate() { return do_allocate_pod(); } + virtual void do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); } + public: - bitfield_identity(size_t size, TAllocateFn alloc, + bitfield_identity(size_t size, compound_identity *scope_parent, const char *dfhack_name, int num_bits, const bitfield_item_info *bits); @@ -171,8 +173,13 @@ namespace DFHack type_identity *base_type; + protected: + virtual bool can_allocate() { return true; } + virtual void *do_allocate(); + virtual void do_copy(void *tgt, const void *src) { do_copy_pod(tgt, src); } + public: - enum_identity(size_t size, TAllocateFn alloc, + enum_identity(size_t size, compound_identity *scope_parent, const char *dfhack_name, type_identity *base_type, int64_t first_item_value, int64_t last_item_value, @@ -266,6 +273,8 @@ namespace DFHack static void *get_vtable(virtual_ptr instance_ptr) { return *(void**)instance_ptr; } + bool can_allocate() { return struct_identity::can_allocate() && (vtable_ptr != NULL); } + public: virtual_identity(size_t size, TAllocateFn alloc, const char *dfhack_name, const char *original_name, @@ -295,8 +304,8 @@ namespace DFHack } public: - bool can_instantiate() { return (vtable_ptr != NULL); } - virtual_ptr instantiate() { return can_instantiate() ? (virtual_ptr)do_instantiate() : NULL; } + bool can_instantiate() { return can_allocate(); } + virtual_ptr instantiate() { return can_instantiate() ? (virtual_ptr)do_allocate() : NULL; } static virtual_ptr clone(virtual_ptr obj); public: diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index ac5903beb..987793828 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -92,7 +92,7 @@ namespace DFHack protected: virtual int item_count(void *ptr) = 0; - virtual void *item_pointer(void *ptr, int idx) = 0; + virtual void *item_pointer(type_identity *item, void *ptr, int idx) = 0; }; class DFHACK_EXPORT ptr_container_identity : public container_identity { @@ -122,7 +122,7 @@ namespace DFHack virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); protected: - virtual void *item_pointer(void *, int) { return NULL; } + virtual void *item_pointer(type_identity *, void *, int) { return NULL; } virtual bool get_item(void *ptr, int idx) = 0; virtual void set_item(void *ptr, int idx, bool val) = 0; @@ -185,6 +185,11 @@ namespace df class stl_ptr_vector_identity : public ptr_container_identity { public: + /* + * This class assumes that std::vector is equivalent + * in layout and behavior to std::vector for any T. + */ + stl_ptr_vector_identity(type_identity *item = NULL, enum_identity *ienum = NULL) : ptr_container_identity(sizeof(std::vector),allocator_fn >,item, ienum) {}; @@ -199,7 +204,7 @@ namespace df virtual int item_count(void *ptr) { return ((std::vector*)ptr)->size(); }; - virtual void *item_pointer(void *ptr, int idx) { + virtual void *item_pointer(type_identity *, void *ptr, int idx) { return &(*(std::vector*)ptr)[idx]; } }; @@ -220,12 +225,11 @@ namespace df static buffer_container_identity base_instance; - virtual int lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); - virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); - protected: virtual int item_count(void *ptr) { return size; } - virtual void *item_pointer(void *ptr, int idx) { return NULL; } + virtual void *item_pointer(type_identity *item, void *ptr, int idx) { + return ((uint8_t*)ptr) + idx * item->byte_size(); + } }; template @@ -243,29 +247,35 @@ namespace df protected: virtual int item_count(void *ptr) { return ((T*)ptr)->size(); } - virtual void *item_pointer(void *ptr, int idx) { return &(*(T*)ptr)[idx]; } + virtual void *item_pointer(type_identity *item, void *ptr, int idx) { + return &(*(T*)ptr)[idx]; + } }; - template class bit_array_identity : public bit_container_identity { public: - typedef BitArray container; + /* + * This class assumes that BitArray is equivalent + * in layout and behavior to BitArray for any T. + */ + + typedef BitArray container; bit_array_identity(enum_identity *ienum = NULL) : bit_container_identity(sizeof(container), &allocator_fn, ienum) {} std::string getFullName(type_identity *item) { - return "BitArray" + bit_container_identity::getFullName(item); + return "BitArray<>"; } protected: virtual int item_count(void *ptr) { return ((container*)ptr)->size * 8; } virtual bool get_item(void *ptr, int idx) { - return ((container*)ptr)->is_set(T(idx)); + return ((container*)ptr)->is_set(idx); } virtual void set_item(void *ptr, int idx, bool val) { - ((container*)ptr)->set(T(idx), val); + ((container*)ptr)->set(idx, val); } }; @@ -412,7 +422,7 @@ namespace df bit_container_identity *identity_traits >::get() { static type_identity *eid = identity_traits::get(); static enum_identity *reid = eid->type() == DFHack::IDTYPE_ENUM ? (enum_identity*)eid : NULL; - static bit_array_identity identity(reid); + static bit_array_identity identity(reid); return &identity; } diff --git a/library/xml b/library/xml index c90a2d499..b28296a8c 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit c90a2d499024319ea9aa4f98b3b61df7bba2fc62 +Subproject commit b28296a8c0c2e5aaa522840598738109018f0146 From c48c7cf874c7b9d7764f448a72c7bf750eef5d9d Mon Sep 17 00:00:00 2001 From: RossM Date: Thu, 22 Mar 2012 00:33:18 -0700 Subject: [PATCH 12/25] Make logic closer to C# version. Address code comments from peterix. --- plugins/autolabor/autolabor.cpp | 327 +++++++++++++++++++------------- 1 file changed, 195 insertions(+), 132 deletions(-) diff --git a/plugins/autolabor/autolabor.cpp b/plugins/autolabor/autolabor.cpp index 478c53ee0..262e59616 100644 --- a/plugins/autolabor/autolabor.cpp +++ b/plugins/autolabor/autolabor.cpp @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include using namespace DFHack; @@ -28,6 +30,7 @@ using df::global::world; // our own, empty header. #include "autolabor.h" +#define ARRAY_COUNT(array) (sizeof(array)/sizeof((array)[0])) static int enable_autolabor; @@ -40,56 +43,11 @@ command_result autolabor (color_ostream &out, std::vector & parame // The name string provided must correspond to the filename - autolabor.plug.so or autolabor.plug.dll in this case DFHACK_PLUGIN("autolabor"); -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) -{ - // Fill the command list with your commands. - commands.clear(); - commands.push_back(PluginCommand( - "autolabor", "Do nothing, look pretty.", - autolabor, false, /* true means that the command can't be used from non-interactive user interface */ - // Extended help string. Used by CR_WRONG_USAGE and the help command: - " This command does nothing at all.\n" - "Example:\n" - " autolabor\n" - " Does nothing.\n" - )); - return CR_OK; -} - -// This is called right before the plugin library is removed from memory. -DFhackCExport command_result plugin_shutdown ( color_ostream &out ) -{ - // You *MUST* kill all threads you created before this returns. - // If everything fails, just return CR_FAILURE. Your plugin will be - // in a zombie state, but things won't crash. - return CR_OK; -} - -// Called to notify the plugin about important state changes. -// Invoked with DF suspended, and always before the matching plugin_onupdate. -// More event codes may be added in the future. -/* -DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event) -{ - switch (event) { - case SC_GAME_LOADED: - // initialize from the world just loaded - break; - case SC_GAME_UNLOADED: - // cleanup - break; - default: - break; - } - return CR_OK; -} -*/ - enum labor_mode { + FIXED, + AUTOMATIC, EVERYONE, HAULERS, - AUTOMATIC, - FIXED, }; enum dwarf_state { @@ -345,7 +303,7 @@ struct labor_info int minimum_dwarfs; }; -static const struct labor_info labor_info[] = { +static const struct labor_info labor_infos[] = { /* MINE */ {AUTOMATIC, true, 2}, /* HAUL_STONE */ {HAULERS, false, 1}, /* HAUL_WOOD */ {HAULERS, false, 1}, @@ -438,6 +396,34 @@ struct dwarf_info bool has_exclusive_labor; }; +DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) +{ + assert(ARRAY_COUNT(labor_infos) > ENUM_LAST_ITEM(unit_labor)); + + // Fill the command list with your commands. + commands.clear(); + commands.push_back(PluginCommand( + "autolabor", "Automatically manage dwarf labors.", + autolabor, false, /* true means that the command can't be used from non-interactive user interface */ + // Extended help string. Used by CR_WRONG_USAGE and the help command: + " autolabor enable\n" + " autolabor disable\n" + " Enables or disables the plugin.\n" + "Function:\n" + " When enabled, autolabor periodically checks your dwarves and enables or\n" + " disables labors. It tries to keep as many dwarves as possible busy but\n" + " also tries to have dwarves specialize in specific skills.\n" + " Warning: autolabor will override any manual changes you make to labors\n" + " while it is enabled.\n" + )); + return CR_OK; +} + +DFhackCExport command_result plugin_shutdown ( color_ostream &out ) +{ + return CR_OK; +} + DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { static int step_count = 0; @@ -454,10 +440,27 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) std::vector dwarfs; + bool has_butchers = false; + bool has_fishery = false; + + for (int i = 0; i < world->buildings.all.size(); ++i) + { + df::building *build = world->buildings.all[i]; + auto type = build->getType(); + if (df::enums::building_type::Workshop == type) + { + auto subType = build->getSubtype(); + if (df::enums::workshop_type::Butchers == subType) + has_butchers = true; + if (df::enums::workshop_type::Fishery == subType) + has_fishery = true; + } + } + for (int i = 0; i < world->units.all.size(); ++i) { df::unit* cre = world->units.all[i]; - if (cre->race == race && cre->civ_id == civ && !cre->flags1.bits.marauder && !cre->flags1.bits.diplomat && !cre->flags1.bits.merchant) { + if (cre->race == race && cre->civ_id == civ && !cre->flags1.bits.marauder && !cre->flags1.bits.diplomat && !cre->flags1.bits.merchant && !cre->flags1.bits.dead) { dwarfs.push_back(cre); } } @@ -479,7 +482,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) { assert(dwarfs[dwarf]->status.souls.size() > 0); - for (auto s = dwarfs[dwarf]->status.souls[0]->skills.begin(); s < dwarfs[dwarf]->status.souls[0]->skills.end(); s++) + for (auto s = dwarfs[dwarf]->status.souls[0]->skills.begin(); s != dwarfs[dwarf]->status.souls[0]->skills.end(); s++) { df::job_skill skill = (*s)->id; @@ -548,9 +551,9 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) continue; assert(labor >= 0); - assert(labor < _countof(labor_info)); + assert(labor < _countof(labor_infos)); - if (labor_info[labor].is_exclusive && dwarfs[dwarf]->status.labors[labor]) + if (labor_infos[labor].is_exclusive && dwarfs[dwarf]->status.labors[labor]) dwarf_info[dwarf].mastery_penalty -= 100; } } @@ -602,68 +605,6 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) state_count[dwarf_info[dwarf].state]++; } - // Set about 1/3 of the dwarfs as haulers. The haulers have all HAULER labors enabled. Having a lot of haulers helps - // make sure that hauling jobs are handled quickly rather than building up. - - int num_haulers = state_count[IDLE] + state_count[BUSY] / 3; - if (num_haulers < 1) - num_haulers = 1; - - std::vector hauler_ids; - for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) - { - if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY) - hauler_ids.push_back(dwarf); - } - - // Idle dwarves come first, then we sort from least-skilled to most-skilled. - - std::sort(hauler_ids.begin(), hauler_ids.end(), [&dwarf_info] (int i, int j) -> bool - { - if (dwarf_info[i].state == IDLE && dwarf_info[j].state != IDLE) - return true; - if (dwarf_info[i].state != IDLE && dwarf_info[j].state == IDLE) - return false; - return dwarf_info[i].mastery_penalty > dwarf_info[j].mastery_penalty; - }); - - FOR_ENUM_ITEMS(unit_labor, labor) - { - if (labor == df::enums::unit_labor::NONE) - continue; - - assert(labor >= 0); - assert(labor < _countof(labor_info)); - - if (labor_info[labor].mode != HAULERS) - continue; - - for (int i = 0; i < num_haulers; i++) - { - assert(i < hauler_ids.size()); - - int dwarf = hauler_ids[i]; - - assert(dwarf >= 0); - assert(dwarf < n_dwarfs); - - dwarfs[dwarf]->status.labors[labor] = true; - dwarf_info[dwarf].assigned_jobs++; - } - - for (int i = num_haulers; i < hauler_ids.size(); i++) - { - assert(i < hauler_ids.size()); - - int dwarf = hauler_ids[i]; - - assert(dwarf >= 0); - assert(dwarf < n_dwarfs); - - dwarfs[dwarf]->status.labors[labor] = false; - } - } - // Generate labor -> skill mapping df::job_skill labor_to_skill[ENUM_LAST_ITEM(unit_labor) + 1]; @@ -682,7 +623,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) } } - // Handle all skills except those marked HAULERS + std::vector labors; FOR_ENUM_ITEMS(unit_labor, labor) { @@ -690,11 +631,25 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) continue; assert(labor >= 0); - assert(labor < _countof(labor_info)); + assert(labor < _countof(labor_infos)); + + labors.push_back(labor); + } + + std::sort(labors.begin(), labors.end(), [] (int i, int j) { return labor_infos[i].mode < labor_infos[j].mode; }); + + // Handle all skills except those marked HAULERS + + for (auto lp = labors.begin(); lp != labors.end(); ++lp) + { + auto labor = *lp; + + assert(labor >= 0); + assert(labor < _countof(labor_infos)); df::job_skill skill = labor_to_skill[labor]; - if (labor_info[labor].mode == HAULERS) + if (labor_infos[labor].mode == HAULERS) continue; int best_dwarf = 0; @@ -702,20 +657,21 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) std::vector values(n_dwarfs); std::vector candidates; + std::vector backup_candidates; + std::map dwarf_skill; + + auto mode = labor_infos[labor].mode; + if (AUTOMATIC == mode && state_count[IDLE] == 0) + mode = FIXED; for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) { - if (labor_info[labor].mode == AUTOMATIC && state_count[IDLE] > 0 && dwarf_info[dwarf].state != IDLE) + if (dwarf_info[dwarf].state != IDLE && dwarf_info[dwarf].state != BUSY && mode != EVERYONE) continue; - if (dwarf_info[dwarf].state != IDLE && dwarf_info[dwarf].state != BUSY) + if (labor_infos[labor].is_exclusive && dwarf_info[dwarf].has_exclusive_labor) continue; - if (labor_info[labor].is_exclusive && dwarf_info[dwarf].has_exclusive_labor) - continue; - - candidates.push_back(dwarf); - int value = dwarf_info[dwarf].mastery_penalty - dwarf_info[dwarf].assigned_jobs; if (skill != df::enums::job_skill::NONE) @@ -733,6 +689,8 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) } } + dwarf_skill[dwarf] = skill_level; + value += skill_level * 100; value += skill_experience / 20; if (skill_level > 0 || skill_experience > 0) @@ -740,26 +698,63 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) if (skill_level >= 15) value += 1000 * (skill_level - 14); } + else + { + dwarf_skill[dwarf] = 0; + } if (dwarfs[dwarf]->status.labors[labor]) { value += 5; - if (labor_info[labor].is_exclusive) + if (labor_infos[labor].is_exclusive) value += 350; } values[dwarf] = value; + + if (mode == AUTOMATIC && dwarf_info[dwarf].state != IDLE) + backup_candidates.push_back(dwarf); + else + candidates.push_back(dwarf); + + } + + if (candidates.size() == 0) + { + candidates = backup_candidates; + mode = FIXED; } - if (labor_info[labor].mode != EVERYONE) + if (labor_infos[labor].mode != EVERYONE) std::sort(candidates.begin(), candidates.end(), [&values] (int i, int j) { return values[i] > values[j]; }); for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) { - if (state_count[IDLE] > 0 && dwarf_info[dwarf].state == BUSY && labor_info[labor].mode == AUTOMATIC) + bool allow_labor = false; + + if (dwarf_info[dwarf].state == BUSY && + mode == AUTOMATIC && + (labor_infos[labor].is_exclusive || dwarf_skill[dwarf] > 0)) { - if (dwarfs[dwarf]->status.labors[labor]) - dwarf_info[dwarf].assigned_jobs++; + allow_labor = true; + } + + if (dwarf_info[dwarf].state == OTHER && + mode == AUTOMATIC && + dwarf_skill[dwarf] > 0 && + !dwarf_info[dwarf].is_best_noble) + { + allow_labor = true; + } + + if (dwarfs[dwarf]->status.labors[labor] && + allow_labor && + !(labor_infos[labor].is_exclusive && dwarf_info[dwarf].has_exclusive_labor)) + { + if (labor_infos[labor].is_exclusive) + dwarf_info[dwarf].has_exclusive_labor = true; + + dwarf_info[dwarf].assigned_jobs++; } else { @@ -767,11 +762,17 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) } } - int minimum_dwarfs = labor_info[labor].minimum_dwarfs; + int minimum_dwarfs = labor_infos[labor].minimum_dwarfs; - if (labor_info[labor].mode == EVERYONE) + if (labor_infos[labor].mode == EVERYONE) minimum_dwarfs = n_dwarfs; + // Special - don't assign hunt without a butchers, or fish without a fishery + if (df::enums::unit_labor::HUNT == labor && !has_butchers) + minimum_dwarfs = 0; + if (df::enums::unit_labor::FISH == labor && !has_fishery) + minimum_dwarfs = 0; + for (int i = 0; i < candidates.size() && i < minimum_dwarfs; i++) { int dwarf = candidates[i]; @@ -784,11 +785,73 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) dwarfs[dwarf]->status.labors[labor] = true; - if (labor_info[labor].is_exclusive) + if (labor_infos[labor].is_exclusive) dwarf_info[dwarf].has_exclusive_labor = true; } } + // Set about 1/3 of the dwarfs as haulers. The haulers have all HAULER labors enabled. Having a lot of haulers helps + // make sure that hauling jobs are handled quickly rather than building up. + + int num_haulers = state_count[IDLE] + state_count[BUSY] / 3; + if (num_haulers < 1) + num_haulers = 1; + + std::vector hauler_ids; + for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) + { + if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY) + hauler_ids.push_back(dwarf); + } + + // Idle dwarves come first, then we sort from least-skilled to most-skilled. + + std::sort(hauler_ids.begin(), hauler_ids.end(), [&dwarf_info] (int i, int j) -> bool + { + if (dwarf_info[i].state == IDLE && dwarf_info[j].state != IDLE) + return true; + if (dwarf_info[i].state != IDLE && dwarf_info[j].state == IDLE) + return false; + return dwarf_info[i].mastery_penalty > dwarf_info[j].mastery_penalty; + }); + + FOR_ENUM_ITEMS(unit_labor, labor) + { + if (labor == df::enums::unit_labor::NONE) + continue; + + assert(labor >= 0); + assert(labor < _countof(labor_infos)); + + if (labor_infos[labor].mode != HAULERS) + continue; + + for (int i = 0; i < num_haulers; i++) + { + assert(i < hauler_ids.size()); + + int dwarf = hauler_ids[i]; + + assert(dwarf >= 0); + assert(dwarf < n_dwarfs); + + dwarfs[dwarf]->status.labors[labor] = true; + dwarf_info[dwarf].assigned_jobs++; + } + + for (int i = num_haulers; i < hauler_ids.size(); i++) + { + assert(i < hauler_ids.size()); + + int dwarf = hauler_ids[i]; + + assert(dwarf >= 0); + assert(dwarf < n_dwarfs); + + dwarfs[dwarf]->status.labors[labor] = false; + } + } + return CR_OK; } From 8f72a642b2400a05ef9e766bc0b1ba214c817dcc Mon Sep 17 00:00:00 2001 From: Warmist Date: Thu, 22 Mar 2012 21:47:33 +0200 Subject: [PATCH 13/25] Fixed getxyz(), getCreatureAtPos() (also returns nil/creature) and empregnate to work with look/talk --- plugins/Dfusion/luafiles/common.lua | 27 +++++++++++-------------- plugins/Dfusion/luafiles/tools/init.lua | 2 ++ 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/plugins/Dfusion/luafiles/common.lua b/plugins/Dfusion/luafiles/common.lua index bf41d7103..ecb5b5023 100644 --- a/plugins/Dfusion/luafiles/common.lua +++ b/plugins/Dfusion/luafiles/common.lua @@ -476,28 +476,25 @@ function getSelectedUnit() end end function getxyz() -- this will return pointers x,y and z coordinates. - local off=VersionInfo.getGroup("Position"):getAddress("cursor_xyz") -- lets find where in memory its being held - -- now lets read them (they are double words (or unsigned longs or 4 bits each) and go in sucesion - local x=engine.peekd(off) - local y=engine.peekd(off+4) --next is 4 from start - local z=engine.peekd(off+8) --next is 8 from start - --print("Pointer @:"..x..","..y..","..z) + local x=df.cursor.x + local y=df.cursor.y + local z=df.cursor.z return x,y,z -- return the coords end -function GetCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord +function getCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord --local x,y,z=getxyz() --get 'X' coords - local vector=engine.peek(VersionInfo.getGroup("Creatures"):getAddress("vector"),ptr_vector) -- load all creatures - for i = 0, vector:size()-1 do -- look into all creatures offsets - local curoff=vector:getval(i) -- get i-th creatures offset - local cx=engine.peek(curoff,ptr_Creature.x) --get its coordinates - local cy=engine.peek(curoff,ptr_Creature.y) - local cz=engine.peek(curoff,ptr_Creature.z) + local vector=df.world.units.all -- load all creatures + for i = 0, vector.size-1 do -- look into all creatures offsets + local curpos=vector[i]:deref().pos --get its coordinates + local cx=curpos.x + local cy=curpos.y + local cz=curpos.z if cx==x and cy==y and cz==z then --compare them - return i --return index + return vector[i]:deref() --return index end end print("Creature not found!") - return -1 + return nil end function Allocate(size) diff --git a/plugins/Dfusion/luafiles/tools/init.lua b/plugins/Dfusion/luafiles/tools/init.lua index a12a01481..630de564f 100644 --- a/plugins/Dfusion/luafiles/tools/init.lua +++ b/plugins/Dfusion/luafiles/tools/init.lua @@ -264,6 +264,8 @@ function tools.empregnate(unit) if unit==nil then unit=getSelectedUnit() end + if unit==nil then + unit=getCreatureAtPos(getxyz()) if unit==nil then error("Failed to empregnate. Unit not selected/valide") end From f25d15ce9c6efa9e76ad7afe4ccaac1762c390ee Mon Sep 17 00:00:00 2001 From: Warmist Date: Thu, 22 Mar 2012 23:27:25 +0200 Subject: [PATCH 14/25] Stupid mistake fixed --- plugins/Dfusion/luafiles/tools/init.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/Dfusion/luafiles/tools/init.lua b/plugins/Dfusion/luafiles/tools/init.lua index 630de564f..43747aba6 100644 --- a/plugins/Dfusion/luafiles/tools/init.lua +++ b/plugins/Dfusion/luafiles/tools/init.lua @@ -264,8 +264,11 @@ function tools.empregnate(unit) if unit==nil then unit=getSelectedUnit() end + if unit==nil then unit=getCreatureAtPos(getxyz()) + end + if unit==nil then error("Failed to empregnate. Unit not selected/valide") end From ead28db451b3f433bc241f3b2b83e04c924cbacd Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 23 Mar 2012 09:38:49 +0400 Subject: [PATCH 15/25] Remove the return type from lua_read, because it always returns 1. --- library/LuaWrapper.cpp | 68 ++++++++++++++++++---------------- library/include/DataDefs.h | 6 +-- library/include/DataIdentity.h | 16 ++++---- 3 files changed, 47 insertions(+), 43 deletions(-) diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 770f8f5af..7071d15d2 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -152,10 +152,9 @@ void *DFHack::GetDFObject(lua_State *state, type_identity *type, int val_index) * Identity object read/write methods * **************************************/ -int constructed_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +void constructed_identity::lua_read(lua_State *state, int fname_idx, void *ptr) { push_object_internal(state, this, ptr); - return 1; } void constructed_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) @@ -163,9 +162,9 @@ void constructed_identity::lua_write(lua_State *state, int fname_idx, void *ptr, field_error(state, fname_idx, "complex object", "write"); } -int enum_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +void enum_identity::lua_read(lua_State *state, int fname_idx, void *ptr) { - return base_type->lua_read(state, fname_idx, ptr); + base_type->lua_read(state, fname_idx, ptr); } void enum_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) @@ -173,10 +172,9 @@ void enum_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int va base_type->lua_write(state, fname_idx, ptr, val_index); } -int df::number_identity_base::lua_read(lua_State *state, int fname_idx, void *ptr) +void df::number_identity_base::lua_read(lua_State *state, int fname_idx, void *ptr) { lua_pushnumber(state, read(ptr)); - return 1; } void df::number_identity_base::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) @@ -187,10 +185,9 @@ void df::number_identity_base::lua_write(lua_State *state, int fname_idx, void * write(ptr, lua_tonumber(state, val_index)); } -int df::bool_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +void df::bool_identity::lua_read(lua_State *state, int fname_idx, void *ptr) { lua_pushboolean(state, *(bool*)ptr); - return 1; } void df::bool_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) @@ -205,11 +202,10 @@ void df::bool_identity::lua_write(lua_State *state, int fname_idx, void *ptr, in field_error(state, fname_idx, "boolean or number expected", "write"); } -int df::stl_string_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +void df::stl_string_identity::lua_read(lua_State *state, int fname_idx, void *ptr) { auto pstr = (std::string*)ptr; lua_pushlstring(state, pstr->data(), pstr->size()); - return 1; } void df::stl_string_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) @@ -222,15 +218,14 @@ void df::stl_string_identity::lua_write(lua_State *state, int fname_idx, void *p *(std::string*)ptr = std::string(bytes, size); } -int df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target) +void df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target) { push_object_internal(state, target, *(void**)ptr); - return 1; } -int df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr) +void df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr) { - return lua_read(state, fname_idx, ptr, target); + lua_read(state, fname_idx, ptr, target); } void df::pointer_identity::lua_write(lua_State *state, int fname_idx, void *ptr, @@ -263,11 +258,11 @@ int container_identity::lua_item_count(lua_State *state, void *ptr) return item_count(ptr); } -int container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) +void container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) { auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); void *pitem = item_pointer(id, ptr, idx); - return id->lua_read(state, fname_idx, pitem); + id->lua_read(state, fname_idx, pitem); } void container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index) @@ -277,11 +272,11 @@ void container_identity::lua_item_write(lua_State *state, int fname_idx, void *p id->lua_write(state, fname_idx, pitem, val_index); } -int ptr_container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) +void ptr_container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) { auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); void *pitem = item_pointer(&df::identity_traits::identity, ptr, idx); - return df::pointer_identity::lua_read(state, fname_idx, pitem, id); + df::pointer_identity::lua_read(state, fname_idx, pitem, id); } void ptr_container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index) @@ -291,10 +286,9 @@ void ptr_container_identity::lua_item_write(lua_State *state, int fname_idx, voi df::pointer_identity::lua_write(state, fname_idx, pitem, id, val_index); } -int bit_container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) +void bit_container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) { lua_pushboolean(state, get_item(ptr, idx)); - return 1; } void bit_container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index) @@ -573,7 +567,7 @@ static uint8_t *get_object_addr(lua_State *state, int obj, int field, const char static void GetAdHocMetatable(lua_State *state, const struct_field_info *field); -static int read_field(lua_State *state, const struct_field_info *field, void *ptr) +static void read_field(lua_State *state, const struct_field_info *field, void *ptr) { switch (field->mode) { @@ -581,32 +575,38 @@ static int read_field(lua_State *state, const struct_field_info *field, void *pt { int len = strnlen((char*)ptr, field->count); lua_pushlstring(state, (char*)ptr, len); - return 1; + return; } case struct_field_info::PRIMITIVE: case struct_field_info::SUBSTRUCT: - return field->type->lua_read(state, 2, ptr); + field->type->lua_read(state, 2, ptr); + return; case struct_field_info::POINTER: - return df::pointer_identity::lua_read(state, 2, ptr, field->type); + df::pointer_identity::lua_read(state, 2, ptr, field->type); + return; case struct_field_info::CONTAINER: if (!field->eid || !field->type->isContainer() || field->eid == ((container_identity*)field->type)->getIndexEnumType()) - return field->type->lua_read(state, 2, ptr); + { + field->type->lua_read(state, 2, ptr); + return; + } + // fallthrough case struct_field_info::STATIC_ARRAY: case struct_field_info::STL_VECTOR_PTR: GetAdHocMetatable(state, field); push_object_ref(state, ptr); - return 1; + return; case struct_field_info::END: - return 0; + break; } - return 0; + lua_pushnil(state); } static void write_field(lua_State *state, const struct_field_info *field, void *ptr, int value_idx) @@ -686,7 +686,8 @@ static int meta_struct_index(lua_State *state) auto field = (struct_field_info*)find_field(state, 2, "read"); if (!field) return get_metafield(state); - return read_field(state, field, ptr + field->offset); + read_field(state, field, ptr + field->offset); + return 1; } /** @@ -710,7 +711,8 @@ static int meta_primitive_index(lua_State *state) auto type = (type_identity*)find_field(state, 2, "read"); if (!type) return get_metafield(state); - return type->lua_read(state, 2, ptr); + type->lua_read(state, 2, ptr); + return 1; } /** @@ -788,7 +790,8 @@ static int meta_container_index(lua_State *state) auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); int len = id->lua_item_count(state, ptr); int idx = check_container_index(state, len, 2, iidx, "read"); - return id->lua_item_read(state, 2, ptr, idx); + id->lua_item_read(state, 2, ptr, idx); + return 1; } /** @@ -893,7 +896,8 @@ static int meta_global_index(lua_State *state) void *ptr = *(void**)field->offset; if (!ptr) field_error(state, 2, "global address not known", "read"); - return read_field(state, field, ptr); + read_field(state, field, ptr); + return 1; } /** diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index da9ffd4b5..17751d90d 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -86,7 +86,7 @@ namespace DFHack virtual std::string getFullName() = 0; // For internal use in the lua wrapper - virtual int lua_read(lua_State *state, int fname_idx, void *ptr) = 0; + virtual void lua_read(lua_State *state, int fname_idx, void *ptr) = 0; virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) = 0; virtual void build_metatable(lua_State *state); @@ -107,7 +107,7 @@ namespace DFHack virtual void *do_allocate() { return allocator(NULL,NULL); } virtual void do_copy(void *tgt, const void *src) { allocator(tgt,src); } - virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void lua_read(lua_State *state, int fname_idx, void *ptr); virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); }; @@ -194,7 +194,7 @@ namespace DFHack type_identity *getBaseType() { return base_type; } - virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void lua_read(lua_State *state, int fname_idx, void *ptr); virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); }; diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index 987793828..77e43f99b 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -58,10 +58,10 @@ namespace DFHack std::string getFullName(); virtual void build_metatable(lua_State *state); - static int lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target); + static void lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target); static void lua_write(lua_State *state, int fname_idx, void *ptr, type_identity *target, int val_index); - virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void lua_read(lua_State *state, int fname_idx, void *ptr); virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); }; @@ -87,7 +87,7 @@ namespace DFHack int lua_item_count(lua_State *state, void *ptr); - virtual int lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); + virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); protected: @@ -105,7 +105,7 @@ namespace DFHack std::string getFullName(type_identity *item); - virtual int lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); + virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); }; @@ -118,7 +118,7 @@ namespace DFHack std::string getFullName(type_identity *item); - virtual int lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); + virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); protected: @@ -146,7 +146,7 @@ namespace df std::string getFullName() { return name; } - virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void lua_read(lua_State *state, int fname_idx, void *ptr); virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); protected: @@ -169,7 +169,7 @@ namespace df std::string getFullName() { return "bool"; } - virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void lua_read(lua_State *state, int fname_idx, void *ptr); virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); }; @@ -179,7 +179,7 @@ namespace df std::string getFullName() { return "string"; } - virtual int lua_read(lua_State *state, int fname_idx, void *ptr); + virtual void lua_read(lua_State *state, int fname_idx, void *ptr); virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index); }; From ccc8fac166ad04e67b581da44918a504865811a7 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 23 Mar 2012 10:56:29 +0400 Subject: [PATCH 16/25] Get rid of the write mode field table in metamethods. Two separate tables can be confusing, e.g. if a builtin field overrides a writable custom one only in the read table. --- depends/lua/CMakeLists.txt | 3 + library/LuaWrapper.cpp | 123 +++++++++++++++------------------ library/include/DataIdentity.h | 1 - 3 files changed, 60 insertions(+), 67 deletions(-) diff --git a/depends/lua/CMakeLists.txt b/depends/lua/CMakeLists.txt index 6a97bd434..23ea6a485 100644 --- a/depends/lua/CMakeLists.txt +++ b/depends/lua/CMakeLists.txt @@ -1,6 +1,9 @@ PROJECT ( lua C ) CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +# TODO: make this RelWithDebInfo only +ADD_DEFINITIONS(-DLUA_USE_APICHECK) + IF(WIN32) ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE ) ELSE() diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 7071d15d2..21938627b 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -127,7 +127,7 @@ static void field_error(lua_State *state, int index, const char *err, const char { lua_getfield(state, UPVAL_METATABLE, "__metatable"); const char *cname = lua_tostring(state, -1); - const char *fname = lua_tostring(state, index); + const char *fname = index ? lua_tostring(state, index) : "*"; luaL_error(state, "Cannot %s field %s.%s: %s.", mode, (cname ? cname : "?"), (fname ? fname : "?"), err); } @@ -335,6 +335,14 @@ static void freeze_table(lua_State *state, bool leave_metatable = false, const c lua_pop(state, 1); } +static void LookupInTable(lua_State *state, const char *tname) +{ + lua_getfield(state, LUA_REGISTRYINDEX, tname); + lua_swap(state); + lua_rawget(state, -2); + lua_remove(state, -2); +} + /** * Look up the key on the stack in DFHACK_TYPETABLE; * if found, put result on the stack and return true. @@ -349,10 +357,7 @@ static bool LookupTypeInfo(lua_State *state, bool in_method) } else { - lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPETABLE_NAME); - lua_swap(state); - lua_rawget(state, -2); - lua_remove(state, -2); + LookupInTable(state, DFHACK_TYPETABLE_NAME); } // stack: [info] @@ -556,7 +561,7 @@ static uint8_t *get_object_addr(lua_State *state, int obj, int field, const char !lua_getmetatable(state, obj)) field_error(state, field, "invalid object", mode); - if (!lua_equal(state, -1, UPVAL_METATABLE)) + if (!lua_rawequal(state, -1, UPVAL_METATABLE)) field_error(state, field, "invalid object metatable", mode); lua_pop(state, 1); @@ -697,6 +702,8 @@ static int meta_struct_newindex(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "write"); auto field = (struct_field_info*)find_field(state, 2, "write"); + if (!field) + field_error(state, 2, "builtin property", "write"); write_field(state, field, ptr + field->offset, 3); return 0; } @@ -722,6 +729,8 @@ static int meta_primitive_newindex(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "write"); auto type = (type_identity*)find_field(state, 2, "write"); + if (!type) + field_error(state, 2, "builtin property", "write"); type->lua_write(state, 2, ptr, 3); return 0; } @@ -744,15 +753,18 @@ static int meta_container_len(lua_State *state) * - Numbers are indices and handled directly. * - NULL userdata are metafields; push and exit; */ -static int lookup_container_field(lua_State *state, int field, const char *mode) +static int lookup_container_field(lua_State *state, int field, bool write = false) { if (lua_type(state, field) == LUA_TNUMBER) return field; - lookup_field(state, field, mode); + lookup_field(state, field, write ? "write" : "read"); if (lua_isuserdata(state, -1) && !lua_touserdata(state, -1)) { + if (write) + field_error(state, field, "builtin property", "write"); + lua_pop(state, 1); get_metafield(state); return 0; @@ -783,7 +795,7 @@ static int check_container_index(lua_State *state, int len, static int meta_container_index(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "read"); - int iidx = lookup_container_field(state, 2, "read"); + int iidx = lookup_container_field(state, 2); if (!iidx) return 1; @@ -800,7 +812,7 @@ static int meta_container_index(lua_State *state) static int meta_container_newindex(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "write"); - int iidx = lookup_container_field(state, 2, "write"); + int iidx = lookup_container_field(state, 2, true); auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); int len = id->lua_item_count(state, ptr); @@ -826,7 +838,7 @@ static int meta_bitfield_len(lua_State *state) static int meta_bitfield_index(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "read"); - int iidx = lookup_container_field(state, 2, "read"); + int iidx = lookup_container_field(state, 2); if (!iidx) return 1; @@ -858,7 +870,7 @@ static int meta_bitfield_index(lua_State *state) static int meta_bitfield_newindex(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "write"); - int iidx = lookup_container_field(state, 2, "write"); + int iidx = lookup_container_field(state, 2, true); auto id = (bitfield_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); @@ -906,6 +918,8 @@ static int meta_global_index(lua_State *state) static int meta_global_newindex(lua_State *state) { auto field = (struct_field_info*)find_field(state, 2, "write"); + if (!field) + field_error(state, 2, "builtin property", "write"); void *ptr = *(void**)field->offset; if (!ptr) field_error(state, 2, "global address not known", "write"); @@ -916,35 +930,24 @@ static int meta_global_newindex(lua_State *state) /** * Add fields in the array to the UPVAL_FIELDTABLE candidates on the stack. */ -static void IndexFields(lua_State *state, const struct_field_info *fields) +static void IndexFields(lua_State *state, struct_identity *pstruct) { - // stack: read write + // stack: fieldtable - int base = lua_gettop(state) - 2; + int base = lua_gettop(state); - for (; fields; ++fields) + for (struct_identity *p = pstruct; p; p = p->getParent()) { - switch (fields->mode) - { - case struct_field_info::END: - return; + auto fields = p->getFields(); - case struct_field_info::PRIMITIVE: - case struct_field_info::STATIC_STRING: - case struct_field_info::POINTER: - lua_pushstring(state,fields->name); - lua_pushlightuserdata(state,(void*)fields); - lua_rawset(state,base+2); - // fallthrough - - case struct_field_info::STATIC_ARRAY: - case struct_field_info::SUBSTRUCT: - case struct_field_info::CONTAINER: - case struct_field_info::STL_VECTOR_PTR: - lua_pushstring(state,fields->name); - lua_pushlightuserdata(state,(void*)fields); - lua_rawset(state,base+1); + for (; fields; ++fields) + { + if (fields->mode == struct_field_info::END) break; + + lua_pushstring(state,fields->name); + lua_pushlightuserdata(state,(void*)fields); + lua_rawset(state,base); } } } @@ -989,7 +992,7 @@ static void SetStructMethod(lua_State *state, int meta_idx, int ftable_idx, } /** - * Make a metatable with most common fields, and two empty tables for UPVAL_FIELDTABLE. + * Make a metatable with most common fields, and an empty table for UPVAL_FIELDTABLE. */ static void MakeMetatable(lua_State *state, type_identity *type, const char *kind) { @@ -1005,6 +1008,7 @@ static void MakeMetatable(lua_State *state, type_identity *type, const char *kin LookupInTable(state, type, DFHACK_TYPEID_TABLE_NAME); if (lua_isnil(state, -1)) { + // Copy the string from __metatable if no real type lua_pop(state, 1); lua_getfield(state, base+1, "__metatable"); } @@ -1013,8 +1017,7 @@ static void MakeMetatable(lua_State *state, type_identity *type, const char *kin lua_pushstring(state, kind); lua_setfield(state, base+1, "_kind"); - lua_newtable(state); // read - lua_newtable(state); // write + lua_newtable(state); // fieldtable } /** @@ -1025,15 +1028,12 @@ static void MakeFieldMetatable(lua_State *state, struct_identity *pstruct, { int base = lua_gettop(state); - MakeMetatable(state, pstruct, "struct"); // meta, read, write + MakeMetatable(state, pstruct, "struct"); // meta, fields - for (struct_identity *p = pstruct; p; p = p->getParent()) - { - IndexFields(state, p->getFields()); - } + IndexFields(state, pstruct); SetStructMethod(state, base+1, base+2, reader, "__index"); - SetStructMethod(state, base+1, base+3, writer, "__newindex"); + SetStructMethod(state, base+1, base+2, writer, "__newindex"); // returns: [metatable readfields writefields]; } @@ -1046,13 +1046,13 @@ static void MakePrimitiveMetatable(lua_State *state, type_identity *type) int base = lua_gettop(state); MakeMetatable(state, type, "primitive"); + SetPtrMethods(state, base+1, base+2); EnableMetaField(state, base+2, "value", type); - EnableMetaField(state, base+3, "value", type); SetStructMethod(state, base+1, base+2, meta_primitive_index, "__index"); - SetStructMethod(state, base+1, base+3, meta_primitive_newindex, "__newindex"); + SetStructMethod(state, base+1, base+2, meta_primitive_newindex, "__newindex"); } /** @@ -1090,9 +1090,7 @@ static void AttachEnumKeys(lua_State *state, int base, type_identity *ienum) lua_newtable(state); lua_swap(state); lua_setfield(state, -2, "__index"); - lua_dup(state); lua_setmetatable(state, base+2); - lua_setmetatable(state, base+3); } else lua_pop(state, 1); @@ -1114,6 +1112,7 @@ static void MakeContainerMetatable(lua_State *state, container_identity *type, MakeMetatable(state, type, "container"); SetPtrMethods(state, base+1, base+2); + // Update the type name using full info lua_pushstring(state, type->getFullName(item).c_str()); lua_dup(state); lua_setfield(state, base+1, "__metatable"); @@ -1130,7 +1129,7 @@ static void MakeContainerMetatable(lua_State *state, container_identity *type, SetContainerMethod(state, base+1, base+2, meta_container_len, "__len", type, item, count); SetContainerMethod(state, base+1, base+2, meta_container_index, "__index", type, item, count); - SetContainerMethod(state, base+1, base+3, meta_container_newindex, "__newindex", type, item, count); + SetContainerMethod(state, base+1, base+2, meta_container_newindex, "__newindex", type, item, count); AttachEnumKeys(state, base, ienum); } @@ -1148,31 +1147,21 @@ void container_identity::build_metatable(lua_State *state) MakeContainerMetatable(state, this, getItemType(), -1, getIndexEnumType()); } -void pointer_identity::build_metatable(lua_State *state) -{ - int base = lua_gettop(state); - - primitive_identity::build_metatable(state); - - EnableMetaField(state, base+2, "target", this); - EnableMetaField(state, base+3, "target", this); -} - void bitfield_identity::build_metatable(lua_State *state) { int base = lua_gettop(state); MakeMetatable(state, this, "bitfield"); + SetPtrMethods(state, base+1, base+2); SetContainerMethod(state, base+1, base+2, meta_bitfield_len, "__len", this, NULL, -1); SetContainerMethod(state, base+1, base+2, meta_bitfield_index, "__index", this, NULL, -1); - SetContainerMethod(state, base+1, base+3, meta_bitfield_newindex, "__newindex", this, NULL, -1); + SetContainerMethod(state, base+1, base+2, meta_bitfield_newindex, "__newindex", this, NULL, -1); AttachEnumKeys(state, base, this); EnableMetaField(state, base+2, "whole", this); - EnableMetaField(state, base+3, "whole", this); } void struct_identity::build_metatable(lua_State *state) @@ -1191,7 +1180,7 @@ static void BuildTypeMetatable(lua_State *state, type_identity *type) { type->build_metatable(state); - lua_pop(state, 2); + lua_pop(state, 1); SaveTypeInfo(state, type); } @@ -1229,7 +1218,7 @@ static void GetAdHocMetatable(lua_State *state, const struct_field_info *field) luaL_error(state, "Invalid ad-hoc field: %d", field->mode); } - lua_pop(state, 2); + lua_pop(state, 1); SaveTypeInfo(state, (void*)field); } @@ -1263,9 +1252,6 @@ static void RenderType(lua_State *state, compound_identity *node) int base = lua_gettop(state); - lua_pushlightuserdata(state, node); - lua_setfield(state, base, "_identity"); - switch (node->type()) { case IDTYPE_STRUCT: @@ -1344,12 +1330,14 @@ static void RenderType(lua_State *state, compound_identity *node) { BuildTypeMetatable(state, node); + // Set metatable for the inner table lua_dup(state); lua_setmetatable(state, base); lua_swap(state); // -> meta curtable freeze_table(state, true, "global"); + // Copy __newindex to the outer metatable lua_getfield(state, base, "__newindex"); lua_setfield(state, -2, "__newindex"); @@ -1363,6 +1351,9 @@ static void RenderType(lua_State *state, compound_identity *node) lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME); lua_setfield(state, -2, "__tostring"); + lua_pushlightuserdata(state, node); + lua_setfield(state, -2, "_identity"); + lua_pop(state, 1); SaveInTable(state, node, DFHACK_TYPEID_TABLE_NAME); diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index 77e43f99b..7b576b117 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -56,7 +56,6 @@ namespace DFHack type_identity *getTarget() { return target; } std::string getFullName(); - virtual void build_metatable(lua_State *state); static void lua_read(lua_State *state, int fname_idx, void *ptr, type_identity *target); static void lua_write(lua_State *state, int fname_idx, void *ptr, type_identity *target, int val_index); From 78437310d0e032880d19ee97eac67af60b369258 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 23 Mar 2012 11:30:54 +0400 Subject: [PATCH 17/25] Add a sizeof method/function to retrieve object/type size and address. --- library/LuaWrapper.cpp | 121 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 110 insertions(+), 11 deletions(-) diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 21938627b..a394b4cfd 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -67,6 +67,7 @@ inline void lua_swap(lua_State *state) { lua_insert(state, -2); } #define DFHACK_CHANGEERROR_NAME "DFHack::ChangeError" #define DFHACK_COMPARE_NAME "DFHack::ComparePtrs" #define DFHACK_TYPE_TOSTRING_NAME "DFHack::TypeToString" +#define DFHACK_SIZEOF_NAME "DFHack::Sizeof" /* * Upvalue: contents of DFHACK_TYPETABLE_NAME @@ -417,6 +418,14 @@ static void push_object_ref(lua_State *state, void *ptr) // stack: [userdata] } +static void *get_object_ref(lua_State *state, int val_index) +{ + assert(!lua_islightuserdata(state, val_index)); + + auto ref = (DFRefHeader*)lua_touserdata(state, val_index); + return ref->ptr; +} + /** * Push the pointer using given identity. */ @@ -499,8 +508,42 @@ static void *get_object_internal(lua_State *state, type_identity *type, int val_ /* * Finally decode the reference. */ - auto ref = (DFRefHeader*)lua_touserdata(state, val_index); - return ref->ptr; + return get_object_ref(state, val_index); +} + +/** + * Given a DF object reference or type, safely retrieve its identity pointer. + */ +static type_identity *get_object_identity(lua_State *state, int objidx, const char *ctx) +{ + if (!lua_getmetatable(state, objidx)) + luaL_error(state, "Invalid object in %s", ctx); + + // Verify object type validity + if (lua_isuserdata(state, objidx)) + { + lua_dup(state); + LookupInTable(state, DFHACK_TYPETABLE_NAME); + } + else + { + lua_pushvalue(state, objidx); + LookupInTable(state, DFHACK_TYPEID_TABLE_NAME); + } + + if (lua_isnil(state, -1)) + luaL_error(state, "Invalid object metatable in %s", ctx); + lua_pop(state, 1); + + // Extract identity from metatable + lua_getfield(state, -1, "_identity"); + + type_identity *id = (type_identity*)lua_touserdata(state, -1); + if (!id) + luaL_error(state, "Invalid object identity in %s", ctx); + + lua_pop(state, 2); + return id; } /** @@ -517,19 +560,63 @@ static int meta_ptr_compare(lua_State *state) return 1; } - if (!lua_equal(state, -1, -2)) + if (get_object_ref(state, 1) != get_object_ref(state, 2)) + { + lua_pushboolean(state, false); + return 1; + } + + if (!lua_rawequal(state, -1, -2)) { // todo: nonidentical type comparison lua_pushboolean(state, false); return 1; } - auto ref1 = (DFRefHeader*)lua_touserdata(state, 1); - auto ref2 = (DFRefHeader*)lua_touserdata(state, 2); - lua_pushboolean(state, ref1->ptr == ref2->ptr); + lua_pushboolean(state, true); return 1; } +/** + * Method: sizeof for DF object references. + * + * Returns: size[, address] + */ +static int meta_sizeof(lua_State *state) +{ + int argc = lua_gettop(state); + + if (argc != 1) + luaL_error(state, "Usage: object:sizeof() or df.sizeof(object)"); + + // Two special cases: nil and lightuserdata for NULL and void* + if (lua_isnil(state, 1)) + { + lua_pushnil(state); + lua_pushinteger(state, 0); + return 2; + } + + if (lua_islightuserdata(state, 1)) + { + lua_pushnil(state); + lua_pushnumber(state, (size_t)lua_touserdata(state, 1)); + return 2; + } + + type_identity *id = get_object_identity(state, 1, "df.sizeof()"); + + lua_pushinteger(state, id->byte_size()); + + if (lua_isuserdata(state, 1)) + { + lua_pushnumber(state, (size_t)get_object_ref(state, 1)); + return 2; + } + else + return 1; +} + /** * Resolve the field name in UPVAL_FIELDTABLE, die if not found. */ @@ -566,8 +653,7 @@ static uint8_t *get_object_addr(lua_State *state, int obj, int field, const char lua_pop(state, 1); - auto ref = (DFRefHeader*)lua_touserdata(state, obj); - return (uint8_t*)ref->ptr; + return (uint8_t*)get_object_ref(state, obj); } static void GetAdHocMetatable(lua_State *state, const struct_field_info *field); @@ -847,9 +933,9 @@ static int meta_bitfield_index(lua_State *state) // whole if (lua_isuserdata(state, iidx) && lua_touserdata(state, iidx) == id) { - lua_Integer intv = 0; + size_t intv = 0; memcpy(&intv, ptr, std::min(sizeof(intv), size_t(id->byte_size()))); - lua_pushinteger(state, intv); + lua_pushnumber(state, intv); return 1; } @@ -880,7 +966,7 @@ static int meta_bitfield_newindex(lua_State *state) if (!lua_isnumber(state, 3)) field_error(state, 2, "number expected", "write"); - lua_Integer intv = lua_tointeger(state, 3); + size_t intv = (size_t)lua_tonumber(state, 3); memcpy(ptr, &intv, std::min(sizeof(intv), size_t(id->byte_size()))); return 0; } @@ -974,8 +1060,12 @@ static void SetPtrMethods(lua_State *state, int meta_idx, int read_idx) lua_pushcclosure(state, meta_ptr_tostring, 2); lua_setfield(state, meta_idx, "__tostring"); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); + lua_setfield(state, meta_idx, "sizeof"); + EnableMetaField(state, read_idx, "_type"); EnableMetaField(state, read_idx, "_kind"); + EnableMetaField(state, read_idx, "sizeof"); } /** @@ -1326,6 +1416,9 @@ static void RenderType(lua_State *state, compound_identity *node) assert(base == lua_gettop(state)); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); + lua_setfield(state, base, "sizeof"); + if (node->type() == IDTYPE_GLOBAL) { BuildTypeMetatable(state, node); @@ -1389,6 +1482,9 @@ static void DoAttach(lua_State *state) lua_pushcfunction(state, meta_type_tostring); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_TYPE_TOSTRING_NAME); + lua_pushcfunction(state, meta_sizeof); + lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); + luaL_register(state, "df", no_functions); { @@ -1398,6 +1494,9 @@ static void DoAttach(lua_State *state) // Render the type structure RenderTypeChildren(state, compound_identity::getTopScope()); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); + lua_setfield(state, -2, "sizeof"); + freeze_table(state, true, "df"); lua_remove(state, -2); lua_setmetatable(state, -2); From 6b2006361dfc2e976c70f8b5507acb9f8a7224db Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 23 Mar 2012 11:54:59 +0400 Subject: [PATCH 18/25] Add a _displace method that implements offsetting a pointer by an int. --- library/LuaWrapper.cpp | 75 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index a394b4cfd..9214a4c5d 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -68,6 +68,7 @@ inline void lua_swap(lua_State *state) { lua_insert(state, -2); } #define DFHACK_COMPARE_NAME "DFHack::ComparePtrs" #define DFHACK_TYPE_TOSTRING_NAME "DFHack::TypeToString" #define DFHACK_SIZEOF_NAME "DFHack::Sizeof" +#define DFHACK_DISPLACE_NAME "DFHack::Displace" /* * Upvalue: contents of DFHACK_TYPETABLE_NAME @@ -514,7 +515,8 @@ static void *get_object_internal(lua_State *state, type_identity *type, int val_ /** * Given a DF object reference or type, safely retrieve its identity pointer. */ -static type_identity *get_object_identity(lua_State *state, int objidx, const char *ctx) +static type_identity *get_object_identity(lua_State *state, int objidx, + const char *ctx, bool allow_type = false) { if (!lua_getmetatable(state, objidx)) luaL_error(state, "Invalid object in %s", ctx); @@ -527,6 +529,9 @@ static type_identity *get_object_identity(lua_State *state, int objidx, const ch } else { + if (!allow_type) + luaL_error(state, "Object expected in %s", ctx); + lua_pushvalue(state, objidx); LookupInTable(state, DFHACK_TYPEID_TABLE_NAME); } @@ -604,7 +609,7 @@ static int meta_sizeof(lua_State *state) return 2; } - type_identity *id = get_object_identity(state, 1, "df.sizeof()"); + type_identity *id = get_object_identity(state, 1, "df.sizeof()", true); lua_pushinteger(state, id->byte_size()); @@ -617,6 +622,62 @@ static int meta_sizeof(lua_State *state) return 1; } +/** + * Method: displace for DF object references. + * + * Returns: a reference with the same type, but modified address + */ +static int meta_displace(lua_State *state) +{ + int argc = lua_gettop(state); + + bool has_step = (argc >= 3); + if ((argc < 2 || argc > 3) || + !lua_isnumber(state, 2) || + (has_step && !lua_isnumber(state, 3))) + { + luaL_error(state, "Usage: object:_displace(index[,step]) or df._displace(object,...)"); + } + + int index = lua_tointeger(state, 2); + int step = has_step ? lua_tointeger(state, 3) : 1; + + // Two special cases: nil and lightuserdata for NULL and void* + if (lua_isnil(state, 1)) + { + lua_pushnil(state); + return 1; + } + + if (lua_islightuserdata(state, 1)) + { + if (!has_step) + luaL_error(state, "Step is mandatory in _displace of void*"); + + auto ptr = (uint8_t*)lua_touserdata(state, 1); + lua_pushlightuserdata(state, ptr + index*step); + return 1; + } + + type_identity *id = get_object_identity(state, 1, "df._displace()"); + + if (!has_step) + step = id->byte_size(); + + if (index == 0 || step == 0) + { + lua_pushvalue(state, 1); + } + else + { + auto ptr = (uint8_t*)get_object_ref(state, 1); + lua_getmetatable(state, 1); + push_object_ref(state, ptr + index*step); + } + + return 1; +} + /** * Resolve the field name in UPVAL_FIELDTABLE, die if not found. */ @@ -1063,9 +1124,14 @@ static void SetPtrMethods(lua_State *state, int meta_idx, int read_idx) lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); lua_setfield(state, meta_idx, "sizeof"); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_DISPLACE_NAME); + lua_setfield(state, meta_idx, "_displace"); + EnableMetaField(state, read_idx, "_type"); EnableMetaField(state, read_idx, "_kind"); + EnableMetaField(state, read_idx, "sizeof"); + EnableMetaField(state, read_idx, "_displace"); } /** @@ -1485,6 +1551,9 @@ static void DoAttach(lua_State *state) lua_pushcfunction(state, meta_sizeof); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); + lua_pushcfunction(state, meta_displace); + lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_DISPLACE_NAME); + luaL_register(state, "df", no_functions); { @@ -1496,6 +1565,8 @@ static void DoAttach(lua_State *state) lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_SIZEOF_NAME); lua_setfield(state, -2, "sizeof"); + lua_getfield(state, LUA_REGISTRYINDEX, DFHACK_DISPLACE_NAME); + lua_setfield(state, -2, "_displace"); freeze_table(state, true, "df"); lua_remove(state, -2); From 2b1f8aa2bbb2bc67a661c2930f6d379bae55ca75 Mon Sep 17 00:00:00 2001 From: Alexander Gavrilov Date: Fri, 23 Mar 2012 12:55:29 +0400 Subject: [PATCH 19/25] Add a _field method that returns refs to struct and container items. Hack: allocate ad-hoc pointer identities as full lua userdata. --- library/LuaWrapper.cpp | 145 +++++++++++++++++++++++++++++++-- library/include/DataIdentity.h | 3 + 2 files changed, 142 insertions(+), 6 deletions(-) diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index 9214a4c5d..d6edef4e2 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -63,6 +63,11 @@ inline void lua_swap(lua_State *state) { lua_insert(state, -2); } */ #define DFHACK_ENUM_TABLE_NAME "DFHack::DFEnums" +/* + * Registry name: hash of pointer target identity <-> adhoc pointer identity userdata. + */ +#define DFHACK_PTR_IDTABLE_NAME "DFHack::PtrDFTypes" + // Function registry names #define DFHACK_CHANGEERROR_NAME "DFHack::ChangeError" #define DFHACK_COMPARE_NAME "DFHack::ComparePtrs" @@ -150,6 +155,8 @@ void *DFHack::GetDFObject(lua_State *state, type_identity *type, int val_index) return get_object_internal(state, type, val_index, false); } +static void push_adhoc_pointer(lua_State *state, void *ptr, type_identity *target); + /************************************** * Identity object read/write methods * **************************************/ @@ -260,6 +267,13 @@ int container_identity::lua_item_count(lua_State *state, void *ptr) return item_count(ptr); } +void container_identity::lua_item_reference(lua_State *state, int fname_idx, void *ptr, int idx) +{ + auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + void *pitem = item_pointer(id, ptr, idx); + push_object_internal(state, id, pitem); +} + void container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) { auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); @@ -274,6 +288,13 @@ void container_identity::lua_item_write(lua_State *state, int fname_idx, void *p id->lua_write(state, fname_idx, pitem, val_index); } +void ptr_container_identity::lua_item_reference(lua_State *state, int fname_idx, void *ptr, int idx) +{ + auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); + void *pitem = item_pointer(id, ptr, idx); + push_adhoc_pointer(state, pitem, id); +} + void ptr_container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) { auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); @@ -288,6 +309,11 @@ void ptr_container_identity::lua_item_write(lua_State *state, int fname_idx, voi df::pointer_identity::lua_write(state, fname_idx, pitem, id, val_index); } +void bit_container_identity::lua_item_reference(lua_State *state, int, void *, int) +{ + lua_pushnil(state); +} + void bit_container_identity::lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx) { lua_pushboolean(state, get_item(ptr, idx)); @@ -761,6 +787,37 @@ static void read_field(lua_State *state, const struct_field_info *field, void *p lua_pushnil(state); } +static void field_reference(lua_State *state, const struct_field_info *field, void *ptr) +{ + switch (field->mode) + { + case struct_field_info::PRIMITIVE: + case struct_field_info::SUBSTRUCT: + push_object_internal(state, field->type, ptr); + return; + + case struct_field_info::POINTER: + push_adhoc_pointer(state, ptr, field->type); + return; + + case struct_field_info::CONTAINER: + read_field(state, field, ptr); + return; + + case struct_field_info::STATIC_STRING: + case struct_field_info::STATIC_ARRAY: + case struct_field_info::STL_VECTOR_PTR: + GetAdHocMetatable(state, field); + push_object_ref(state, ptr); + return; + + case struct_field_info::END: + break; + } + + lua_pushnil(state); +} + static void write_field(lua_State *state, const struct_field_info *field, void *ptr, int value_idx) { switch (field->mode) @@ -842,6 +899,21 @@ static int meta_struct_index(lua_State *state) return 1; } +/** + * Method: _field for structures. + */ +static int meta_struct_field_reference(lua_State *state) +{ + if (lua_gettop(state) != 2) + luaL_error(state, "Usage: object._field(name)"); + uint8_t *ptr = get_object_addr(state, 1, 2, "reference"); + auto field = (struct_field_info*)find_field(state, 2, "reference"); + if (!field) + field_error(state, 2, "builtin property", "reference"); + field_reference(state, field, ptr + field->offset); + return 1; +} + /** * Metamethod: __newindex for structures. */ @@ -900,17 +972,17 @@ static int meta_container_len(lua_State *state) * - Numbers are indices and handled directly. * - NULL userdata are metafields; push and exit; */ -static int lookup_container_field(lua_State *state, int field, bool write = false) +static int lookup_container_field(lua_State *state, int field, const char *mode = NULL) { if (lua_type(state, field) == LUA_TNUMBER) return field; - lookup_field(state, field, write ? "write" : "read"); + lookup_field(state, field, mode ? mode : "read"); if (lua_isuserdata(state, -1) && !lua_touserdata(state, -1)) { - if (write) - field_error(state, field, "builtin property", "write"); + if (mode) + field_error(state, field, "builtin property", mode); lua_pop(state, 1); get_metafield(state); @@ -953,13 +1025,30 @@ static int meta_container_index(lua_State *state) return 1; } +/** + * Method: _field for containers. + */ +static int meta_container_field_reference(lua_State *state) +{ + if (lua_gettop(state) != 2) + luaL_error(state, "Usage: object._field(index)"); + uint8_t *ptr = get_object_addr(state, 1, 2, "reference"); + int iidx = lookup_container_field(state, 2, "reference"); + + auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); + int len = id->lua_item_count(state, ptr); + int idx = check_container_index(state, len, 2, iidx, "reference"); + id->lua_item_reference(state, 2, ptr, idx); + return 1; +} + /** * Metamethod: __index for containers. */ static int meta_container_newindex(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "write"); - int iidx = lookup_container_field(state, 2, true); + int iidx = lookup_container_field(state, 2, "write"); auto id = (container_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); int len = id->lua_item_count(state, ptr); @@ -1017,7 +1106,7 @@ static int meta_bitfield_index(lua_State *state) static int meta_bitfield_newindex(lua_State *state) { uint8_t *ptr = get_object_addr(state, 1, 2, "write"); - int iidx = lookup_container_field(state, 2, true); + int iidx = lookup_container_field(state, 2, "write"); auto id = (bitfield_identity*)lua_touserdata(state, UPVAL_CONTAINER_ID); @@ -1130,6 +1219,8 @@ static void SetPtrMethods(lua_State *state, int meta_idx, int read_idx) EnableMetaField(state, read_idx, "_type"); EnableMetaField(state, read_idx, "_kind"); + EnableMetaField(state, read_idx, "_field"); + EnableMetaField(state, read_idx, "sizeof"); EnableMetaField(state, read_idx, "_displace"); } @@ -1287,6 +1378,8 @@ static void MakeContainerMetatable(lua_State *state, container_identity *type, SetContainerMethod(state, base+1, base+2, meta_container_index, "__index", type, item, count); SetContainerMethod(state, base+1, base+2, meta_container_newindex, "__newindex", type, item, count); + SetContainerMethod(state, base+1, base+2, meta_container_field_reference, "_field", type, item, count); + AttachEnumKeys(state, base, ienum); } @@ -1324,6 +1417,7 @@ void struct_identity::build_metatable(lua_State *state) { int base = lua_gettop(state); MakeFieldMetatable(state, this, meta_struct_index, meta_struct_newindex); + SetStructMethod(state, base+1, base+2, meta_struct_field_reference, "_field"); SetPtrMethods(state, base+1, base+2); } @@ -1360,6 +1454,11 @@ static void GetAdHocMetatable(lua_State *state, const struct_field_info *field) break; } + case struct_field_info::STATIC_STRING: + MakeContainerMetatable(state, &df::buffer_container_identity::base_instance, + &df::identity_traits::identity, field->count, NULL); + break; + case struct_field_info::STATIC_ARRAY: MakeContainerMetatable(state, &df::buffer_container_identity::base_instance, field->type, field->count, field->eid); @@ -1380,6 +1479,37 @@ static void GetAdHocMetatable(lua_State *state, const struct_field_info *field) } } +static void push_adhoc_pointer(lua_State *state, void *ptr, type_identity *target) +{ + if (!target) + { + push_object_internal(state, &df::identity_traits::identity, ptr); + return; + } + + LookupInTable(state, target, DFHACK_PTR_IDTABLE_NAME); + + type_identity *id = (type_identity*)lua_touserdata(state, -1); + lua_pop(state, 1); + + if (!id) + { + /* + * HACK: relies on + * 1) pointer_identity destructor being no-op + * 2) lua gc never moving objects in memory + */ + + void *newobj = lua_newuserdata(state, sizeof(pointer_identity)); + id = new (newobj) pointer_identity(target); + + SaveInTable(state, target, DFHACK_PTR_IDTABLE_NAME); + lua_pop(state, 1); + } + + push_object_internal(state, id, ptr); +} + /* * Recursive walk of scopes to construct the df... tree. */ @@ -1533,6 +1663,9 @@ static void DoAttach(lua_State *state) { int base = lua_gettop(state); + lua_newtable(state); + lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_PTR_IDTABLE_NAME); + lua_newtable(state); lua_setfield(state, LUA_REGISTRYINDEX, DFHACK_TYPEID_TABLE_NAME); diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index 7b576b117..808634798 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -86,6 +86,7 @@ namespace DFHack int lua_item_count(lua_State *state, void *ptr); + virtual void lua_item_reference(lua_State *state, int fname_idx, void *ptr, int idx); virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); @@ -104,6 +105,7 @@ namespace DFHack std::string getFullName(type_identity *item); + virtual void lua_item_reference(lua_State *state, int fname_idx, void *ptr, int idx); virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); }; @@ -117,6 +119,7 @@ namespace DFHack std::string getFullName(type_identity *item); + virtual void lua_item_reference(lua_State *state, int fname_idx, void *ptr, int idx); virtual void lua_item_read(lua_State *state, int fname_idx, void *ptr, int idx); virtual void lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index); From e10b1a50a352fe63ca674310673c8b19dcee48fa Mon Sep 17 00:00:00 2001 From: Warmist Date: Fri, 23 Mar 2012 11:07:54 +0200 Subject: [PATCH 20/25] Onfunction start --- plugins/Dfusion/luafiles/init.lua | 3 ++- plugins/Dfusion/luafiles/onfunction/locations.lua | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/plugins/Dfusion/luafiles/init.lua b/plugins/Dfusion/luafiles/init.lua index ccb51f572..97766a978 100644 --- a/plugins/Dfusion/luafiles/init.lua +++ b/plugins/Dfusion/luafiles/init.lua @@ -72,7 +72,8 @@ table.insert(plugins,{"adv_tools","some tools for (mainly) advneturer hacking"}) table.insert(plugins,{"triggers","a function calling plug (discontinued...)"}) table.insert(plugins,{"migrants","multi race imigrations"}) -table.insert(plugins,{"onfunction","run lua on some df function"})--]=] +--]=] +table.insert(plugins,{"onfunction","run lua on some df function"}) table.insert(plugins,{"editor","edit internals of df",EditDF}) table.insert(plugins,{"saves","run current worlds's init.lua",RunSaved}) loadall(plugins) diff --git a/plugins/Dfusion/luafiles/onfunction/locations.lua b/plugins/Dfusion/luafiles/onfunction/locations.lua index 57043b7a3..7849fc45d 100644 --- a/plugins/Dfusion/luafiles/onfunction/locations.lua +++ b/plugins/Dfusion/luafiles/onfunction/locations.lua @@ -1,5 +1,5 @@ if WINDOWS then --windows function defintions - onfunction.AddFunction(0x55499D+offsets.base(),"Move") --on creature move found with "watch mem=xcoord" + --[=[onfunction.AddFunction(0x55499D+offsets.base(),"Move") --on creature move found with "watch mem=xcoord" onfunction.AddFunction(0x275933+offsets.base(),"Die",{creature="edi"}) --on creature death? found by watching dead flag then stepping until new function onfunction.AddFunction(0x2c1834+offsets.base(),"CreateCreature",{protocreature="eax"}) --arena onfunction.AddFunction(0x349640+offsets.base(),"AddItem",{item="esp"}) --or esp @@ -7,8 +7,9 @@ if WINDOWS then --windows function defintions onfunction.AddFunction(0x3d4301+offsets.base(),"Make_Item",{item_type="esp"}) onfunction.AddFunction(0x5af826+offsets.base(),"Hurt",{target="esi",attacker={off=0x74,rtype=DWORD,reg="esp"}}) onfunction.AddFunction(0x3D5886+offsets.base(),"Flip",{building="esi"}) - onfunction.AddFunction(0x35E340+offsets.base(),"ItemCreate") + onfunction.AddFunction(0x35E340+offsets.base(),"ItemCreate")--]=] + onfunction.AddFunction(4B34B6+offsets.base(),"ReactionFinish") --esp item. Ecx creature, edx? else --linux - onfunction.AddFunction(0x899befe+offsets.base(),"Move") -- found out by attaching watch... - onfunction.AddFunction(0x850eecd+offsets.base(),"Die",{creature="ebx"}) -- same + --[=[onfunction.AddFunction(0x899befe+offsets.base(),"Move") -- found out by attaching watch... + onfunction.AddFunction(0x850eecd+offsets.base(),"Die",{creature="ebx"}) -- same--]=] end From 90de6a1b62584289c173c82b7971c069d5e1c863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Fri, 23 Mar 2012 22:59:40 +0100 Subject: [PATCH 21/25] Track submodules --- library/xml | 2 +- plugins/stonesense | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/xml b/library/xml index b28296a8c..1a6d5acf0 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit b28296a8c0c2e5aaa522840598738109018f0146 +Subproject commit 1a6d5acf09ac4c11da62c5ed11a567e480d9fb59 diff --git a/plugins/stonesense b/plugins/stonesense index 719dbc048..7525c0030 160000 --- a/plugins/stonesense +++ b/plugins/stonesense @@ -1 +1 @@ -Subproject commit 719dbc048a55ba1def2ce21e9cd29e33dbe833ce +Subproject commit 7525c003089367823183eaf5093a90271a5eb9b4 From 078caf363f693a8f7744de85a0539de4f68346cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Fri, 23 Mar 2012 23:49:28 +0100 Subject: [PATCH 22/25] Move merged plugins into main plugin folder. --- plugins/CMakeLists.txt | 15 +++-------- plugins/{cursecheck => }/cursecheck.cpp | 0 plugins/cursecheck/CMakeLists.txt | 33 ------------------------- plugins/cursecheck/cursecheck.h | 1 - plugins/{liquidsgo => }/liquidsgo.cpp | 0 plugins/liquidsgo/CMakeLists.txt | 33 ------------------------- plugins/liquidsgo/liquidsgo.h | 1 - 7 files changed, 4 insertions(+), 79 deletions(-) rename plugins/{cursecheck => }/cursecheck.cpp (100%) delete mode 100644 plugins/cursecheck/CMakeLists.txt delete mode 100644 plugins/cursecheck/cursecheck.h rename plugins/{liquidsgo => }/liquidsgo.cpp (100%) delete mode 100644 plugins/liquidsgo/CMakeLists.txt delete mode 100644 plugins/liquidsgo/liquidsgo.h diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 36efb82e1..f3afa3f23 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -65,6 +65,10 @@ OPTION(BUILD_SUPPORTED "Build the supported plugins (reveal, probe, etc.)." ON) if (BUILD_SUPPORTED) DFHACK_PLUGIN(reveal reveal.cpp) DFHACK_PLUGIN(probe probe.cpp) + # this is a plugin which helps detect cursed creatures (vampires, necromancers, werebeasts, ...) + DFHACK_PLUGIN(cursecheck cursecheck.cpp) + # alternative version of liquids which can be used non-interactively after configuring it + DFHACK_PLUGIN(liquidsgo liquidsgo.cpp) DFHACK_PLUGIN(drybuckets drybuckets.cpp) DFHACK_PLUGIN(getplants getplants.cpp) DFHACK_PLUGIN(plants plants.cpp) @@ -109,14 +113,3 @@ if(BUILD_SKELETON) add_subdirectory(skeleton) endif() -# this is a plugin which helps detect cursed creatures (vampires, necromancers, werebeasts, ...) -OPTION(BUILD_CURSECHECK "Build the cursecheck plugin." ON) -if(BUILD_CURSECHECK) - add_subdirectory(cursecheck) -endif() - -# alternative version of liquids which can be used non-interactively after configuring it -OPTION(BUILD_LIQUIDSGO "Build the liquidsgo plugin." ON) -if(BUILD_LIQUIDSGO) - add_subdirectory(liquidsgo) -endif() diff --git a/plugins/cursecheck/cursecheck.cpp b/plugins/cursecheck.cpp similarity index 100% rename from plugins/cursecheck/cursecheck.cpp rename to plugins/cursecheck.cpp diff --git a/plugins/cursecheck/CMakeLists.txt b/plugins/cursecheck/CMakeLists.txt deleted file mode 100644 index 0c94976db..000000000 --- a/plugins/cursecheck/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -PROJECT (cursecheck) -# A list of source files -SET(PROJECT_SRCS - cursecheck.cpp -) -# A list of headers -SET(PROJECT_HDRS - cursecheck.h -) -SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE) - -# mash them together (headers are marked as headers and nothing will try to compile them) -LIST(APPEND PROJECT_SRCS ${PROJECT_HDRS}) - -# option to use a thread for no particular reason -#OPTION(SKELETON_THREAD "Use threads in the skeleton plugin." ON) -#linux -IF(UNIX) - add_definitions(-DLINUX_BUILD) - SET(PROJECT_LIBS - # add any extra linux libs here - ${PROJECT_LIBS} - ) -# windows -ELSE(UNIX) - SET(PROJECT_LIBS - # add any extra linux libs here - ${PROJECT_LIBS} - $(NOINHERIT) - ) -ENDIF(UNIX) -# this makes sure all the stuff is put in proper places and linked to dfhack -DFHACK_PLUGIN(cursecheck ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS}) diff --git a/plugins/cursecheck/cursecheck.h b/plugins/cursecheck/cursecheck.h deleted file mode 100644 index 7b9637ef9..000000000 --- a/plugins/cursecheck/cursecheck.h +++ /dev/null @@ -1 +0,0 @@ -#pragma once \ No newline at end of file diff --git a/plugins/liquidsgo/liquidsgo.cpp b/plugins/liquidsgo.cpp similarity index 100% rename from plugins/liquidsgo/liquidsgo.cpp rename to plugins/liquidsgo.cpp diff --git a/plugins/liquidsgo/CMakeLists.txt b/plugins/liquidsgo/CMakeLists.txt deleted file mode 100644 index cdae8efd7..000000000 --- a/plugins/liquidsgo/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -PROJECT (liquidsgo) -# A list of source files -SET(PROJECT_SRCS - liquidsgo.cpp -) -# A list of headers -SET(PROJECT_HDRS - liquidsgo.h -) -SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE) - -# mash them together (headers are marked as headers and nothing will try to compile them) -LIST(APPEND PROJECT_SRCS ${PROJECT_HDRS}) - -# option to use a thread for no particular reason -#OPTION(SKELETON_THREAD "Use threads in the skeleton plugin." ON) -#linux -IF(UNIX) - add_definitions(-DLINUX_BUILD) - SET(PROJECT_LIBS - # add any extra linux libs here - ${PROJECT_LIBS} - ) -# windows -ELSE(UNIX) - SET(PROJECT_LIBS - # add any extra linux libs here - ${PROJECT_LIBS} - $(NOINHERIT) - ) -ENDIF(UNIX) -# this makes sure all the stuff is put in proper places and linked to dfhack -DFHACK_PLUGIN(liquidsgo ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS}) diff --git a/plugins/liquidsgo/liquidsgo.h b/plugins/liquidsgo/liquidsgo.h deleted file mode 100644 index 7b9637ef9..000000000 --- a/plugins/liquidsgo/liquidsgo.h +++ /dev/null @@ -1 +0,0 @@ -#pragma once \ No newline at end of file From addb5c87aa73fa08258923444a031e011f2e8a50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 24 Mar 2012 00:13:16 +0100 Subject: [PATCH 23/25] liquids vs liquidsgo: FIGHT! liquidsgo WINS, renamed to liquids to not confuse users. --- plugins/Brushes.h | 178 +++++++++++ plugins/CMakeLists.txt | 2 - plugins/liquids.cpp | 640 ++++++++++++++++--------------------- plugins/liquidsgo.cpp | 709 ----------------------------------------- 4 files changed, 457 insertions(+), 1072 deletions(-) create mode 100644 plugins/Brushes.h delete mode 100644 plugins/liquidsgo.cpp diff --git a/plugins/Brushes.h b/plugins/Brushes.h new file mode 100644 index 000000000..73c3a05a9 --- /dev/null +++ b/plugins/Brushes.h @@ -0,0 +1,178 @@ +#pragma once + +typedef vector coord_vec; +class Brush +{ +public: + virtual ~Brush(){}; + virtual coord_vec points(MapExtras::MapCache & mc,DFHack::DFCoord start) = 0; +}; +/** + * generic 3D rectangle brush. you can specify the dimensions of + * the rectangle and optionally which tile is its 'center' + */ +class RectangleBrush : public Brush +{ +public: + RectangleBrush(int x, int y, int z = 1, int centerx = -1, int centery = -1, int centerz = -1) + { + if(centerx == -1) + cx_ = x/2; + else + cx_ = centerx; + if(centery == -1) + cy_ = y/2; + else + cy_ = centery; + if(centerz == -1) + cz_ = z/2; + else + cz_ = centerz; + x_ = x; + y_ = y; + z_ = z; + }; + coord_vec points(MapExtras::MapCache & mc, DFHack::DFCoord start) + { + coord_vec v; + DFHack::DFCoord iterstart(start.x - cx_, start.y - cy_, start.z - cz_); + DFHack::DFCoord iter = iterstart; + for(int xi = 0; xi < x_; xi++) + { + for(int yi = 0; yi < y_; yi++) + { + for(int zi = 0; zi < z_; zi++) + { + if(mc.testCoord(iter)) + v.push_back(iter); + iter.z++; + } + iter.z = iterstart.z; + iter.y++; + } + iter.y = iterstart.y; + iter.x ++; + } + return v; + }; + ~RectangleBrush(){}; +private: + int x_, y_, z_; + int cx_, cy_, cz_; +}; + +/** + * stupid block brush, legacy. use when you want to apply something to a whole DF map block. + */ +class BlockBrush : public Brush +{ +public: + BlockBrush(){}; + ~BlockBrush(){}; + coord_vec points(MapExtras::MapCache & mc, DFHack::DFCoord start) + { + coord_vec v; + DFHack::DFCoord blockc = start / 16; + DFHack::DFCoord iterc = blockc * 16; + if( !mc.testCoord(start) ) + return v; + auto starty = iterc.y; + for(int xi = 0; xi < 16; xi++) + { + for(int yi = 0; yi < 16; yi++) + { + v.push_back(iterc); + iterc.y++; + } + iterc.y = starty; + iterc.x ++; + } + return v; + }; +}; + +/** + * Column from a position through open space tiles + * example: create a column of magma + */ +class ColumnBrush : public Brush +{ +public: + ColumnBrush(){}; + ~ColumnBrush(){}; + coord_vec points(MapExtras::MapCache & mc, DFHack::DFCoord start) + { + coord_vec v; + bool juststarted = true; + while (mc.testCoord(start)) + { + df::tiletype tt = mc.tiletypeAt(start); + if(DFHack::LowPassable(tt) || juststarted && DFHack::HighPassable(tt)) + { + v.push_back(start); + juststarted = false; + start.z++; + } + else break; + } + return v; + }; +}; + +/** + * Flood-fill water tiles from cursor (for wclean) + * example: remove salt flag from a river + */ +class FloodBrush : public Brush +{ +public: + FloodBrush(Core *c){c_ = c;}; + ~FloodBrush(){}; + coord_vec points(MapExtras::MapCache & mc, DFHack::DFCoord start) + { + coord_vec v; + + std::stack to_flood; + to_flood.push(start); + + std::set seen; + + while (!to_flood.empty()) { + DFCoord xy = to_flood.top(); + to_flood.pop(); + + df::tile_designation des = mc.designationAt(xy); + + if (seen.find(xy) == seen.end() + && des.bits.flow_size + && des.bits.liquid_type == tile_liquid::Water) { + v.push_back(xy); + seen.insert(xy); + + maybeFlood(DFCoord(xy.x - 1, xy.y, xy.z), to_flood, mc); + maybeFlood(DFCoord(xy.x + 1, xy.y, xy.z), to_flood, mc); + maybeFlood(DFCoord(xy.x, xy.y - 1, xy.z), to_flood, mc); + maybeFlood(DFCoord(xy.x, xy.y + 1, xy.z), to_flood, mc); + + df::tiletype tt = mc.tiletypeAt(xy); + if (LowPassable(tt)) + { + maybeFlood(DFCoord(xy.x, xy.y, xy.z - 1), to_flood, mc); + } + if (HighPassable(tt)) + { + maybeFlood(DFCoord(xy.x, xy.y, xy.z + 1), to_flood, mc); + } + } + } + + return v; + } +private: + void maybeFlood(DFCoord c, std::stack &to_flood, MapExtras::MapCache &mc) { + if (mc.testCoord(c)) { + to_flood.push(c); + } + } + Core *c_; +}; diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index f3afa3f23..cb38b884e 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -67,8 +67,6 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(probe probe.cpp) # this is a plugin which helps detect cursed creatures (vampires, necromancers, werebeasts, ...) DFHACK_PLUGIN(cursecheck cursecheck.cpp) - # alternative version of liquids which can be used non-interactively after configuring it - DFHACK_PLUGIN(liquidsgo liquidsgo.cpp) DFHACK_PLUGIN(drybuckets drybuckets.cpp) DFHACK_PLUGIN(getplants getplants.cpp) DFHACK_PLUGIN(plants plants.cpp) diff --git a/plugins/liquids.cpp b/plugins/liquids.cpp index 3bf67f4d2..5f4d5b4fe 100644 --- a/plugins/liquids.cpp +++ b/plugins/liquids.cpp @@ -1,3 +1,25 @@ +// plugin liquids +// +// This is a rewrite of the liquids module which can also be used non-interactively (hotkey). +// First the user sets the mode and other parameters with the interactive command liqiudsgo +// just like in the original liquids module. +// They are stored in statics to allow being used after the interactive session was closed. +// After defining an action the non-interactive command liquids-here can be used to call the +// execute method without the necessity to go back to the console. This allows convenient painting +// of liquids and obsidian using the ingame cursor and a hotkey. +// +// Commands: +// liquids - basically the original liquids with the map changing stuff moved to an execute method +// liquids-here - runs the execute method with the last settings from liquids +// (intended to be mapped to a hotkey) +// Options: +// ?, help - print some help +// +// TODO: +// - maybe allow all parameters be passed as command line options? tedious parsing but might be useful +// - grab the code from digcircle to get a circle brush - could be nice when painting with obsidian +// - maybe store the last parameters in a file to make them persistent after dfhack is closed? + #include #include #include @@ -19,14 +41,16 @@ using std::set; #include "modules/Gui.h" #include "TileTypes.h" #include "modules/MapCache.h" +#include "Brushes.h" using namespace MapExtras; using namespace DFHack; using namespace df::enums; -typedef vector coord_vec; CommandHistory liquids_hist; command_result df_liquids (color_ostream &out, vector & parameters); +command_result df_liquids_here (color_ostream &out, vector & parameters); +command_result df_liquids_execute (color_ostream &out); DFHACK_PLUGIN("liquids"); @@ -34,7 +58,14 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector to_flood; - to_flood.push(start); - - std::set seen; - - while (!to_flood.empty()) { - DFCoord xy = to_flood.top(); - to_flood.pop(); - - df::tile_designation des = mc.designationAt(xy); - - if (seen.find(xy) == seen.end() - && des.bits.flow_size - && des.bits.liquid_type == tile_liquid::Water) { - v.push_back(xy); - seen.insert(xy); - - maybeFlood(DFCoord(xy.x - 1, xy.y, xy.z), to_flood, mc); - maybeFlood(DFCoord(xy.x + 1, xy.y, xy.z), to_flood, mc); - maybeFlood(DFCoord(xy.x, xy.y - 1, xy.z), to_flood, mc); - maybeFlood(DFCoord(xy.x, xy.y + 1, xy.z), to_flood, mc); - - df::tiletype tt = mc.tiletypeAt(xy); - if (LowPassable(tt)) - { - maybeFlood(DFCoord(xy.x, xy.y, xy.z - 1), to_flood, mc); - } - if (HighPassable(tt)) - { - maybeFlood(DFCoord(xy.x, xy.y, xy.z + 1), to_flood, mc); - } - } - } - - return v; - } -private: - void maybeFlood(DFCoord c, std::stack &to_flood, MapCache &mc) { - if (mc.testCoord(c)) { - to_flood.push(c); - } - } - Core *c_; -}; +// static stuff to be remembered between sessions +static string brushname = "point"; +static string mode="magma"; +static string flowmode="f+"; +static string setmode ="s."; +static unsigned int amount = 7; +static int width = 1, height = 1, z_levels = 1; command_result df_liquids (color_ostream &out_, vector & parameters) { - int32_t x,y,z; - - assert(out_.is_console()); + if(!out_.is_console()) + return CR_FAILURE; Console &out = static_cast(out_); for(size_t i = 0; i < parameters.size();i++) { if(parameters[i] == "help" || parameters[i] == "?") { - out.print("This tool allows placing magma, water and other similar things.\n" - "It is interactive and further help is available when you run it.\n" - ); + out.print( "This tool allows placing magma, water and other similar things.\n" + "It is interactive and further help is available when you run it.\n" + "The settings will be remembered until dfhack is closed and you can call\n" + "'liquids-here' (mapped to a hotkey) to paint liquids at the cursor position\n" + "without the need to go back to the dfhack console.\n"); return CR_OK; } } @@ -244,23 +108,22 @@ command_result df_liquids (color_ostream &out_, vector & parameters) return CR_FAILURE; } - Brush * brush = new RectangleBrush(1,1); - string brushname = "point"; bool end = false; + out << "Welcome to the liquid spawner.\nType 'help' or '?' for a list of available commands, 'q' to quit.\nPress return after a command to confirm." << std::endl; - string mode="magma"; - string flowmode="f+"; - string setmode ="s."; - unsigned int amount = 7; - int width = 1, height = 1, z_levels = 1; while(!end) { string command = ""; + std::stringstream str; - str <<"[" << mode << ":" << brushname << ":" << amount << ":" << flowmode << ":" << setmode << "]#"; + str <<"[" << mode << ":" << brushname; + if (brushname == "range") + str << "(w" << width << ":h" << height << ":z" << z_levels << ")"; + str << ":" << amount << ":" << flowmode << ":" << setmode << "]#"; if(out.lineedit(str.str(),command,liquids_hist) == -1) return CR_FAILURE; + if(command=="help" || command == "?") { out << "Modes:" << endl @@ -296,6 +159,7 @@ command_result df_liquids (color_ostream &out_, vector & parameters) << endl << "Usage: point the DF cursor at a tile you want to modify" << endl << "and use the commands available :)" << endl; + out << endl << "Settings will be remembered until you quit DF. You can call liquids-here to execute the last configured action. Useful in combination with keybindings." << endl; } else if(command == "m") { @@ -327,9 +191,7 @@ command_result df_liquids (color_ostream &out_, vector & parameters) } else if(command == "point" || command == "p") { - delete brush; brushname = "point"; - brush = new RectangleBrush(1,1); } else if(command == "range" || command == "r") { @@ -354,35 +216,27 @@ command_result df_liquids (color_ostream &out_, vector & parameters) range_hist.add(command); z_levels = command == "" ? z_levels : atoi (command.c_str()); if(z_levels < 1) z_levels = 1; - delete brush; if(width == 1 && height == 1 && z_levels == 1) { - brushname="point"; + brushname = "point"; } else { brushname = "range"; } - brush = new RectangleBrush(width,height,z_levels,0,0,0); } else if(command == "block") { - delete brush; brushname = "block"; - brush = new BlockBrush(); } else if(command == "column") { - delete brush; brushname = "column"; - brush = new ColumnBrush(); } - else if(command == "flood") - { - delete brush; - brushname = "flood"; - brush = new FloodBrush(&Core::getInstance()); - } + else if(command == "flood") + { + brushname = "flood"; + } else if(command == "q") { end = true; @@ -430,184 +284,248 @@ command_result df_liquids (color_ostream &out_, vector & parameters) amount = 7; else if(command.empty()) { - CoreSuspender suspend; + df_liquids_execute(out); + } + else + { + out << command << " : unknown command." << endl; + } + } + return CR_OK; +} + +command_result df_liquids_here (color_ostream &out, vector & parameters) +{ + for(size_t i = 0; i < parameters.size();i++) + { + if(parameters[i] == "help" || parameters[i] == "?") + { + out << "This command is supposed to be mapped to a hotkey." << endl; + out << "It will use the current/last parameters set in liquids." << endl; + return CR_OK; + } + } - do + out.print("Run liquids-here with these parameters: "); + out << "[" << mode << ":" << brushname; + if (brushname == "range") + out << "(w" << width << ":h" << height << ":z" << z_levels << ")"; + out << ":" << amount << ":" << flowmode << ":" << setmode << "]\n"; + + return df_liquids_execute(out); +} + +command_result df_liquids_execute(color_ostream &out) +{ + // create brush type depending on old parameters + Brush * brush; + + if (brushname == "point") + { + brush = new RectangleBrush(1,1,1,0,0,0); + //width = 1; + //height = 1; + //z_levels = 1; + } + else if (brushname == "range") + { + brush = new RectangleBrush(width,height,z_levels,0,0,0); + } + else if(brushname == "block") + { + brush = new BlockBrush(); + } + else if(brushname == "column") + { + brush = new ColumnBrush(); + } + else if(brushname == "flood") + { + brush = new FloodBrush(&Core::getInstance()); + } + else + { + // this should never happen! + out << "Old brushtype is invalid! Resetting to point brush.\n"; + brushname = "point"; + width = 1; + height = 1; + z_levels = 1; + brush = new RectangleBrush(width,height,z_levels,0,0,0); + } + + CoreSuspender suspend; + + do + { + if (!Maps::IsValid()) + { + out << "Can't see any DF map loaded." << endl; + break;; + } + int32_t x,y,z; + if(!Gui::getCursorCoords(x,y,z)) + { + out << "Can't get cursor coords! Make sure you have a cursor active in DF." << endl; + break; + } + out << "cursor coords: " << x << "/" << y << "/" << z << endl; + MapCache mcache; + DFHack::DFCoord cursor(x,y,z); + coord_vec all_tiles = brush->points(mcache,cursor); + out << "working..." << endl; + if(mode == "obsidian") + { + coord_vec::iterator iter = all_tiles.begin(); + while (iter != all_tiles.end()) + { + mcache.setTiletypeAt(*iter, tiletype::LavaWall); + mcache.setTemp1At(*iter,10015); + mcache.setTemp2At(*iter,10015); + df::tile_designation des = mcache.designationAt(*iter); + des.bits.flow_size = 0; + mcache.setDesignationAt(*iter, des); + iter ++; + } + } + if(mode == "obsidian_floor") + { + coord_vec::iterator iter = all_tiles.begin(); + while (iter != all_tiles.end()) + { + mcache.setTiletypeAt(*iter, findRandomVariant(tiletype::LavaFloor1)); + iter ++; + } + } + else if(mode == "riversource") + { + coord_vec::iterator iter = all_tiles.begin(); + while (iter != all_tiles.end()) + { + mcache.setTiletypeAt(*iter, tiletype::RiverSource); + + df::tile_designation a = mcache.designationAt(*iter); + a.bits.liquid_type = tile_liquid::Water; + a.bits.liquid_static = false; + a.bits.flow_size = 7; + mcache.setTemp1At(*iter,10015); + mcache.setTemp2At(*iter,10015); + mcache.setDesignationAt(*iter,a); + + Block * b = mcache.BlockAt((*iter)/16); + DFHack::t_blockflags bf = b->BlockFlags(); + bf.bits.liquid_1 = true; + bf.bits.liquid_2 = true; + b->setBlockFlags(bf); + + iter++; + } + } + else if(mode=="wclean") + { + coord_vec::iterator iter = all_tiles.begin(); + while (iter != all_tiles.end()) { - if (!Maps::IsValid()) + DFHack::DFCoord current = *iter; + df::tile_designation des = mcache.designationAt(current); + des.bits.water_salt = false; + des.bits.water_stagnant = false; + mcache.setDesignationAt(current,des); + iter++; + } + } + else if(mode== "magma" || mode== "water" || mode == "flowbits") + { + set seen_blocks; + coord_vec::iterator iter = all_tiles.begin(); + while (iter != all_tiles.end()) + { + DFHack::DFCoord current = *iter; // current tile coord + DFHack::DFCoord curblock = current /16; // current block coord + // check if the block is actually there + if(!mcache.BlockAt(curblock)) { - out << "Can't see any DF map loaded." << endl; - break;; + iter ++; + continue; } - if(!Gui::getCursorCoords(x,y,z)) + df::tile_designation des = mcache.designationAt(current); + df::tiletype tt = mcache.tiletypeAt(current); + // don't put liquids into places where they don't belong... + if(!DFHack::FlowPassable(tt)) { - out << "Can't get cursor coords! Make sure you have a cursor active in DF." << endl; - break; + iter++; + continue; } - out << "cursor coords: " << x << "/" << y << "/" << z << endl; - MapCache mcache; - DFHack::DFCoord cursor(x,y,z); - coord_vec all_tiles = brush->points(mcache,cursor); - out << "working..." << endl; - if(mode == "obsidian") + if(mode != "flowbits") { - coord_vec::iterator iter = all_tiles.begin(); - while (iter != all_tiles.end()) + if(setmode == "s.") { - mcache.setTiletypeAt(*iter, tiletype::LavaWall); - mcache.setTemp1At(*iter,10015); - mcache.setTemp2At(*iter,10015); - df::tile_designation des = mcache.designationAt(*iter); - des.bits.flow_size = 0; - mcache.setDesignationAt(*iter, des); - iter ++; + des.bits.flow_size = amount; } - } - if(mode == "obsidian_floor") - { - coord_vec::iterator iter = all_tiles.begin(); - while (iter != all_tiles.end()) + else if(setmode == "s+") { - mcache.setTiletypeAt(*iter, findRandomVariant(tiletype::LavaFloor1)); - iter ++; + if(des.bits.flow_size < amount) + des.bits.flow_size = amount; } - } - else if(mode == "riversource") - { - coord_vec::iterator iter = all_tiles.begin(); - while (iter != all_tiles.end()) + else if(setmode == "s-") { - mcache.setTiletypeAt(*iter, tiletype::RiverSource); - - df::tile_designation a = mcache.designationAt(*iter); - a.bits.liquid_type = tile_liquid::Water; - a.bits.liquid_static = false; - a.bits.flow_size = 7; - mcache.setTemp1At(*iter,10015); - mcache.setTemp2At(*iter,10015); - mcache.setDesignationAt(*iter,a); - - Block * b = mcache.BlockAt((*iter)/16); - DFHack::t_blockflags bf = b->BlockFlags(); - bf.bits.liquid_1 = true; - bf.bits.liquid_2 = true; - b->setBlockFlags(bf); - - iter++; + if (des.bits.flow_size > amount) + des.bits.flow_size = amount; } - } - else if(mode=="wclean") - { - coord_vec::iterator iter = all_tiles.begin(); - while (iter != all_tiles.end()) + if(amount != 0 && mode == "magma") { - DFHack::DFCoord current = *iter; - df::tile_designation des = mcache.designationAt(current); - des.bits.water_salt = false; - des.bits.water_stagnant = false; - mcache.setDesignationAt(current,des); - iter++; + des.bits.liquid_type = tile_liquid::Magma; + mcache.setTemp1At(current,12000); + mcache.setTemp2At(current,12000); } - } - else if(mode== "magma" || mode== "water" || mode == "flowbits") - { - set seen_blocks; - coord_vec::iterator iter = all_tiles.begin(); - while (iter != all_tiles.end()) + else if(amount != 0 && mode == "water") { - DFHack::DFCoord current = *iter; // current tile coord - DFHack::DFCoord curblock = current /16; // current block coord - // check if the block is actually there - if(!mcache.BlockAt(curblock)) - { - iter ++; - continue; - } - df::tile_designation des = mcache.designationAt(current); - df::tiletype tt = mcache.tiletypeAt(current); - // don't put liquids into places where they don't belong... - if(!DFHack::FlowPassable(tt)) - { - iter++; - continue; - } - if(mode != "flowbits") - { - if(setmode == "s.") - { - des.bits.flow_size = amount; - } - else if(setmode == "s+") - { - if(des.bits.flow_size < amount) - des.bits.flow_size = amount; - } - else if(setmode == "s-") - { - if (des.bits.flow_size > amount) - des.bits.flow_size = amount; - } - if(amount != 0 && mode == "magma") - { - des.bits.liquid_type = tile_liquid::Magma; - mcache.setTemp1At(current,12000); - mcache.setTemp2At(current,12000); - } - else if(amount != 0 && mode == "water") - { - des.bits.liquid_type = tile_liquid::Water; - mcache.setTemp1At(current,10015); - mcache.setTemp2At(current,10015); - } - else if(amount == 0 && (mode == "water" || mode == "magma")) - { - // reset temperature to sane default - mcache.setTemp1At(current,10015); - mcache.setTemp2At(current,10015); - } - mcache.setDesignationAt(current,des); - } - seen_blocks.insert(mcache.BlockAt(current / 16)); - iter++; + des.bits.liquid_type = tile_liquid::Water; + mcache.setTemp1At(current,10015); + mcache.setTemp2At(current,10015); } - set ::iterator biter = seen_blocks.begin(); - while (biter != seen_blocks.end()) + else if(amount == 0 && (mode == "water" || mode == "magma")) { - DFHack::t_blockflags bflags = (*biter)->BlockFlags(); - if(flowmode == "f+") - { - bflags.bits.liquid_1 = true; - bflags.bits.liquid_2 = true; - (*biter)->setBlockFlags(bflags); - } - else if(flowmode == "f-") - { - bflags.bits.liquid_1 = false; - bflags.bits.liquid_2 = false; - (*biter)->setBlockFlags(bflags); - } - else - { - out << "flow bit 1 = " << bflags.bits.liquid_1 << endl; - out << "flow bit 2 = " << bflags.bits.liquid_2 << endl; - } - biter ++; + // reset temperature to sane default + mcache.setTemp1At(current,10015); + mcache.setTemp2At(current,10015); } + mcache.setDesignationAt(current,des); + } + seen_blocks.insert(mcache.BlockAt(current / 16)); + iter++; + } + set ::iterator biter = seen_blocks.begin(); + while (biter != seen_blocks.end()) + { + DFHack::t_blockflags bflags = (*biter)->BlockFlags(); + if(flowmode == "f+") + { + bflags.bits.liquid_1 = true; + bflags.bits.liquid_2 = true; + (*biter)->setBlockFlags(bflags); + } + else if(flowmode == "f-") + { + bflags.bits.liquid_1 = false; + bflags.bits.liquid_2 = false; + (*biter)->setBlockFlags(bflags); } - if(mcache.WriteAll()) - out << "OK" << endl; else - out << "Something failed horribly! RUN!" << endl; - } while (0); + { + out << "flow bit 1 = " << bflags.bits.liquid_1 << endl; + out << "flow bit 2 = " << bflags.bits.liquid_2 << endl; + } + biter ++; + } } + if(mcache.WriteAll()) + out << "OK" << endl; else - { - out << command << " : unknown command." << endl; - } - } - - //cleanup - delete brush; + out << "Something failed horribly! RUN!" << endl; + } while (0); + // cleanup + delete brush; return CR_OK; } diff --git a/plugins/liquidsgo.cpp b/plugins/liquidsgo.cpp deleted file mode 100644 index edae580a6..000000000 --- a/plugins/liquidsgo.cpp +++ /dev/null @@ -1,709 +0,0 @@ -// plugin liquidsgo -// -// This is a rewrite of the liquids module which can also be used non-interactively (hotkey). -// First the user sets the mode and other parameters with the interactive command liqiudsgo -// just like in the original liquids module. -// They are stored in statics to allow being used after the interactive session was closed. -// After defining an action the non-interactive command liquidsgo-here can be used to call the -// execute method without the necessity to go back to the console. This allows convenient painting -// of liquids and obsidian using the ingame cursor and a hotkey. -// -// Commands: -// liquidsgo - basically the original liquids with the map changing stuff moved to an execute method -// liquidsgo-here - runs the execute method with the last settings from liquidsgo -// (intended to be mapped to a hotkey) -// Options: -// ?, help - print some help -// -// TODO: -// - maybe allow all parameters be passed as command line options? tedious parsing but might be useful -// - grab the code from digcircle to get a circle brush - could be nice when painting with obsidian -// - maybe store the last parameters in a file to make them persistent after dfhack is closed? - -#include -#include -#include -#include -#include -#include -#include -using std::vector; -using std::string; -using std::endl; -using std::set; - -#include "Core.h" -#include "Console.h" -#include "Export.h" -#include "PluginManager.h" -#include "modules/Vegetation.h" -#include "modules/Maps.h" -#include "modules/Gui.h" -#include "TileTypes.h" -#include "modules/MapCache.h" -using namespace MapExtras; -using namespace DFHack; -using namespace df::enums; -typedef vector coord_vec; - -CommandHistory liquidsgo_hist; - -command_result df_liquidsgo (color_ostream &out, vector & parameters); -command_result df_liquidsgo_here (color_ostream &out, vector & parameters); -command_result df_liquidsgo_execute (color_ostream &out); - -DFHACK_PLUGIN("liquidsgo"); - -DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) -{ - liquidsgo_hist.load("liquidsgo.history"); - commands.clear(); - commands.push_back(PluginCommand( - "liquidsgo", "Place magma, water or obsidian.", - df_liquidsgo, true)); // interactive, needs console for prompt - commands.push_back(PluginCommand( - "liquidsgo-here", "Use settings from liquidsgo at cursor position.", - df_liquidsgo_here, Gui::cursor_hotkey, // non-interactive, needs ingame cursor - " Identical to pressing enter in liquidsgo, intended for use as keybinding.\n" - " Can (but doesn't need to) be called while liquidsgo is running in the console.")); - return CR_OK; -} - -DFhackCExport command_result plugin_shutdown ( color_ostream &out ) -{ - liquidsgo_hist.save("liquidsgo.history"); - return CR_OK; -} - -class Brush -{ -public: - virtual ~Brush(){}; - virtual coord_vec points(MapCache & mc,DFHack::DFCoord start) = 0; -}; -/** - * generic 3D rectangle brush. you can specify the dimensions of - * the rectangle and optionally which tile is its 'center' - */ -class RectangleBrush : public Brush -{ -public: - RectangleBrush(int x, int y, int z = 1, int centerx = -1, int centery = -1, int centerz = -1) - { - if(centerx == -1) - cx_ = x/2; - else - cx_ = centerx; - if(centery == -1) - cy_ = y/2; - else - cy_ = centery; - if(centerz == -1) - cz_ = z/2; - else - cz_ = centerz; - x_ = x; - y_ = y; - z_ = z; - }; - coord_vec points(MapCache & mc, DFHack::DFCoord start) - { - coord_vec v; - DFHack::DFCoord iterstart(start.x - cx_, start.y - cy_, start.z - cz_); - DFHack::DFCoord iter = iterstart; - for(int xi = 0; xi < x_; xi++) - { - for(int yi = 0; yi < y_; yi++) - { - for(int zi = 0; zi < z_; zi++) - { - if(mc.testCoord(iter)) - v.push_back(iter); - iter.z++; - } - iter.z = iterstart.z; - iter.y++; - } - iter.y = iterstart.y; - iter.x ++; - } - return v; - }; - ~RectangleBrush(){}; -private: - int x_, y_, z_; - int cx_, cy_, cz_; -}; - -/** - * stupid block brush, legacy. use when you want to apply something to a whole DF map block. - */ -class BlockBrush : public Brush -{ -public: - BlockBrush(){}; - ~BlockBrush(){}; - coord_vec points(MapCache & mc, DFHack::DFCoord start) - { - coord_vec v; - DFHack::DFCoord blockc = start / 16; - DFHack::DFCoord iterc = blockc * 16; - if( !mc.testCoord(start) ) - return v; - auto starty = iterc.y; - for(int xi = 0; xi < 16; xi++) - { - for(int yi = 0; yi < 16; yi++) - { - v.push_back(iterc); - iterc.y++; - } - iterc.y = starty; - iterc.x ++; - } - return v; - }; -}; - -/** - * Column from a position through open space tiles - * example: create a column of magma - */ -class ColumnBrush : public Brush -{ -public: - ColumnBrush(){}; - ~ColumnBrush(){}; - coord_vec points(MapCache & mc, DFHack::DFCoord start) - { - coord_vec v; - bool juststarted = true; - while (mc.testCoord(start)) - { - df::tiletype tt = mc.tiletypeAt(start); - if(DFHack::LowPassable(tt) || juststarted && DFHack::HighPassable(tt)) - { - v.push_back(start); - juststarted = false; - start.z++; - } - else break; - } - return v; - }; -}; - -/** - * Flood-fill water tiles from cursor (for wclean) - * example: remove salt flag from a river - */ -class FloodBrush : public Brush -{ -public: - FloodBrush(Core *c){c_ = c;}; - ~FloodBrush(){}; - coord_vec points(MapCache & mc, DFHack::DFCoord start) - { - coord_vec v; - - std::stack to_flood; - to_flood.push(start); - - std::set seen; - - while (!to_flood.empty()) { - DFCoord xy = to_flood.top(); - to_flood.pop(); - - df::tile_designation des = mc.designationAt(xy); - - if (seen.find(xy) == seen.end() - && des.bits.flow_size - && des.bits.liquid_type == tile_liquid::Water) { - v.push_back(xy); - seen.insert(xy); - - maybeFlood(DFCoord(xy.x - 1, xy.y, xy.z), to_flood, mc); - maybeFlood(DFCoord(xy.x + 1, xy.y, xy.z), to_flood, mc); - maybeFlood(DFCoord(xy.x, xy.y - 1, xy.z), to_flood, mc); - maybeFlood(DFCoord(xy.x, xy.y + 1, xy.z), to_flood, mc); - - df::tiletype tt = mc.tiletypeAt(xy); - if (LowPassable(tt)) - { - maybeFlood(DFCoord(xy.x, xy.y, xy.z - 1), to_flood, mc); - } - if (HighPassable(tt)) - { - maybeFlood(DFCoord(xy.x, xy.y, xy.z + 1), to_flood, mc); - } - } - } - - return v; - } -private: - void maybeFlood(DFCoord c, std::stack &to_flood, MapCache &mc) { - if (mc.testCoord(c)) { - to_flood.push(c); - } - } - Core *c_; -}; - - -// static stuff to be remembered between sessions -static string brushname = "point"; -static string mode="magma"; -static string flowmode="f+"; -static string setmode ="s."; -static unsigned int amount = 7; -static int width = 1, height = 1, z_levels = 1; - -command_result df_liquidsgo (color_ostream &out_, vector & parameters) -{ - assert(out_.is_console()); - Console &out = static_cast(out_); - - for(size_t i = 0; i < parameters.size();i++) - { - if(parameters[i] == "help" || parameters[i] == "?") - { - out.print( "This tool allows placing magma, water and other similar things.\n" - "It is interactive and further help is available when you run it.\n" - "The settings will be remembered until dfhack is closed and you can call\n" - "'liquidsgo-here' (mapped to a hotkey) to paint liquids at the cursor position\n" - "without the need to go back to the dfhack console.\n"); - return CR_OK; - } - } - - if (!Maps::IsValid()) - { - out.printerr("Map is not available!\n"); - return CR_FAILURE; - } - - bool end = false; - - out << "Welcome to the liquid spawner.\nType 'help' or '?' for a list of available commands, 'q' to quit.\nPress return after a command to confirm." << std::endl; - - while(!end) - { - string command = ""; - - std::stringstream str; - str <<"[" << mode << ":" << brushname; - if (brushname == "range") - str << "(w" << width << ":h" << height << ":z" << z_levels << ")"; - str << ":" << amount << ":" << flowmode << ":" << setmode << "]#"; - if(out.lineedit(str.str(),command,liquidsgo_hist) == -1) - return CR_FAILURE; - - if(command=="help" || command == "?") - { - out << "Modes:" << endl - << "m - switch to magma" << endl - << "w - switch to water" << endl - << "o - make obsidian wall instead" << endl - << "of - make obsidian floors" << endl - << "rs - make a river source" << endl - << "f - flow bits only" << endl - << "wclean - remove salt and stagnant flags from tiles" << endl - << "Set-Modes (only for magma/water):" << endl - << "s+ - only add" << endl - << "s. - set" << endl - << "s- - only remove" << endl - << "Properties (only for magma/water):" << endl - << "f+ - make the spawned liquid flow" << endl - << "f. - don't change flow state (read state in flow mode)" << endl - << "f- - make the spawned liquid static" << endl - << "0-7 - set liquid amount" << endl - << "Brush:" << endl - << "point - single tile [p]" << endl - << "range - block with cursor at bottom north-west [r]" << endl - << " (any place, any size)" << endl - << "block - DF map block with cursor in it" << endl - << " (regular spaced 16x16x1 blocks)" << endl - << "column - Column from cursor, up through free space" << endl - << "flood - Flood-fill water tiles from cursor" << endl - << " (only makes sense with wclean)" << endl - << "Other:" << endl - << "q - quit" << endl - << "help or ? - print this list of commands" << endl - << "empty line - put liquid" << endl - << endl - << "Usage: point the DF cursor at a tile you want to modify" << endl - << "and use the commands available :)" << endl; - out << endl << "Settings will be remembered until you quit DF. You can call liquidsgo-here to execute the last configured action. Useful in combination with keybindings." << endl; - } - else if(command == "m") - { - mode = "magma"; - } - else if(command == "o") - { - mode = "obsidian"; - } - else if(command == "of") - { - mode = "obsidian_floor"; - } - else if(command == "w") - { - mode = "water"; - } - else if(command == "f") - { - mode = "flowbits"; - } - else if(command == "rs") - { - mode = "riversource"; - } - else if(command == "wclean") - { - mode = "wclean"; - } - else if(command == "point" || command == "p") - { - brushname = "point"; - } - else if(command == "range" || command == "r") - { - std::stringstream str; - CommandHistory range_hist; - str << " :set range width<" << width << "># "; - out.lineedit(str.str(),command,range_hist); - range_hist.add(command); - width = command == "" ? width : atoi (command.c_str()); - if(width < 1) width = 1; - - str.str(""); - str << " :set range height<" << height << "># "; - out.lineedit(str.str(),command,range_hist); - range_hist.add(command); - height = command == "" ? height : atoi (command.c_str()); - if(height < 1) height = 1; - - str.str(""); - str << " :set range z-levels<" << z_levels << "># "; - out.lineedit(str.str(),command,range_hist); - range_hist.add(command); - z_levels = command == "" ? z_levels : atoi (command.c_str()); - if(z_levels < 1) z_levels = 1; - if(width == 1 && height == 1 && z_levels == 1) - { - brushname = "point"; - } - else - { - brushname = "range"; - } - } - else if(command == "block") - { - brushname = "block"; - } - else if(command == "column") - { - brushname = "column"; - } - else if(command == "flood") - { - brushname = "flood"; - } - else if(command == "q") - { - end = true; - } - else if(command == "f+") - { - flowmode = "f+"; - } - else if(command == "f-") - { - flowmode = "f-"; - } - else if(command == "f.") - { - flowmode = "f."; - } - else if(command == "s+") - { - setmode = "s+"; - } - else if(command == "s-") - { - setmode = "s-"; - } - else if(command == "s.") - { - setmode = "s."; - } - // blah blah, bad code, bite me. - else if(command == "0") - amount = 0; - else if(command == "1") - amount = 1; - else if(command == "2") - amount = 2; - else if(command == "3") - amount = 3; - else if(command == "4") - amount = 4; - else if(command == "5") - amount = 5; - else if(command == "6") - amount = 6; - else if(command == "7") - amount = 7; - else if(command.empty()) - { - df_liquidsgo_execute(out); - } - else - { - out << command << " : unknown command." << endl; - } - } - - return CR_OK; -} - -command_result df_liquidsgo_here (color_ostream &out, vector & parameters) -{ - for(size_t i = 0; i < parameters.size();i++) - { - if(parameters[i] == "help" || parameters[i] == "?") - { - out << "This command is supposed to be mapped to a hotkey." << endl; - out << "It will use the current/last parameters set in liquidsgo." << endl; - return CR_OK; - } - } - - out.print("Run liquidsgo-here with these parameters: "); - out << "[" << mode << ":" << brushname; - if (brushname == "range") - out << "(w" << width << ":h" << height << ":z" << z_levels << ")"; - out << ":" << amount << ":" << flowmode << ":" << setmode << "]\n"; - - return df_liquidsgo_execute(out); -} - -command_result df_liquidsgo_execute(color_ostream &out) -{ - // create brush type depending on old parameters - Brush * brush; - - if (brushname == "point") - { - brush = new RectangleBrush(1,1,1,0,0,0); - //width = 1; - //height = 1; - //z_levels = 1; - } - else if (brushname == "range") - { - brush = new RectangleBrush(width,height,z_levels,0,0,0); - } - else if(brushname == "block") - { - brush = new BlockBrush(); - } - else if(brushname == "column") - { - brush = new ColumnBrush(); - } - else if(brushname == "flood") - { - brush = new FloodBrush(&Core::getInstance()); - } - else - { - // this should never happen! - out << "Old brushtype is invalid! Resetting to point brush.\n"; - brushname = "point"; - width = 1; - height = 1; - z_levels = 1; - brush = new RectangleBrush(width,height,z_levels,0,0,0); - } - - CoreSuspender suspend; - - do - { - if (!Maps::IsValid()) - { - out << "Can't see any DF map loaded." << endl; - break;; - } - int32_t x,y,z; - if(!Gui::getCursorCoords(x,y,z)) - { - out << "Can't get cursor coords! Make sure you have a cursor active in DF." << endl; - break; - } - out << "cursor coords: " << x << "/" << y << "/" << z << endl; - MapCache mcache; - DFHack::DFCoord cursor(x,y,z); - coord_vec all_tiles = brush->points(mcache,cursor); - out << "working..." << endl; - if(mode == "obsidian") - { - coord_vec::iterator iter = all_tiles.begin(); - while (iter != all_tiles.end()) - { - mcache.setTiletypeAt(*iter, tiletype::LavaWall); - mcache.setTemp1At(*iter,10015); - mcache.setTemp2At(*iter,10015); - df::tile_designation des = mcache.designationAt(*iter); - des.bits.flow_size = 0; - mcache.setDesignationAt(*iter, des); - iter ++; - } - } - if(mode == "obsidian_floor") - { - coord_vec::iterator iter = all_tiles.begin(); - while (iter != all_tiles.end()) - { - mcache.setTiletypeAt(*iter, findRandomVariant(tiletype::LavaFloor1)); - iter ++; - } - } - else if(mode == "riversource") - { - coord_vec::iterator iter = all_tiles.begin(); - while (iter != all_tiles.end()) - { - mcache.setTiletypeAt(*iter, tiletype::RiverSource); - - df::tile_designation a = mcache.designationAt(*iter); - a.bits.liquid_type = tile_liquid::Water; - a.bits.liquid_static = false; - a.bits.flow_size = 7; - mcache.setTemp1At(*iter,10015); - mcache.setTemp2At(*iter,10015); - mcache.setDesignationAt(*iter,a); - - Block * b = mcache.BlockAt((*iter)/16); - DFHack::t_blockflags bf = b->BlockFlags(); - bf.bits.liquid_1 = true; - bf.bits.liquid_2 = true; - b->setBlockFlags(bf); - - iter++; - } - } - else if(mode=="wclean") - { - coord_vec::iterator iter = all_tiles.begin(); - while (iter != all_tiles.end()) - { - DFHack::DFCoord current = *iter; - df::tile_designation des = mcache.designationAt(current); - des.bits.water_salt = false; - des.bits.water_stagnant = false; - mcache.setDesignationAt(current,des); - iter++; - } - } - else if(mode== "magma" || mode== "water" || mode == "flowbits") - { - set seen_blocks; - coord_vec::iterator iter = all_tiles.begin(); - while (iter != all_tiles.end()) - { - DFHack::DFCoord current = *iter; // current tile coord - DFHack::DFCoord curblock = current /16; // current block coord - // check if the block is actually there - if(!mcache.BlockAt(curblock)) - { - iter ++; - continue; - } - df::tile_designation des = mcache.designationAt(current); - df::tiletype tt = mcache.tiletypeAt(current); - // don't put liquids into places where they don't belong... - if(!DFHack::FlowPassable(tt)) - { - iter++; - continue; - } - if(mode != "flowbits") - { - if(setmode == "s.") - { - des.bits.flow_size = amount; - } - else if(setmode == "s+") - { - if(des.bits.flow_size < amount) - des.bits.flow_size = amount; - } - else if(setmode == "s-") - { - if (des.bits.flow_size > amount) - des.bits.flow_size = amount; - } - if(amount != 0 && mode == "magma") - { - des.bits.liquid_type = tile_liquid::Magma; - mcache.setTemp1At(current,12000); - mcache.setTemp2At(current,12000); - } - else if(amount != 0 && mode == "water") - { - des.bits.liquid_type = tile_liquid::Water; - mcache.setTemp1At(current,10015); - mcache.setTemp2At(current,10015); - } - else if(amount == 0 && (mode == "water" || mode == "magma")) - { - // reset temperature to sane default - mcache.setTemp1At(current,10015); - mcache.setTemp2At(current,10015); - } - mcache.setDesignationAt(current,des); - } - seen_blocks.insert(mcache.BlockAt(current / 16)); - iter++; - } - set ::iterator biter = seen_blocks.begin(); - while (biter != seen_blocks.end()) - { - DFHack::t_blockflags bflags = (*biter)->BlockFlags(); - if(flowmode == "f+") - { - bflags.bits.liquid_1 = true; - bflags.bits.liquid_2 = true; - (*biter)->setBlockFlags(bflags); - } - else if(flowmode == "f-") - { - bflags.bits.liquid_1 = false; - bflags.bits.liquid_2 = false; - (*biter)->setBlockFlags(bflags); - } - else - { - out << "flow bit 1 = " << bflags.bits.liquid_1 << endl; - out << "flow bit 2 = " << bflags.bits.liquid_2 << endl; - } - biter ++; - } - } - if(mcache.WriteAll()) - out << "OK" << endl; - else - out << "Something failed horribly! RUN!" << endl; - } while (0); - - // cleanup - delete brush; - - return CR_OK; -} From e90da2bff1e8e93864557388e44e45729e464a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 24 Mar 2012 01:29:09 +0100 Subject: [PATCH 24/25] Move autolabor to main plugin folder --- plugins/CMakeLists.txt | 7 ++---- plugins/{autolabor => }/autolabor.cpp | 0 plugins/autolabor/CMakeLists.txt | 31 --------------------------- plugins/autolabor/autolabor.h | 1 - 4 files changed, 2 insertions(+), 37 deletions(-) rename plugins/{autolabor => }/autolabor.cpp (100%) delete mode 100644 plugins/autolabor/CMakeLists.txt delete mode 100644 plugins/autolabor/autolabor.h diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 03cf9ee0d..ff03de3e6 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -45,11 +45,6 @@ if (BUILD_DWARFEXPORT) add_subdirectory (dwarfexport) endif() -OPTION(BUILD_AUTOLABOR "Build autolabor." ON) -if (BUILD_AUTOLABOR) - add_subdirectory (autolabor) -endif() - # Protobuf FILE(GLOB PROJECT_PROTOS ${CMAKE_CURRENT_SOURCE_DIR}/proto/*.proto) @@ -72,6 +67,8 @@ if (BUILD_SUPPORTED) DFHACK_PLUGIN(probe probe.cpp) # this is a plugin which helps detect cursed creatures (vampires, necromancers, werebeasts, ...) DFHACK_PLUGIN(cursecheck cursecheck.cpp) + # automatically assign labors to dwarves! + DFHACK_PLUGIN(autolabor autolabor.cpp) DFHACK_PLUGIN(drybuckets drybuckets.cpp) DFHACK_PLUGIN(getplants getplants.cpp) DFHACK_PLUGIN(plants plants.cpp) diff --git a/plugins/autolabor/autolabor.cpp b/plugins/autolabor.cpp similarity index 100% rename from plugins/autolabor/autolabor.cpp rename to plugins/autolabor.cpp diff --git a/plugins/autolabor/CMakeLists.txt b/plugins/autolabor/CMakeLists.txt deleted file mode 100644 index 124e36bbe..000000000 --- a/plugins/autolabor/CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -PROJECT (autolabor) -# A list of source files -SET(PROJECT_SRCS - autolabor.cpp -) -# A list of headers -SET(PROJECT_HDRS - autolabor.h -) -SET_SOURCE_FILES_PROPERTIES( ${PROJECT_HDRS} PROPERTIES HEADER_FILE_ONLY TRUE) - -# mash them together (headers are marked as headers and nothing will try to compile them) -LIST(APPEND PROJECT_SRCS ${PROJECT_HDRS}) - -#linux -IF(UNIX) - add_definitions(-DLINUX_BUILD) - SET(PROJECT_LIBS - # add any extra linux libs here - ${PROJECT_LIBS} - ) -# windows -ELSE(UNIX) - SET(PROJECT_LIBS - # add any extra linux libs here - ${PROJECT_LIBS} - $(NOINHERIT) - ) -ENDIF(UNIX) -# this makes sure all the stuff is put in proper places and linked to dfhack -DFHACK_PLUGIN(autolabor ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS}) diff --git a/plugins/autolabor/autolabor.h b/plugins/autolabor/autolabor.h deleted file mode 100644 index 7b9637ef9..000000000 --- a/plugins/autolabor/autolabor.h +++ /dev/null @@ -1 +0,0 @@ -#pragma once \ No newline at end of file From 7fe5fc9a906710bc603bf93b3e282872990a3361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 24 Mar 2012 01:33:29 +0100 Subject: [PATCH 25/25] autolabor fixage --- plugins/autolabor.cpp | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/plugins/autolabor.cpp b/plugins/autolabor.cpp index 262e59616..3ef0f9e5c 100644 --- a/plugins/autolabor.cpp +++ b/plugins/autolabor.cpp @@ -27,9 +27,6 @@ using namespace df::enums; using df::global::ui; using df::global::world; -// our own, empty header. -#include "autolabor.h" - #define ARRAY_COUNT(array) (sizeof(array)/sizeof((array)[0])) static int enable_autolabor; @@ -472,9 +469,9 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) std::vector dwarf_info(n_dwarfs); - std::vector best_noble(_countof(noble_skills)); - std::vector highest_noble_skill(_countof(noble_skills)); - std::vector highest_noble_experience(_countof(noble_skills)); + std::vector best_noble(ARRAY_COUNT(noble_skills)); + std::vector highest_noble_skill(ARRAY_COUNT(noble_skills)); + std::vector highest_noble_experience(ARRAY_COUNT(noble_skills)); // Find total skill and highest skill for each dwarf. More skilled dwarves shouldn't be used for minor tasks. @@ -495,7 +492,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) // They are likely to have appointed noble positions, so should be kept free where possible. int noble_skill_id = -1; - for (int i = 0; i < _countof(noble_skills); i++) + for (int i = 0; i < ARRAY_COUNT(noble_skills); i++) { if (skill == noble_skills[i]) noble_skill_id = i; @@ -503,7 +500,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) if (noble_skill_id >= 0) { - assert(noble_skill_id < _countof(noble_skills)); + assert(noble_skill_id < ARRAY_COUNT(noble_skills)); if (highest_noble_skill[noble_skill_id] < skill_level || (highest_noble_skill[noble_skill_id] == skill_level && @@ -528,7 +525,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) // Mark the best nobles, so we try to keep them non-busy. (It would be better to find the actual assigned nobles.) - for (int i = 0; i < _countof(noble_skills); i++) + for (int i = 0; i < ARRAY_COUNT(noble_skills); i++) { assert(best_noble[i] >= 0); assert(best_noble[i] < n_dwarfs); @@ -551,7 +548,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) continue; assert(labor >= 0); - assert(labor < _countof(labor_infos)); + assert(labor < ARRAY_COUNT(labor_infos)); if (labor_infos[labor].is_exclusive && dwarfs[dwarf]->status.labors[labor]) dwarf_info[dwarf].mastery_penalty -= 100; @@ -597,7 +594,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) int job = dwarfs[dwarf]->job.current_job->job_type; assert(job >= 0); - assert(job < _countof(dwarf_states)); + assert(job < ARRAY_COUNT(dwarf_states)); dwarf_info[dwarf].state = dwarf_states[job]; } @@ -617,7 +614,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) if (labor != df::enums::unit_labor::NONE) { assert(labor >= 0); - assert(labor < _countof(labor_to_skill)); + assert(labor < ARRAY_COUNT(labor_to_skill)); labor_to_skill[labor] = skill; } @@ -631,7 +628,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) continue; assert(labor >= 0); - assert(labor < _countof(labor_infos)); + assert(labor < ARRAY_COUNT(labor_infos)); labors.push_back(labor); } @@ -645,7 +642,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) auto labor = *lp; assert(labor >= 0); - assert(labor < _countof(labor_infos)); + assert(labor < ARRAY_COUNT(labor_infos)); df::job_skill skill = labor_to_skill[labor]; @@ -821,7 +818,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out ) continue; assert(labor >= 0); - assert(labor < _countof(labor_infos)); + assert(labor < ARRAY_COUNT(labor_infos)); if (labor_infos[labor].mode != HAULERS) continue;