widelands-dev team mailing list archive
-
widelands-dev team
-
Mailing list archive
-
Message #07630
Re: [Merge] lp:~widelands-dev/widelands/map_object_info into lp:widelands
Review: Needs Fixing code, running
Review in the diff comments. JSON seems to be generated properly, but I don't have the website set up, so I am unable to test with your other branch there.
Diff comments:
>
> === modified file 'src/CMakeLists.txt'
> --- src/CMakeLists.txt 2016-02-06 11:11:24 +0000
> +++ src/CMakeLists.txt 2016-05-14 07:36:19 +0000
> @@ -82,6 +82,7 @@
> add_subdirectory(third_party)
> add_subdirectory(ui_basic)
> add_subdirectory(ui_fsmenu)
> +add_subdirectory(website)
A CMake option would be nice. The following code works for me:
option(BUILD_WEBSITE_TOOLS "Build website-related tools" ON)
...
if (BUILD_WEBSITE_TOOLS)
add_subdirectory(website)
endif (BUILD_WEBSITE_TOOLS)
> add_subdirectory(wui)
>
> # TODO(unknown): Ideally widelands_ball_of_mud shouldn't exist, everything should be in a
>
> === renamed file 'src/logic/map_info.cc' => 'src/website/map_info.cc'
> === added file 'src/website/map_object_info.cc'
> --- src/website/map_object_info.cc 1970-01-01 00:00:00 +0000
> +++ src/website/map_object_info.cc 2016-05-14 07:36:19 +0000
> @@ -0,0 +1,520 @@
> +/*
> + * Copyright (C) 2016 by the Widelands Development Team
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + *
> + */
> +
> +#include <memory>
> +
> +#include <SDL.h>
> +#include <boost/algorithm/string.hpp>
> +#include <boost/format.hpp>
> +#include <boost/lexical_cast.hpp>
> +
> +#include "base/i18n.h"
> +#include "base/log.h"
> +#include "base/macros.h"
> +#include "config.h"
> +#include "graphic/graphic.h"
> +#include "io/filesystem/filesystem.h"
> +#include "io/filesystem/layered_filesystem.h"
> +#include "io/filewrite.h"
> +#include "logic/editor_game_base.h"
> +#include "logic/map_objects/tribes/tribes.h"
> +#include "logic/map_objects/world/world.h"
> +#include "sound/sound_handler.h"
> +
> +using namespace Widelands;
> +
> +namespace {
> +
> +/*
> + ==========================================================
> + SETUP
> + ==========================================================
> + */
> +
> +
> +// Setup the static objects Widelands needs to operate and initializes systems.
> +void initialize(const std::string& output_path) {
> + i18n::set_locale("en");
> +
> + if (SDL_Init(SDL_INIT_VIDEO) != 0) {
> + throw wexception("Unable to initialize SDL: %s", SDL_GetError());
> + }
> +
> + g_fs = new LayeredFileSystem();
> + g_fs->add_file_system(&FileSystem::create(INSTALL_DATADIR));
> +
> + FileSystem* out_filesystem = &FileSystem::create(output_path);
> + g_fs->add_file_system(out_filesystem);
I think adding out_filesystem to g_fs is undesired, because the game now prioritizes loading data (such as tribe descriptions) from output_path and not the actual data path! Seperating out_filesystem from g_fs should also allow output paths relative to the working directory of the executable. The write_tribes, write_buildings, write_wares and write_workers functions should NOT need to have output_path passed to them then.
> +
> + // We don't really need graphics or sound here, but we will get error messages
> + // when they aren't initialized
> + g_gr = new Graphic();
> + g_gr->initialize(Graphic::TraceGl::kNo, 1, 1, false);
> +
> + g_sound_handler.init();
> + g_sound_handler.nosound_ = true;
> +}
> +
> +/*
> + ==========================================================
> + SPECIALIZED FILEWRITE
> + ==========================================================
> + */
> +
> +// Defines some convenience writing functions for the JSON format
> +class JSONFileWrite : public FileWrite {
> +public:
> + JSONFileWrite() : FileWrite(), level_(0) {}
> +
> + void write_string(const std::string& s, bool use_indent = false) {
> + std::string writeme = s;
> + if (use_indent) {
> + for (int i = 0; i < level_; ++i) {
> + writeme = (boost::format(" %s") % writeme).str();
> + }
> + }
> + data(writeme.c_str(), writeme.size());
> + }
> + void write_key(const std::string& key) {
> + write_string((boost::format("\"%s\":\n") % key).str(), true);
> + }
> + void write_value_string(const std::string& quoted_value) {
> + write_string((boost::format("\"%s\"") % quoted_value).str(), true);
> + }
> + void write_key_value(const std::string& key, const std::string& quoted_value) {
> + write_string((boost::format("\"%s\": %s") % key % quoted_value).str(), true);
> + }
> + void write_key_value_string(const std::string& key, const std::string& value) {
> + std::string quoted_value = value;
> + boost::replace_all(quoted_value, "\"", "\\\"");
> + write_key_value(key, "\"" + value + "\"");
> + }
> + void write_key_value_int(const std::string& key, const int value) {
> + write_key_value(key, boost::lexical_cast<std::string>(value));
> + }
> + void open_brace() {
> + write_string("{\n", true);
> + ++level_;
> + }
> + // JSON hates a final comma. This defaults to having NO comma.
> + void close_brace(bool precede_newline = false, int current = 0, int total = 0) {
> + --level_;
> + if (precede_newline) {
> + write_string("\n");
> + }
> + if (current < total - 1) {
> + write_string("},\n", true);
> + } else {
> + write_string("}", true);
> + }
> + }
> + void open_array(const std::string& name) {
> + write_string((boost::format("\"%s\":[\n") % name).str(), true);
> + ++level_;
> + }
> + // JSON hates a final comma. This defaults to having NO comma.
> + void close_array(int current = 0, int total = 0) {
> + --level_;
> + write_string("\n");
> + if (current < total - 1) {
> + write_string("],\n", true);
> + } else {
> + write_string("]\n", true);
> + }
> + }
> + // JSON hates a final comma. This defaults to having a comma.
> + void close_element(int current = -2, int total = 0) {
> + if (current < total - 1) {
> + write_string(",\n");
> + }
> + }
> +private:
> + int level_;
> +};
> +
> +/*
> + ==========================================================
> + BUILDINGS
> + ==========================================================
> + */
> +
> +void write_buildings(const TribeDescr& tribe, EditorGameBase& egbase, const std::string& output_path) {
> +
> + log("\n==================\nWriting buildings:\n==================\n");
> + JSONFileWrite fw;
> + fw.open_brace(); // Main
> + fw.open_array("buildings"); // Buildings
> +
> + // We don't want any partially finished buildings
> + std::vector<const BuildingDescr*> buildings;
> + for (const DescriptionIndex& index : tribe.buildings()) {
> + const BuildingDescr* building = tribe.get_building_descr(index);
> + if (building->type() != MapObjectType::CONSTRUCTIONSITE &&
> + building->type() != MapObjectType::DISMANTLESITE) {
> + buildings.push_back(building);
> + }
> + }
> +
> + // Now write
> + for (size_t i = 0; i < buildings.size(); ++i) {
> + const BuildingDescr& building = *buildings[i];
> + log(" %s", building.name().c_str());
> + fw.open_brace(); // Building
> +
> + fw.write_key_value_string("name", building.name());
> + fw.close_element();
> + fw.write_key_value_string("descname", building.descname());
> + fw.close_element();
> + fw.write_key_value_string("icon", building.representative_image_filename());
> + fw.close_element();
> +
> + // Conditional stuff in between, so we won't run into trouble with the commas.
> +
> + // Buildcost
> + if (building.is_buildable()) {
> + fw.open_array("buildcost"); // Buildcost
> + size_t buildcost_counter = 0;
> + for (WareAmount buildcost : building.buildcost()) {
> + const WareDescr& ware = *tribe.get_ware_descr(buildcost.first);
> + fw.open_brace(); // Buildcost
> + fw.write_key_value_string("name", ware.name());
> + fw.close_element();
> + fw.write_key_value_int("amount", buildcost.second);
> + fw.close_brace(true, buildcost_counter, building.buildcost().size()); // Buildcost
> + ++buildcost_counter;
> + }
> + fw.close_array(1, 5); // Buildcost - we need a comma
> + }
> +
> + if (building.is_enhanced()) {
> + fw.write_key_value_string("enhanced", tribe.get_building_descr(building.enhanced_from())->name());
> + fw.close_element();
> + }
> +
> + if (building.enhancement() != INVALID_INDEX) {
> + fw.write_key_value_string("enhancement", tribe.get_building_descr(building.enhancement())->name());
> + fw.close_element();
> + }
> +
> + if (upcast(ProductionSiteDescr const, productionsite, &building)) {
> + // Produces
> + if (productionsite->output_ware_types().size() > 0) {
> + fw.open_array("produced_wares"); // Produces
> + size_t produces_counter = 0;
> + for (DescriptionIndex ware_index : productionsite->output_ware_types()) {
> + fw.write_value_string(tribe.get_ware_descr(ware_index)->name());
> + fw.close_element(produces_counter, productionsite->output_ware_types().size());
> + ++produces_counter;
> + }
> + fw.close_array(1, 5); // Produces - we need a comma
> + }
> + if (productionsite->output_worker_types().size() > 0) {
> + fw.open_array("produced_workers"); // Produces
> + size_t produces_counter = 0;
> + for (DescriptionIndex worker_index : productionsite->output_worker_types()) {
> + fw.write_value_string(tribe.get_worker_descr(worker_index)->name());
> + fw.close_element(produces_counter, productionsite->output_worker_types().size());
> + ++produces_counter;
> + }
> + fw.close_array(1, 5); // Produces - we need a comma
> + }
> +
> + // Consumes
> + if (productionsite->inputs().size() > 0) {
> + fw.open_array("stored_wares"); // Consumes
> + size_t consumes_counter = 0;
> + for (WareAmount input : productionsite->inputs()) {
> + const WareDescr& ware = *tribe.get_ware_descr(input.first);
> + fw.open_brace(); // Input
> + fw.write_key_value_string("name", ware.name());
> + fw.close_element();
> + fw.write_key_value_int("amount", input.second);
> + fw.close_brace(true, consumes_counter, productionsite->inputs().size()); // Input
> + ++consumes_counter;
> + }
> + fw.close_array(1, 5); // Consumes - we need a comma
> + }
> +
> + fw.open_array("workers"); // Workers
> + size_t worker_counter = 0;
> + for (WareAmount input : productionsite->working_positions()) {
> + const WorkerDescr& worker = *tribe.get_worker_descr(input.first);
> + fw.open_brace(); // Worker
> + fw.write_key_value_string("name", worker.name());
> + fw.close_element();
> + fw.write_key_value_int("amount", input.second);
> + fw.close_brace(true, worker_counter, productionsite->working_positions().size()); // Worker
> + ++worker_counter;
> + }
> + fw.close_array(1, 5); // Workers - we need a comma
> + } else if (upcast(MilitarySiteDescr const, militarysite, &building)) {
> + fw.write_key_value_int("conquers", militarysite->get_conquers());
> + fw.close_element();
> + fw.write_key_value_int("max_soldiers", militarysite->get_max_number_of_soldiers());
> + fw.close_element();
> + fw.write_key_value_int("heal_per_second", militarysite->get_heal_per_second());
> + fw.close_element();
> + }
> +
> + switch (building.type()) {
> + case MapObjectType::PRODUCTIONSITE:
> + fw.write_key_value_string("type", "productionsite");
> + break;
> + case MapObjectType::WAREHOUSE:
> + fw.write_key_value_string("type", "warehouse");
> + break;
> + case MapObjectType::MILITARYSITE:
> + fw.write_key_value_string("type", "militarysite");
> + break;
> + case MapObjectType::TRAININGSITE:
> + fw.write_key_value_string("type", "trainingsite");
> + break;
> + default:
> + NEVER_HERE();
> + }
> + fw.close_element();
> +
> + // Size
> + if (building.type() == MapObjectType::WAREHOUSE &&
> + !building.is_buildable() && !building.is_enhanced()) {
> + fw.write_key_value_string("size", "headquarters");
> + } else if (building.get_ismine()) {
> + fw.write_key_value_string("size", "mine");
> + } else if (building.get_isport()) {
> + fw.write_key_value_string("size", "port");
> + } else {
> + fw.write_key_value_string("size", BaseImmovable::size_to_string(building.get_size()));
> + }
> + fw.close_element();
> +
> + // Helptext
> + try {
> + std::unique_ptr<LuaTable> table(
> + egbase.lua().run_script("tribes/scripting/mapobject_info/building_helptext.lua"));
> + std::unique_ptr<LuaCoroutine> cr(table->get_coroutine("func"));
> + cr->push_arg(building.helptext_script());
> + cr->resume();
> + const std::string help_text = cr->pop_string();
> + fw.write_key_value_string("helptext", help_text);
> + } catch (LuaError& err) {
> + fw.write_key_value_string("helptext", err.what());
> + }
> +
> + fw.close_brace(true, i, buildings.size()); // Building
> + }
> + fw.close_array(); // Buildings
> + fw.close_brace(); // Main
> + fw.write(*g_fs, (boost::format("%s/%s_buildings.json") % output_path % tribe.name()).str().c_str());
> + log("\n");
> +}
> +
> +/*
> + ==========================================================
> + WARES
> + ==========================================================
> + */
> +
> +void write_wares(const TribeDescr& tribe, EditorGameBase& egbase, const std::string& output_path) {
> + log("\n===============\nWriting wares:\n===============\n");
> + JSONFileWrite fw;
> + fw.open_brace(); // Main
> +
> + fw.open_array("wares"); // Wares
> + size_t counter = 0;
> + const size_t no_of_wares = tribe.wares().size();
> + for (DescriptionIndex ware_index : tribe.wares()) {
> + const WareDescr& ware = *tribe.get_ware_descr(ware_index);
> + log(" %s", ware.name().c_str());
> + fw.open_brace();
> + fw.write_key_value_string("name", ware.name());
> + fw.close_element();
> + fw.write_key_value_string("descname", ware.descname());
> + fw.close_element();
> + fw.write_key_value_string("icon", ware.icon_filename());
> + fw.close_element();
> +
> + // Helptext
> + try {
> + std::unique_ptr<LuaTable> table(
> + egbase.lua().run_script("tribes/scripting/mapobject_info/ware_helptext.lua"));
> + std::unique_ptr<LuaCoroutine> cr(table->get_coroutine("func"));
> + cr->push_arg(tribe.name());
> + cr->push_arg(ware.helptext_script());
> + cr->resume();
> + const std::string help_text = cr->pop_string();
> + fw.write_key_value_string("helptext", help_text);
> + } catch (LuaError& err) {
> + fw.write_key_value_string("helptext", err.what());
> + }
> + fw.close_brace(true, counter, no_of_wares); // Ware
> + ++counter;
> + }
> + fw.close_array(); // Wares
> +
> + fw.close_brace(); // Main
> + fw.write(*g_fs, (boost::format("%s/%s_wares.json") % output_path % tribe.name()).str().c_str());
> + log("\n");
> +}
> +
> +/*
> + ==========================================================
> + WORKERS
> + ==========================================================
> + */
> +
> +void write_workers(const TribeDescr& tribe, EditorGameBase& egbase, const std::string& output_path) {
> + log("\n================\nWriting workers:\n================\n");
> + JSONFileWrite fw;
> + fw.open_brace(); // Main
> +
> + fw.open_array("workers"); // Workers
> + size_t counter = 0;
> + const size_t no_of_workers = tribe.workers().size();
> + for (DescriptionIndex worker_index : tribe.workers()) {
> + const WorkerDescr& worker = *tribe.get_worker_descr(worker_index);
> + log(" %s", worker.name().c_str());
> + fw.open_brace();
> + fw.write_key_value_string("name", worker.name());
> + fw.close_element();
> + fw.write_key_value_string("descname", worker.descname());
> + fw.close_element();
> + fw.write_key_value_string("icon", worker.icon_filename());
> + fw.close_element();
> +
> + // Helptext
> + try {
> + std::unique_ptr<LuaTable> table(
> + egbase.lua().run_script("tribes/scripting/mapobject_info/worker_helptext.lua"));
> + std::unique_ptr<LuaCoroutine> cr(table->get_coroutine("func"));
> + cr->push_arg(worker.helptext_script());
> + cr->resume();
> + const std::string help_text = cr->pop_string();
> + fw.write_key_value_string("helptext", help_text);
> + } catch (LuaError& err) {
> + fw.write_key_value_string("helptext", err.what());
> + }
> +
> + if (worker.becomes() != INVALID_INDEX) {
> + fw.close_element();
> + const WorkerDescr& becomes = *tribe.get_worker_descr(worker.becomes());
> + fw.write_key("becomes");
> + fw.open_brace();
> + fw.write_key_value_string("name", becomes.name());
> + fw.close_element();
> + fw.write_key_value_int("experience", worker.get_needed_experience());
> + fw.close_brace(true);
> + }
> + fw.close_brace(true, counter, no_of_workers); // Worker
> + ++counter;
> + }
> + fw.close_array(); // Workers
> +
> + fw.close_brace(); // Main
> + fw.write(*g_fs, (boost::format("%s/%s_workers.json") % output_path % tribe.name()).str().c_str());
> + log("\n");
> +}
> +
> +/*
> + ==========================================================
> + TRIBES
> + ==========================================================
> + */
> +
> +void add_tribe_info(const TribeBasicInfo& tribe_info, JSONFileWrite* fw) {
> + fw->write_key_value_string("name", tribe_info.name);
> + fw->close_element();
> + fw->write_key_value_string("descname", tribe_info.descname);
> + fw->close_element();
> + fw->write_key_value_string("author", tribe_info.author);
> + fw->close_element();
> + fw->write_key_value_string("tooltip", tribe_info.tooltip);
> + fw->close_element();
> + fw->write_key_value_string("icon", tribe_info.icon);
> +}
> +
> +void write_tribes(EditorGameBase& egbase, const std::string& output_path) {
> + JSONFileWrite fw;
> + fw.open_brace(); // Main
> + fw.open_array("tribes"); // Tribes
> +
> + /// Tribes
> + egbase.mutable_tribes()->postload(); // Make sure that all values have been set.
> + const Tribes& tribes = egbase.tribes();
> +
> + std::vector<TribeBasicInfo> tribeinfos = tribes.get_all_tribeinfos();
> + for (size_t tribe_index = 0; tribe_index < tribeinfos.size(); ++tribe_index) {
> + const TribeBasicInfo& tribe_info = tribeinfos[tribe_index];
> + log("\n\n=========================\nWriting tribe: %s\n=========================\n",
> + tribe_info.name.c_str());
> +
> + fw.open_brace(); // TribeDescr
> + add_tribe_info(tribe_info, &fw);
> + fw.close_brace(true, tribe_index, tribeinfos.size()); // TribeDescr
> +
> + // These go in separate files
> +
> + JSONFileWrite fw_tribe;
> + fw_tribe.open_brace(); // TribeDescr
> + add_tribe_info(tribe_info, &fw_tribe);
> + fw_tribe.close_brace(true); // TribeDescr
> + fw_tribe.write(*g_fs,
> + (boost::format("%s/tribe_%s.json") % output_path % tribe_info.name).str().c_str());
> +
> + const TribeDescr& tribe =
> + *tribes.get_tribe_descr(tribes.tribe_index(tribe_info.name));
> +
> + write_buildings(tribe, egbase, output_path);
> + write_wares(tribe, egbase, output_path);
> + write_workers(tribe, egbase, output_path);
> + }
> + fw.close_array(); // Tribes
> + fw.close_brace(); // Main
> + fw.write(*g_fs, (boost::format("%s/tribes.json") % output_path).str().c_str());
> +}
> +
> +} // namespace
> +
> +/*
> + ==========================================================
> + MAIN
> + ==========================================================
> + */
> +
> +int main(int argc, char ** argv)
> +{
> + if (!(2 <= argc && argc <= 3)) {
This allows 1 or 2 arguments (not counting argv[0]), but only one is ever used?
> + log("Usage: %s <existing-absolute-output-path>\n", argv[0]);
> + return 1;
> + }
> +
> + const std::string output_path = argv[argc - 1];
> +
> + try {
> + initialize(output_path);
> + EditorGameBase egbase(nullptr);
> + write_tribes(egbase, output_path);
> + }
> + catch (std::exception& e) {
> + log("Exception: %s.\n", e.what());
> + g_sound_handler.shutdown();
> + return 1;
> + }
> + g_sound_handler.shutdown();
> + return 0;
> +}
>
> === added file 'utils/validate_json.py'
> --- utils/validate_json.py 1970-01-01 00:00:00 +0000
> +++ utils/validate_json.py 2016-05-14 07:36:19 +0000
> @@ -0,0 +1,54 @@
> +#!/usr/bin/env python
> +# encoding: utf-8
> +
> +import codecs
> +import json
> +import os.path
> +import sys
> +
> +# Tests if the .json files in the directories on the bottom are valid JSON files
> +
> +def validate_files_in_path(source_path):
> +
> + if (not os.path.isdir(source_path)):
> + print("Error: Path " + source_path + " not found.")
> + sys.exit(1)
> +
> + source_files = sorted(os.listdir(source_path), key=str.lower)
> +
> + print("Reading JSON files in:\n " + source_path)
> + failed = 0
> + for source_filename in source_files:
> + file_path = source_path + "/" + source_filename
> + if source_filename.endswith(".json"):
> + source_file = open(file_path, "r")
> + try:
> + dataset = json.load(source_file)
> + except ValueError as err:
> + failed = failed + 1
> + print("\n Error reading " + source_filename + ":");
> + print(" " + str(err))
> +
> + if failed == 0:
> + print("\nAll JSON files are OK.")
> + else:
> + if failed == 1:
> + print("\n" + str(failed) + " file is not valid JSON!");
> + else:
> + print("\n" + str(failed) + " files are not valid JSON!");
> + return failed < 1
> +
> +sucess = False
> +
> +if (len(sys.argv) == 2):
> + base_path = os.path.normpath(sys.argv[1])
> + if (not os.path.exists(base_path) or os.path.isfile(base_path)):
> + base_path = os.path.abspath(os.path.join(os.path.dirname(__file__),os.path.pardir),base_path)
I get an error here:
TypeError: abspath() takes exactly 1 argument (2 given)
> +
> + if (os.path.exists(base_path) and not os.path.isfile(base_path)):
> + success = validate_files_in_path(os.path.normpath(base_path))
> +
> +if success:
> + sys.exit(0)
> +else:
> + sys.exit(1)
--
https://code.launchpad.net/~widelands-dev/widelands/map_object_info/+merge/287409
Your team Widelands Developers is subscribed to branch lp:~widelands-dev/widelands/map_object_info.
References