← Back to team overview

widelands-dev team mailing list archive

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