summaryrefslogtreecommitdiff
path: root/src/serialization.h
blob: ab6fe0f791201ed19392e1b124eea8ec70474f5d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef SERIALIZATION_HEADER
#define SERIALIZATION_HEADER

#include "irrlichttypes.h"
#include "exceptions.h"
#include <iostream>
#include "util/pointer.h"

/*
	Map format serialization version
	--------------------------------

	For map data (blocks, nodes, sectors).
	
	NOTE: The goal is to increment this so that saved maps will be
	      loadable by any version. Other compatibility is not
		  maintained.
		  
	0: original networked test with 1-byte nodes
	1: update with 2-byte nodes
	2: lighting is transmitted in param
	3: optional fetching of far blocks
	4: block compression
	5: sector objects NOTE: block compression was left accidentally out
	6: failed attempt at switching block compression on again
	7: block compression switched on again
	8: server-initiated block transfers and all kinds of stuff
	9: block objects
	10: water pressure
	11: zlib'd blocks, block flags
	12: UnlimitedHeightmap now uses interpolated areas
	13: Mapgen v2
	14: NodeMetadata
	15: StaticObjects
	16: larger maximum size of node metadata, and compression
	17: MapBlocks contain timestamp
	18: new generator (not really necessary, but it's there)
	19: new content type handling
	20: many existing content types translated to extended ones
	21: dynamic content type allocation
	22: minerals removed, facedir & wallmounted changed
	23: new node metadata format
	24: 16-bit node ids and node timers (never released as stable)
	25: Improved node timer format
	26: Never written; read the same as 25
*/
// This represents an uninitialized or invalid format
#define SER_FMT_VER_INVALID 255
// Highest supported serialization version
#define SER_FMT_VER_HIGHEST_READ 26
// Saved on disk version
#define SER_FMT_VER_HIGHEST_WRITE 25
// Lowest supported serialization version
#define SER_FMT_VER_LOWEST 0
// Lowest client supported serialization version
// Can't do < 24 anymore; we have 16-bit dynamically allocated node IDs
// in memory; conversion just won't work in this direction.
#define SER_FMT_CLIENT_VER_LOWEST 24

inline bool ser_ver_supported(s32 v) {
	return v >= SER_FMT_VER_LOWEST && v <= SER_FMT_VER_HIGHEST_READ;
}

/*
	Misc. serialization functions
*/

void compressZlib(SharedBuffer<u8> data, std::ostream &os, int level = -1);
void compressZlib(const std::string &data, std::ostream &os, int level = -1);
void decompressZlib(std::istream &is, std::ostream &os);

// These choose between zlib and a self-made one according to version
void compress(SharedBuffer<u8> data, std::ostream &os, u8 version);
//void compress(const std::string &data, std::ostream &os, u8 version);
void decompress(std::istream &is, std::ostream &os, u8 version);

#endif

