ソースを参照

Added option for saving generated maps on client side (#307)

* new configuration option 'general.saveRandomMaps'
* maps being saved to 'userCachePath/RandomMaps'
* no deletion of old random maps
* map filename generated based on template name and random seed
Alexander Shishkin 8 年 前
コミット
7e1b0d71c5

+ 2 - 3
client/Client.cpp

@@ -408,10 +408,9 @@ void CClient::newGame( CConnection *con, StartInfo *si )
 
 	// Initialize game state
 	gs = new CGameState();
-	logNetwork->infoStream() <<"\tCreating gamestate: "<<tmh.getDiff();
+	logNetwork->info("\tCreating gamestate: %i",tmh.getDiff());
 
-	gs->scenarioOps = si;
-	gs->init(si);
+	gs->init(si, settings["general"]["saveRandomMaps"].Bool());
 	logNetwork->infoStream() <<"Initializing GameState (together): "<<tmh.getDiff();
 
 	// Now after possible random map gen, we know exact player count.

+ 7 - 3
config/schemas/settings.json

@@ -5,8 +5,8 @@
 	"$schema": "http://json-schema.org/draft-04/schema",
 	"required" : [ "general", "video", "adventure", "pathfinder", "battle", "server", "logging", "launcher" ],
 	"definitions" : {
-		"logLevelEnum" : { 
-			"type" : "string", 
+		"logLevelEnum" : {
+			"type" : "string",
 			"enum" : [ "trace", "debug", "info", "warn", "error" ]
 		}
 	},
@@ -17,7 +17,7 @@
 			"type" : "object",
 			"default": {},
 			"additionalProperties" : false,
-			"required" : [ "playerName", "showfps", "music", "sound", "encoding", "swipe" ],
+			"required" : [ "playerName", "showfps", "music", "sound", "encoding", "swipe", "saveRandomMaps" ],
 			"properties" : {
 				"playerName" : {
 					"type":"string",
@@ -43,6 +43,10 @@
 					"type" : "boolean",
 					"default" : false
 				},
+				"saveRandomMaps" : {
+					"type" : "boolean",
+					"default" : false
+				}
 			}
 		},
 		"video" : {

+ 35 - 4
lib/CGameState.cpp

@@ -24,8 +24,10 @@
 #include "rmg/CMapGenerator.h"
 #include "CStopWatch.h"
 #include "mapping/CMapEditManager.h"
+#include "mapping/CMapService.h"
 #include "serializer/CTypeList.h"
 #include "serializer/CMemorySerializer.h"
+#include "VCMIDirs.h"
 
 #ifdef min
 #undef min
@@ -698,7 +700,7 @@ CGameState::~CGameState()
 		ptr.second.dellNull();
 }
 
-void CGameState::init(StartInfo * si)
+void CGameState::init(StartInfo * si, bool allowSavingRandomMap)
 {
 	logGlobal->infoStream() << "\tUsing random seed: "<< si->seedToBeUsed;
 	getRandomGenerator().setSeed(si->seedToBeUsed);
@@ -709,7 +711,7 @@ void CGameState::init(StartInfo * si)
 	switch(scenarioOps->mode)
 	{
 	case StartInfo::NEW_GAME:
-		initNewGame();
+		initNewGame(allowSavingRandomMap);
 		break;
 	case StartInfo::CAMPAIGN:
 		initCampaign();
@@ -771,7 +773,7 @@ void CGameState::init(StartInfo * si)
 	}
 }
 
-void CGameState::initNewGame()
+void CGameState::initNewGame(bool allowSavingRandomMap)
 {
 	if(scenarioOps->createRandomMap())
 	{
@@ -780,8 +782,37 @@ void CGameState::initNewGame()
 
 		// Gen map
 		CMapGenerator mapGenerator;
-		map = mapGenerator.generate(scenarioOps->mapGenOptions.get(), scenarioOps->seedToBeUsed).release();
 
+		std::unique_ptr<CMap> randomMap = mapGenerator.generate(scenarioOps->mapGenOptions.get(), scenarioOps->seedToBeUsed);
+
+		if(allowSavingRandomMap)
+		{
+			try
+			{
+				auto path = VCMIDirs::get().userCachePath() / "RandomMaps";
+				boost::filesystem::create_directories(path);
+
+				std::shared_ptr<CMapGenOptions> options = scenarioOps->mapGenOptions;
+
+				const std::string templateName = options->getMapTemplate()->getName();
+				const ui32 seed = scenarioOps->seedToBeUsed;
+
+				const std::string fileName = boost::str(boost::format("%s_%d.vmap") % templateName % seed );
+				const auto fullPath = path / fileName;
+
+				CMapService::saveMap(randomMap, fullPath);
+
+				logGlobal->info("Random map has been saved to:");
+				logGlobal->info(fullPath.string());
+			}
+			catch(...)
+			{
+				logGlobal->error("Saving random map failed with exception");
+				handleException();
+			}
+		}
+
+		map = randomMap.release();
 		// Update starting options
 		for(int i = 0; i < map->players.size(); ++i)
 		{

+ 2 - 2
lib/CGameState.h

@@ -201,7 +201,7 @@ public:
 	CGameState();
 	virtual ~CGameState();
 
-	void init(StartInfo * si);
+	void init(StartInfo * si, bool allowSavingRandomMap = false);
 
 	ConstTransitivePtr<StartInfo> scenarioOps, initialOpts; //second one is a copy of settings received from pregame (not randomized)
 	PlayerColor currentPlayer; //ID of player currently having turn
@@ -283,7 +283,7 @@ private:
 
 	// ----- initialization -----
 
-	void initNewGame();
+	void initNewGame(bool allowSavingRandomMap);
 	void initCampaign();
 	void initDuel();
 	void checkMapChecksum();

+ 18 - 0
lib/mapping/CMapService.cpp

@@ -5,6 +5,7 @@
 #include "../filesystem/CBinaryReader.h"
 #include "../filesystem/CCompressedStream.h"
 #include "../filesystem/CMemoryStream.h"
+#include "../filesystem/CMemoryBuffer.h"
 
 #include "CMap.h"
 
@@ -52,6 +53,23 @@ std::unique_ptr<CMapHeader> CMapService::loadMapHeader(const ui8 * buffer, int s
 	return header;
 }
 
+void CMapService::saveMap(const std::unique_ptr<CMap> & map, boost::filesystem::path fullPath)
+{
+	CMemoryBuffer serializeBuffer;
+	{
+		CMapSaverJson saver(&serializeBuffer);
+		saver.saveMap(map);
+	}
+	{
+		boost::filesystem::remove(fullPath);
+		boost::filesystem::ofstream tmp(fullPath, boost::filesystem::ofstream::binary);
+
+		tmp.write((const char *)serializeBuffer.getBuffer().data(),serializeBuffer.getSize());
+		tmp.flush();
+		tmp.close();
+	}
+}
+
 std::unique_ptr<CInputStream> CMapService::getStreamFromFS(const std::string & name)
 {
 	return CResourceHandler::get()->load(ResourceID(name, EResType::MAP));

+ 1 - 0
lib/mapping/CMapService.h

@@ -69,6 +69,7 @@ public:
 	 */
 	static std::unique_ptr<CMapHeader> loadMapHeader(const ui8 * buffer, int size, const std::string & name);
 
+	static void saveMap(const std::unique_ptr<CMap> & map, boost::filesystem::path fullPath);
 private:
 	/**
 	 * Gets a map input stream object specified by a map name.

+ 5 - 0
lib/mapping/MapFormatJson.cpp

@@ -1212,6 +1212,8 @@ void CMapSaverJson::saveMap(const std::unique_ptr<CMap>& map)
 
 void CMapSaverJson::writeHeader()
 {
+	logGlobal->trace("Saving header");
+
 	JsonNode header;
 	JsonSerializer handler(mapObjectResolver.get(), header);
 
@@ -1283,6 +1285,7 @@ JsonNode CMapSaverJson::writeTerrainLevel(const int index)
 
 void CMapSaverJson::writeTerrain()
 {
+	logGlobal->trace("Saving terrain");
 	//todo: multilevel map save support
 
 	JsonNode surface = writeTerrainLevel(0);
@@ -1297,12 +1300,14 @@ void CMapSaverJson::writeTerrain()
 
 void CMapSaverJson::writeObjects()
 {
+	logGlobal->trace("Saving objects");
 	JsonNode data(JsonNode::DATA_STRUCT);
 
 	JsonSerializer handler(mapObjectResolver.get(), data);
 
 	for(CGObjectInstance * obj : map->objects)
 	{
+		logGlobal->trace("\t%s", obj->instanceName);
 		auto temp = handler.enterStruct(obj->instanceName);
 
 		obj->serializeJson(handler);