//define which version of DF this is being built for. #define DF_VER_040 //#define DF_VER_034 // some headers required for a plugin. Nothing special, just the basics. #include "Core.h" #include #include #include // DF data structure definition headers #include "DataDefs.h" #include "df/world.h" #include "df/ui.h" #include "df/item.h" #include "df/creature_raw.h" #include "df/caste_raw.h" #include "df/body_part_raw.h" #include "df/historical_figure.h" #include "df/job_item.h" #include "df/job_material_category.h" #include "df/dfhack_material_category.h" #include "df/matter_state.h" #include "df/material_vec_ref.h" #include "df/builtin_mats.h" #include "df/map_block_column.h" #include "df/plant.h" #include "df/plant_tree_info.h" #include "df/plant_growth.h" #include "df/descriptor_color.h" #include "df/descriptor_pattern.h" #include "df/descriptor_shape.h" #include "df/physical_attribute_type.h" #include "df/mental_attribute_type.h" #include //DFhack specific headers #include "modules/Maps.h" #include "modules/MapCache.h" #include "modules/Materials.h" #include "TileTypes.h" #include #include #include "RemoteFortressReader.pb.h" #include "RemoteServer.h" using namespace DFHack; using namespace df::enums; using namespace RemoteFortressReader; using namespace std; DFHACK_PLUGIN("RemoteFortressReader"); REQUIRE_GLOBAL(world); // Here go all the command declarations... // mostly to allow having the mandatory stuff on top of the file and commands on the bottom static command_result GetGrowthList(color_ostream &stream, const EmptyMessage *in, MaterialList *out); static command_result GetMaterialList(color_ostream &stream, const EmptyMessage *in, MaterialList *out); static command_result GetTiletypeList(color_ostream &stream, const EmptyMessage *in, TiletypeList *out); static command_result GetBlockList(color_ostream &stream, const BlockRequest *in, BlockList *out); static command_result GetPlantList(color_ostream &stream, const BlockRequest *in, PlantList *out); static command_result CheckHashes(color_ostream &stream, const EmptyMessage *in); void CopyBlock(df::map_block * DfBlock, RemoteFortressReader::MapBlock * NetBlock, MapExtras::MapCache * MC); void FindChangedBlocks(); const char* growth_locations[] = { "TWIGS", "LIGHT_BRANCHES", "HEAVY_BRANCHES", "TRUNK", "ROOTS", "CAP", "SAPLING", "SHRUB" }; #define GROWTH_LOCATIONS_SIZE 8 // Mandatory init function. If you have some global state, create it here. DFhackCExport command_result plugin_init(color_ostream &out, std::vector &commands) { //// Fill the command list with your commands. //commands.push_back(PluginCommand( // "isoworldremote", "Dump north-west embark tile to text file for debug purposes.", // isoWorldRemote, 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" // " isoworldremote\n" // " Does nothing.\n" //)); return CR_OK; } DFhackCExport RPCService *plugin_rpcconnect(color_ostream &) { RPCService *svc = new RPCService(); svc->addFunction("GetMaterialList", GetMaterialList); svc->addFunction("GetGrowthList", GetGrowthList); svc->addFunction("GetBlockList", GetBlockList); svc->addFunction("CheckHashes", CheckHashes); svc->addFunction("GetTiletypeList", GetTiletypeList); svc->addFunction("GetPlantList", GetPlantList); return svc; } // 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; } uint16_t fletcher16(uint8_t const *data, size_t bytes) { uint16_t sum1 = 0xff, sum2 = 0xff; while (bytes) { size_t tlen = bytes > 20 ? 20 : bytes; bytes -= tlen; do { sum2 += sum1 += *data++; } while (--tlen); sum1 = (sum1 & 0xff) + (sum1 >> 8); sum2 = (sum2 & 0xff) + (sum2 >> 8); } /* Second reduction step to reduce sums to 8 bits */ sum1 = (sum1 & 0xff) + (sum1 >> 8); sum2 = (sum2 & 0xff) + (sum2 >> 8); return sum2 << 8 | sum1; } RemoteFortressReader::TiletypeMaterial TranslateMaterial(df::tiletype_material material) { switch (material) { case df::enums::tiletype_material::NONE: return RemoteFortressReader::NO_MATERIAL; break; case df::enums::tiletype_material::AIR: return RemoteFortressReader::AIR; break; case df::enums::tiletype_material::SOIL: return RemoteFortressReader::SOIL; break; case df::enums::tiletype_material::STONE: return RemoteFortressReader::STONE; break; case df::enums::tiletype_material::FEATURE: return RemoteFortressReader::FEATURE; break; case df::enums::tiletype_material::LAVA_STONE: return RemoteFortressReader::LAVA_STONE; break; case df::enums::tiletype_material::MINERAL: return RemoteFortressReader::MINERAL; break; case df::enums::tiletype_material::FROZEN_LIQUID: return RemoteFortressReader::FROZEN_LIQUID; break; case df::enums::tiletype_material::CONSTRUCTION: return RemoteFortressReader::CONSTRUCTION; break; case df::enums::tiletype_material::GRASS_LIGHT: return RemoteFortressReader::GRASS_LIGHT; break; case df::enums::tiletype_material::GRASS_DARK: return RemoteFortressReader::GRASS_DARK; break; case df::enums::tiletype_material::GRASS_DRY: return RemoteFortressReader::GRASS_DRY; break; case df::enums::tiletype_material::GRASS_DEAD: return RemoteFortressReader::GRASS_DEAD; break; case df::enums::tiletype_material::PLANT: return RemoteFortressReader::PLANT; break; case df::enums::tiletype_material::HFS: return RemoteFortressReader::HFS; break; case df::enums::tiletype_material::CAMPFIRE: return RemoteFortressReader::CAMPFIRE; break; case df::enums::tiletype_material::FIRE: return RemoteFortressReader::FIRE; break; case df::enums::tiletype_material::ASHES: return RemoteFortressReader::ASHES; break; case df::enums::tiletype_material::MAGMA: return RemoteFortressReader::MAGMA; break; case df::enums::tiletype_material::DRIFTWOOD: return RemoteFortressReader::DRIFTWOOD; break; case df::enums::tiletype_material::POOL: return RemoteFortressReader::POOL; break; case df::enums::tiletype_material::BROOK: return RemoteFortressReader::BROOK; break; case df::enums::tiletype_material::RIVER: return RemoteFortressReader::RIVER; break; case df::enums::tiletype_material::ROOT: return RemoteFortressReader::ROOT; break; case df::enums::tiletype_material::TREE: return RemoteFortressReader::TREE_MATERIAL; break; case df::enums::tiletype_material::MUSHROOM: return RemoteFortressReader::MUSHROOM; break; case df::enums::tiletype_material::UNDERWORLD_GATE: return RemoteFortressReader::UNDERWORLD_GATE; break; default: return RemoteFortressReader::NO_MATERIAL; break; } return RemoteFortressReader::NO_MATERIAL; } RemoteFortressReader::TiletypeSpecial TranslateSpecial(df::tiletype_special special) { switch (special) { case df::enums::tiletype_special::NONE: return RemoteFortressReader::NO_SPECIAL; break; case df::enums::tiletype_special::NORMAL: return RemoteFortressReader::NORMAL; break; case df::enums::tiletype_special::RIVER_SOURCE: return RemoteFortressReader::RIVER_SOURCE; break; case df::enums::tiletype_special::WATERFALL: return RemoteFortressReader::WATERFALL; break; case df::enums::tiletype_special::SMOOTH: return RemoteFortressReader::SMOOTH; break; case df::enums::tiletype_special::FURROWED: return RemoteFortressReader::FURROWED; break; case df::enums::tiletype_special::WET: return RemoteFortressReader::WET; break; case df::enums::tiletype_special::DEAD: return RemoteFortressReader::DEAD; break; case df::enums::tiletype_special::WORN_1: return RemoteFortressReader::WORN_1; break; case df::enums::tiletype_special::WORN_2: return RemoteFortressReader::WORN_2; break; case df::enums::tiletype_special::WORN_3: return RemoteFortressReader::WORN_3; break; case df::enums::tiletype_special::TRACK: return RemoteFortressReader::TRACK; break; case df::enums::tiletype_special::SMOOTH_DEAD: return RemoteFortressReader::SMOOTH_DEAD; break; default: return RemoteFortressReader::NO_SPECIAL; break; } return RemoteFortressReader::NO_SPECIAL; } RemoteFortressReader::TiletypeShape TranslateShape(df::tiletype_shape shape) { switch (shape) { case df::enums::tiletype_shape::NONE: return RemoteFortressReader::NO_SHAPE; break; case df::enums::tiletype_shape::EMPTY: return RemoteFortressReader::EMPTY; break; case df::enums::tiletype_shape::FLOOR: return RemoteFortressReader::FLOOR; break; case df::enums::tiletype_shape::BOULDER: return RemoteFortressReader::BOULDER; break; case df::enums::tiletype_shape::PEBBLES: return RemoteFortressReader::PEBBLES; break; case df::enums::tiletype_shape::WALL: return RemoteFortressReader::WALL; break; case df::enums::tiletype_shape::FORTIFICATION: return RemoteFortressReader::FORTIFICATION; break; case df::enums::tiletype_shape::STAIR_UP: return RemoteFortressReader::STAIR_UP; break; case df::enums::tiletype_shape::STAIR_DOWN: return RemoteFortressReader::STAIR_DOWN; break; case df::enums::tiletype_shape::STAIR_UPDOWN: return RemoteFortressReader::STAIR_UPDOWN; break; case df::enums::tiletype_shape::RAMP: return RemoteFortressReader::RAMP; break; case df::enums::tiletype_shape::RAMP_TOP: return RemoteFortressReader::RAMP_TOP; break; case df::enums::tiletype_shape::BROOK_BED: return RemoteFortressReader::BROOK_BED; break; case df::enums::tiletype_shape::BROOK_TOP: return RemoteFortressReader::BROOK_TOP; break; case df::enums::tiletype_shape::BRANCH: return RemoteFortressReader::BRANCH; break; #ifdef DF_VER_034 case df::enums::tiletype_shape::TREE: return RemoteFortressReader::TREE; break; #endif case df::enums::tiletype_shape::TRUNK_BRANCH: return RemoteFortressReader::TRUNK_BRANCH; break; case df::enums::tiletype_shape::TWIG: return RemoteFortressReader::TWIG; break; case df::enums::tiletype_shape::SAPLING: return RemoteFortressReader::SAPLING; break; case df::enums::tiletype_shape::SHRUB: return RemoteFortressReader::SHRUB; break; case df::enums::tiletype_shape::ENDLESS_PIT: return RemoteFortressReader::EMPTY; break; default: return RemoteFortressReader::NO_SHAPE; break; } return RemoteFortressReader::NO_SHAPE; } RemoteFortressReader::TiletypeVariant TranslateVariant(df::tiletype_variant variant) { switch (variant) { case df::enums::tiletype_variant::NONE: return RemoteFortressReader::NO_VARIANT; break; case df::enums::tiletype_variant::VAR_1: return RemoteFortressReader::VAR_1; break; case df::enums::tiletype_variant::VAR_2: return RemoteFortressReader::VAR_2; break; case df::enums::tiletype_variant::VAR_3: return RemoteFortressReader::VAR_3; break; case df::enums::tiletype_variant::VAR_4: return RemoteFortressReader::VAR_4; break; default: return RemoteFortressReader::NO_VARIANT; break; } return RemoteFortressReader::NO_VARIANT; } static command_result CheckHashes(color_ostream &stream, const EmptyMessage *in) { clock_t start = clock(); for (int i = 0; i < world->map.map_blocks.size(); i++) { df::map_block * block = world->map.map_blocks[i]; fletcher16((uint8_t*)(block->tiletype), 16 * 16 * sizeof(df::enums::tiletype::tiletype)); } clock_t end = clock(); double elapsed_secs = double(end - start) / CLOCKS_PER_SEC; stream.print("Checking all hashes took %f seconds.", elapsed_secs); return CR_OK; } df::matter_state GetState(df::material * mat, uint16_t temp = 10015) { df::matter_state state = matter_state::Solid; if (temp >= mat->heat.melting_point) state = df::matter_state::Liquid; if (temp >= mat->heat.boiling_point) state = matter_state::Gas; return state; } static command_result GetMaterialList(color_ostream &stream, const EmptyMessage *in, MaterialList *out) { if (!Core::getInstance().isWorldLoaded()) { //out->set_available(false); return CR_OK; } df::world_raws *raws = &world->raws; MaterialInfo mat; for (int i = 0; i < raws->inorganics.size(); i++) { mat.decode(0, i); MaterialDefinition *mat_def = out->add_material_list(); mat_def->mutable_mat_pair()->set_mat_type(0); mat_def->mutable_mat_pair()->set_mat_index(i); mat_def->set_id(mat.getToken()); mat_def->set_name(mat.toString()); //find the name at cave temperature; if (raws->inorganics[i]->material.state_color[GetState(&raws->inorganics[i]->material)] < raws->language.colors.size()) { df::descriptor_color *color = raws->language.colors[raws->inorganics[i]->material.state_color[GetState(&raws->inorganics[i]->material)]]; mat_def->mutable_state_color()->set_red(color->red * 255); mat_def->mutable_state_color()->set_green(color->green * 255); mat_def->mutable_state_color()->set_blue(color->blue * 255); } } for (int i = 1; i < 19; i++) { int k = 0; if (i == 7) k = 1;// for coal. for (int j = 0; j <= k; j++) { mat.decode(i, j); MaterialDefinition *mat_def = out->add_material_list(); mat_def->mutable_mat_pair()->set_mat_type(i); mat_def->mutable_mat_pair()->set_mat_index(j); mat_def->set_id(mat.getToken()); mat_def->set_name(mat.toString()); //find the name at cave temperature; if (raws->mat_table.builtin[i]->state_color[GetState(raws->mat_table.builtin[i])] < raws->language.colors.size()) { df::descriptor_color *color = raws->language.colors[raws->mat_table.builtin[i]->state_color[GetState(raws->mat_table.builtin[i])]]; mat_def->mutable_state_color()->set_red(color->red * 255); mat_def->mutable_state_color()->set_green(color->green * 255); mat_def->mutable_state_color()->set_blue(color->blue * 255); } } } for (int i = 0; i < raws->creatures.all.size(); i++) { df::creature_raw * creature = raws->creatures.all[i]; for (int j = 0; j < creature->material.size(); j++) { mat.decode(j + 19, i); MaterialDefinition *mat_def = out->add_material_list(); mat_def->mutable_mat_pair()->set_mat_type(j + 19); mat_def->mutable_mat_pair()->set_mat_index(i); mat_def->set_id(mat.getToken()); mat_def->set_name(mat.toString()); //find the name at cave temperature; if (creature->material[j]->state_color[GetState(creature->material[j])] < raws->language.colors.size()) { df::descriptor_color *color = raws->language.colors[creature->material[j]->state_color[GetState(creature->material[j])]]; mat_def->mutable_state_color()->set_red(color->red * 255); mat_def->mutable_state_color()->set_green(color->green * 255); mat_def->mutable_state_color()->set_blue(color->blue * 255); } } } for (int i = 0; i < raws->plants.all.size(); i++) { df::plant_raw * plant = raws->plants.all[i]; for (int j = 0; j < plant->material.size(); j++) { mat.decode(j + 419, i); MaterialDefinition *mat_def = out->add_material_list(); mat_def->mutable_mat_pair()->set_mat_type(j + 419); mat_def->mutable_mat_pair()->set_mat_index(i); mat_def->set_id(mat.getToken()); mat_def->set_name(mat.toString()); //find the name at cave temperature; if (plant->material[j]->state_color[GetState(plant->material[j])] < raws->language.colors.size()) { df::descriptor_color *color = raws->language.colors[plant->material[j]->state_color[GetState(plant->material[j])]]; mat_def->mutable_state_color()->set_red(color->red * 255); mat_def->mutable_state_color()->set_green(color->green * 255); mat_def->mutable_state_color()->set_blue(color->blue * 255); } } } return CR_OK; } static command_result GetGrowthList(color_ostream &stream, const EmptyMessage *in, MaterialList *out) { if (!Core::getInstance().isWorldLoaded()) { //out->set_available(false); return CR_OK; } df::world_raws *raws = &world->raws; if (!raws) return CR_OK;//'. for (int i = 0; i < raws->plants.all.size(); i++) { df::plant_raw * pp = raws->plants.all[i]; if (!pp) continue; MaterialDefinition * basePlant = out->add_material_list(); basePlant->set_id(pp->id + ":BASE"); basePlant->set_name(pp->name); basePlant->mutable_mat_pair()->set_mat_type(-1); basePlant->mutable_mat_pair()->set_mat_index(i); for (int g = 0; g < pp->growths.size(); g++) { df::plant_growth* growth = pp->growths[g]; if (!growth) continue; for (int l = 0; l < GROWTH_LOCATIONS_SIZE; l++) { MaterialDefinition * out_growth = out->add_material_list(); out_growth->set_id(pp->id + ":" + growth->id + +":" + growth_locations[l]); out_growth->set_name(growth->name); out_growth->mutable_mat_pair()->set_mat_type(g * 10 + l); out_growth->mutable_mat_pair()->set_mat_index(i); } } } return CR_OK; } void CopyBlock(df::map_block * DfBlock, RemoteFortressReader::MapBlock * NetBlock, MapExtras::MapCache * MC) { NetBlock->set_map_x(DfBlock->map_pos.x); NetBlock->set_map_y(DfBlock->map_pos.y); NetBlock->set_map_z(DfBlock->map_pos.z); MapExtras::Block * block = MC->BlockAtTile(DfBlock->map_pos); for (int yy = 0; yy < 16; yy++) { for (int xx = 0; xx < 16; xx++) { df::tiletype tile = DfBlock->tiletype[xx][yy]; NetBlock->add_tiles(tile); RemoteFortressReader::MatPair * material = NetBlock->add_materials(); material->set_mat_type(block->baseMaterialAt(df::coord2d(xx, yy)).mat_index); material->set_mat_index(block->baseMaterialAt(df::coord2d(xx, yy)).mat_type); } } } static command_result GetBlockList(color_ostream &stream, const BlockRequest *in, BlockList *out) { MapExtras::MapCache MC; //stream.print("Got request for blocks from (%d, %d, %d) to (%d, %d, %d).\n", in->min_x(), in->min_y(), in->min_z(), in->max_x(), in->max_y(), in->max_z()); for (int zz = in->min_z(); zz < in->max_z(); zz++) { for (int yy = in->min_y(); yy < in->max_y(); yy++) { for (int xx = in->min_x(); xx < in->max_x(); xx++) { df::map_block * block = DFHack::Maps::getBlock(xx, yy, zz); if (block == NULL) continue; RemoteFortressReader::MapBlock *net_block = out->add_map_blocks(); CopyBlock(block, net_block,&MC); } } } MC.trash(); return CR_OK; } static command_result GetTiletypeList(color_ostream &stream, const EmptyMessage *in, TiletypeList *out) { int count = 0; FOR_ENUM_ITEMS(tiletype, tt) { Tiletype * type = out->add_tiletype_list(); type->set_id(tt); type->set_name(ENUM_KEY_STR(tiletype, tt)); const char * name = tileName(tt); if (name != NULL && name[0] != 0) type->set_caption(name); type->set_shape(TranslateShape(tileShape(tt))); type->set_special(TranslateSpecial(tileSpecial(tt))); type->set_material(TranslateMaterial(tileMaterial(tt))); type->set_variant(TranslateVariant(tileVariant(tt))); type->set_direction(tileDirection(tt).whole); count++; } return CR_OK; } static command_result GetPlantList(color_ostream &stream, const BlockRequest *in, PlantList *out) { int min_x = in->min_x() / 3; int min_y = in->min_y() / 3; int min_z = in->min_z(); int max_x = in->max_x() / 3; int max_y = in->max_y() / 3; int max_z = in->max_z(); for (int xx = min_x; xx < max_x; xx++) for (int yy = min_y; yy < max_y; yy++) { if (xx < 0 || yy < 0 || xx >= world->map.x_count_block || yy >= world->map.y_count_block) continue; df::map_block_column * column = world->map.column_index[xx][yy]; for (int i = 0; i < column->plants.size(); i++) { df::plant * plant = column->plants[i]; if (!plant->tree_info) { if (plant->pos.z < min_z || plant->pos.z >= max_z) continue; if (plant->pos.x < in->min_x() * 16 || plant->pos.x >= in->max_x() * 16) continue; if (plant->pos.y < in->min_y() * 16 || plant->pos.y >= in->max_y() * 16) continue; } else { if (plant->pos.z - plant->tree_info->roots_depth < min_z || plant->pos.z + plant->tree_info->body_height > max_z) continue; if (plant->pos.x - plant->tree_info->dim_x / 2 < in->min_x() * 16 || plant->pos.x + plant->tree_info->dim_x / 2 >= in->max_x() * 16) continue; if (plant->pos.y - plant->tree_info->dim_y / 2 < in->min_y() * 16 || plant->pos.y + plant->tree_info->dim_y / 2 >= in->max_y() * 16) continue; } RemoteFortressReader::PlantDef * out_plant = out->add_plant_list(); out_plant->set_index(plant->material); out_plant->set_pos_x(plant->pos.x); out_plant->set_pos_y(plant->pos.y); out_plant->set_pos_z(plant->pos.z); } } return CR_OK; }