l kwb">bool getGameConfig(const std::string &game_path, Settings &conf) { std::string conf_path = game_path + DIR_DELIM + "game.conf"; return conf.readConfigFile(conf_path.c_str()); } std::string getGameName(const std::string &game_path) { Settings conf; if(!getGameConfig(game_path, conf)) return ""; if(!conf.exists("name")) return ""; return conf.get("name"); } struct GameFindPath { std::string path; bool user_specific; GameFindPath(const std::string &path, bool user_specific): path(path), user_specific(user_specific) {} }; SubgameSpec findSubgame(const std::string &id) { if(id == "") return SubgameSpec(); std::string share = porting::path_share; std::string user = porting::path_user; std::vector<GameFindPath> find_paths; find_paths.push_back(GameFindPath( user + DIR_DELIM + "games" + DIR_DELIM + id + "_game", true)); find_paths.push_back(GameFindPath( user + DIR_DELIM + "games" + DIR_DELIM + id, true)); find_paths.push_back(GameFindPath( share + DIR_DELIM + "games" + DIR_DELIM + id + "_game", false)); find_paths.push_back(GameFindPath( share + DIR_DELIM + "games" + DIR_DELIM + id, false)); // Find game directory std::string game_path; bool user_game = true; // Game is in user's directory for(u32 i=0; i<find_paths.size(); i++){ const std::string &try_path = find_paths[i].path; if(fs::PathExists(try_path)){ game_path = try_path; user_game = find_paths[i].user_specific; break; } } if(game_path == "") return SubgameSpec(); std::string gamemod_path = game_path + DIR_DELIM + "mods"; // Find mod directories std::set<std::string> mods_paths; if(!user_game) mods_paths.insert(share + DIR_DELIM + "mods"); if(user != share || user_game) mods_paths.insert(user + DIR_DELIM + "mods"); std::string game_name = getGameName(game_path); if(game_name == "") game_name = id; std::string menuicon_path; #ifndef SERVER menuicon_path = getImagePath(game_path + DIR_DELIM + "menu" + DIR_DELIM + "icon.png"); #endif return SubgameSpec(id, game_path, gamemod_path, mods_paths, game_name, menuicon_path); } SubgameSpec findWorldSubgame(const std::string &world_path) { std::string world_gameid = getWorldGameId(world_path, true); // See if world contains an embedded game; if so, use it. std::string world_gamepath = world_path + DIR_DELIM + "game"; if(fs::PathExists(world_gamepath)){ SubgameSpec gamespec; gamespec.id = world_gameid; gamespec.path = world_gamepath; gamespec.gamemods_path= world_gamepath + DIR_DELIM + "mods"; gamespec.name = getGameName(world_gamepath); if(gamespec.name == "") gamespec.name = "unknown"; return gamespec; } return findSubgame(world_gameid); } std::set<std::string> getAvailableGameIds() { std::set<std::string> gameids; std::set<std::string> gamespaths; gamespaths.insert(porting::path_share + DIR_DELIM + "games"); gamespaths.insert(porting::path_user + DIR_DELIM + "games"); for(std::set<std::string>::const_iterator i = gamespaths.begin(); i != gamespaths.end(); i++){ std::vector<fs::DirListNode> dirlist = fs::GetDirListing(*i); for(u32 j=0; j<dirlist.size(); j++){ if(!dirlist[j].dir) continue; // If configuration file is not found or broken, ignore game Settings conf; if(!getGameConfig(*i + DIR_DELIM + dirlist[j].name, conf)) continue; // Add it to result const char *ends[] = {"_game", NULL}; std::string shorter = removeStringEnd(dirlist[j].name, ends); if(shorter != "") gameids.insert(shorter); else gameids.insert(dirlist[j].name); } } return gameids; } std::vector<SubgameSpec> getAvailableGames() { std::vector<SubgameSpec> specs; std::set<std::string> gameids = getAvailableGameIds(); for(std::set<std::string>::const_iterator i = gameids.begin(); i != gameids.end(); i++) specs.push_back(findSubgame(*i)); return specs; } #define LEGACY_GAMEID "minetest" bool getWorldExists(const std::string &world_path) { return (fs::PathExists(world_path + DIR_DELIM + "map_meta.txt") || fs::PathExists(world_path + DIR_DELIM + "world.mt")); } std::string getWorldGameId(const std::string &world_path, bool can_be_legacy) { std::string conf_path = world_path + DIR_DELIM + "world.mt"; Settings conf; bool succeeded = conf.readConfigFile(conf_path.c_str()); if(!succeeded){ if(can_be_legacy){ // If map_meta.txt exists, it is probably an old minetest world if(fs::PathExists(world_path + DIR_DELIM + "map_meta.txt")) return LEGACY_GAMEID; } return ""; } if(!conf.exists("gameid")) return ""; // The "mesetint" gameid has been discarded if(conf.get("gameid") == "mesetint") return "minetest"; return conf.get("gameid"); } std::vector<WorldSpec> getAvailableWorlds() { std::vector<WorldSpec> worlds; std::set<std::string> worldspaths; worldspaths.insert(porting::path_user + DIR_DELIM + "worlds"); infostream<<"Searching worlds..."<<std::endl; for(std::set<std::string>::const_iterator i = worldspaths.begin(); i != worldspaths.end(); i++){ infostream<<" In "<<(*i)<<": "<<std::endl; std::vector<fs::DirListNode> dirvector = fs::GetDirListing(*i); for(u32 j=0; j<dirvector.size(); j++){ if(!dirvector[j].dir) continue; std::string fullpath = *i + DIR_DELIM + dirvector[j].name; std::string name = dirvector[j].name; // Just allow filling in the gameid always for now bool can_be_legacy = true; std::string gameid = getWorldGameId(fullpath, can_be_legacy); WorldSpec spec(fullpath, name, gameid); if(!spec.isValid()){ infostream<<"(invalid: "<<name<<") "; } else { infostream<<name<<" "; worlds.push_back(spec); } } infostream<<std::endl; } // Check old world location do{ std::string fullpath = porting::path_user + DIR_DELIM + "world"; if(!fs::PathExists(fullpath)) break; std::string name = "Old World"; std::string gameid = getWorldGameId(fullpath, true); WorldSpec spec(fullpath, name, gameid); infostream<<"Old world found."<<std::endl; worlds.push_back(spec); }while(0); infostream<<worlds.size()<<" found."<<std::endl; return worlds; } bool initializeWorld(const std::string &path, const std::string &gameid) { infostream<<"Initializing world at "<<path<<std::endl; // Create world.mt if does not already exist std::string worldmt_path = path + DIR_DELIM + "world.mt"; if(!fs::PathExists(worldmt_path)){ infostream<<"Creating world.mt ("<<worldmt_path<<")"<<std::endl; fs::CreateAllDirs(path); std::ofstream of(worldmt_path.c_str(), std::ios::binary); of<<"gameid = "<<gameid<<"\n"; } return true; }