Browse Source

Allow ObjDefManager instances to be cloned

sfan5 4 years ago
parent
commit
2062c80e21

+ 45 - 0
src/mapgen/mg_biome.cpp

@@ -92,6 +92,16 @@ void BiomeManager::clear()
 }
 
 
+BiomeManager *BiomeManager::clone() const
+{
+	auto mgr = new BiomeManager();
+	assert(mgr);
+	ObjDefManager::cloneTo(mgr);
+	mgr->m_server = m_server;
+	return mgr;
+}
+
+
 // For BiomeGen type 'BiomeGenOriginal'
 float BiomeManager::getHeatAtPosOriginal(v3s16 pos, NoiseParams &np_heat,
 	NoiseParams &np_heat_blend, u64 seed)
@@ -321,6 +331,41 @@ Biome *BiomeGenOriginal::calcBiomeFromNoise(float heat, float humidity, v3s16 po
 
 ////////////////////////////////////////////////////////////////////////////////
 
+ObjDef *Biome::clone() const
+{
+	auto obj = new Biome();
+	ObjDef::cloneTo(obj);
+	NodeResolver::cloneTo(obj);
+
+	obj->flags = flags;
+
+	obj->c_top = c_top;
+	obj->c_filler = c_filler;
+	obj->c_stone = c_stone;
+	obj->c_water_top = c_water_top;
+	obj->c_water = c_water;
+	obj->c_river_water = c_river_water;
+	obj->c_riverbed = c_riverbed;
+	obj->c_dust = c_dust;
+	obj->c_cave_liquid = c_cave_liquid;
+	obj->c_dungeon = c_dungeon;
+	obj->c_dungeon_alt = c_dungeon_alt;
+	obj->c_dungeon_stair = c_dungeon_stair;
+
+	obj->depth_top = depth_top;
+	obj->depth_filler = depth_filler;
+	obj->depth_water_top = depth_water_top;
+	obj->depth_riverbed = depth_riverbed;
+
+	obj->min_pos = min_pos;
+	obj->max_pos = max_pos;
+	obj->heat_point = heat_point;
+	obj->humidity_point = humidity_point;
+	obj->vertical_blend = vertical_blend;
+
+	return obj;
+}
+
 void Biome::resolveNodeNames()
 {
 	getIdFromNrBacklog(&c_top,           "mapgen_stone",              CONTENT_AIR,    false);

+ 6 - 0
src/mapgen/mg_biome.h

@@ -42,6 +42,8 @@ enum BiomeType {
 
 class Biome : public ObjDef, public NodeResolver {
 public:
+	ObjDef *clone() const;
+
 	u32 flags;
 
 	content_t c_top;
@@ -191,6 +193,8 @@ public:
 	BiomeManager(Server *server);
 	virtual ~BiomeManager() = default;
 
+	BiomeManager *clone() const;
+
 	const char *getObjectTitle() const
 	{
 		return "biome";
@@ -232,6 +236,8 @@ public:
 	Biome *getBiomeFromNoiseOriginal(float heat, float humidity, v3s16 pos);
 
 private:
+	BiomeManager() {};
+
 	Server *m_server;
 
 };

+ 55 - 0
src/mapgen/mg_decoration.cpp

@@ -67,6 +67,13 @@ size_t DecorationManager::placeAllDecos(Mapgen *mg, u32 blockseed,
 	return nplaced;
 }
 
+DecorationManager *DecorationManager::clone() const
+{
+	auto mgr = new DecorationManager();
+	ObjDefManager::cloneTo(mgr);
+	return mgr;
+}
+
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -269,9 +276,42 @@ size_t Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
 }
 
 
+void Decoration::cloneTo(Decoration *def) const
+{
+	ObjDef::cloneTo(def);
+	def->flags = flags;
+	def->mapseed = mapseed;
+	def->c_place_on = c_place_on;
+	def->sidelen = sidelen;
+	def->y_min = y_min;
+	def->y_max = y_max;
+	def->fill_ratio = fill_ratio;
+	def->np = np;
+	def->c_spawnby = c_spawnby;
+	def->nspawnby = nspawnby;
+	def->place_offset_y = place_offset_y;
+	def->biomes = biomes;
+}
+
+
 ///////////////////////////////////////////////////////////////////////////////
 
 
+ObjDef *DecoSimple::clone() const
+{
+	auto def = new DecoSimple();
+	Decoration::cloneTo(def);
+
+	def->c_decos = c_decos;
+	def->deco_height = deco_height;
+	def->deco_height_max = deco_height_max;
+	def->deco_param2 = deco_param2;
+	def->deco_param2_max = deco_param2_max;
+
+	return def;
+}
+
+
 void DecoSimple::resolveNodeNames()
 {
 	Decoration::resolveNodeNames();
@@ -351,6 +391,21 @@ size_t DecoSimple::generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling)
 ///////////////////////////////////////////////////////////////////////////////
 
 
+ObjDef *DecoSchematic::clone() const
+{
+	auto def = new DecoSchematic();
+	Decoration::cloneTo(def);
+	NodeResolver::cloneTo(def);
+
+	def->rotation = rotation;
+	/* FIXME: This is not ideal, we only have a pointer to the schematic despite
+	 * not owning it. Optimally this would be a handle. */
+	def->schematic = schematic; // not cloned
+
+	return def;
+}
+
+
 size_t DecoSchematic::generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling)
 {
 	// Schematic could have been unloaded but not the decoration

+ 12 - 0
src/mapgen/mg_decoration.h

@@ -73,11 +73,16 @@ public:
 	s16 place_offset_y = 0;
 
 	std::unordered_set<u8> biomes;
+
+protected:
+	void cloneTo(Decoration *def) const;
 };
 
 
 class DecoSimple : public Decoration {
 public:
+	ObjDef *clone() const;
+
 	virtual void resolveNodeNames();
 	virtual size_t generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling);
 
@@ -91,6 +96,8 @@ public:
 
 class DecoSchematic : public Decoration {
 public:
+	ObjDef *clone() const;
+
 	DecoSchematic() = default;
 
 	virtual size_t generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling);
@@ -113,6 +120,8 @@ public:
 	DecorationManager(IGameDef *gamedef);
 	virtual ~DecorationManager() = default;
 
+	DecorationManager *clone() const;
+
 	const char *getObjectTitle() const
 	{
 		return "decoration";
@@ -133,4 +142,7 @@ public:
 	}
 
 	size_t placeAllDecos(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
+
+private:
+	DecorationManager() {};
 };

+ 97 - 0
src/mapgen/mg_ore.cpp

@@ -72,6 +72,14 @@ void OreManager::clear()
 }
 
 
+OreManager *OreManager::clone() const
+{
+	auto mgr = new OreManager();
+	ObjDefManager::cloneTo(mgr);
+	return mgr;
+}
+
+
 ///////////////////////////////////////////////////////////////////////////////
 
 
@@ -106,9 +114,37 @@ size_t Ore::placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
 }
 
 
+void Ore::cloneTo(Ore *def) const
+{
+	ObjDef::cloneTo(def);
+	NodeResolver::cloneTo(def);
+	def->c_ore = c_ore;
+	def->c_wherein = c_wherein;
+	def->clust_scarcity = clust_scarcity;
+	def->clust_num_ores = clust_num_ores;
+	def->clust_size = clust_size;
+	def->y_min = y_min;
+	def->y_max = y_max;
+	def->ore_param2 = ore_param2;
+	def->flags = flags;
+	def->nthresh = nthresh;
+	def->np = np;
+	def->noise = nullptr; // cannot be shared! so created on demand
+	def->biomes = biomes;
+}
+
+
 ///////////////////////////////////////////////////////////////////////////////
 
 
+ObjDef *OreScatter::clone() const
+{
+	auto def = new OreScatter();
+	Ore::cloneTo(def);
+	return def;
+}
+
+
 void OreScatter::generate(MMVManip *vm, int mapseed, u32 blockseed,
 	v3s16 nmin, v3s16 nmax, u8 *biomemap)
 {
@@ -158,6 +194,19 @@ void OreScatter::generate(MMVManip *vm, int mapseed, u32 blockseed,
 ///////////////////////////////////////////////////////////////////////////////
 
 
+ObjDef *OreSheet::clone() const
+{
+	auto def = new OreSheet();
+	Ore::cloneTo(def);
+
+	def->column_height_max = column_height_max;
+	def->column_height_min = column_height_min;
+	def->column_midpoint_factor = column_midpoint_factor;
+
+	return def;
+}
+
+
 void OreSheet::generate(MMVManip *vm, int mapseed, u32 blockseed,
 	v3s16 nmin, v3s16 nmax, u8 *biomemap)
 {
@@ -221,6 +270,20 @@ OrePuff::~OrePuff()
 }
 
 
+ObjDef *OrePuff::clone() const
+{
+	auto def = new OrePuff();
+	Ore::cloneTo(def);
+
+	def->np_puff_top = np_puff_top;
+	def->np_puff_bottom = np_puff_bottom;
+	def->noise_puff_top = nullptr; // cannot be shared, on-demand
+	def->noise_puff_bottom = nullptr;
+
+	return def;
+}
+
+
 void OrePuff::generate(MMVManip *vm, int mapseed, u32 blockseed,
 	v3s16 nmin, v3s16 nmax, u8 *biomemap)
 {
@@ -294,6 +357,14 @@ void OrePuff::generate(MMVManip *vm, int mapseed, u32 blockseed,
 ///////////////////////////////////////////////////////////////////////////////
 
 
+ObjDef *OreBlob::clone() const
+{
+	auto def = new OreBlob();
+	Ore::cloneTo(def);
+	return def;
+}
+
+
 void OreBlob::generate(MMVManip *vm, int mapseed, u32 blockseed,
 	v3s16 nmin, v3s16 nmax, u8 *biomemap)
 {
@@ -366,6 +437,19 @@ OreVein::~OreVein()
 }
 
 
+ObjDef *OreVein::clone() const
+{
+	auto def = new OreVein();
+	Ore::cloneTo(def);
+
+	def->random_factor = random_factor;
+	def->noise2 = nullptr; // cannot be shared, on-demand
+	def->sizey_prev = sizey_prev;
+
+	return def;
+}
+
+
 void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed,
 	v3s16 nmin, v3s16 nmax, u8 *biomemap)
 {
@@ -434,6 +518,19 @@ OreStratum::~OreStratum()
 }
 
 
+ObjDef *OreStratum::clone() const
+{
+	auto def = new OreStratum();
+	Ore::cloneTo(def);
+
+	def->np_stratum_thickness = np_stratum_thickness;
+	def->noise_stratum_thickness = nullptr; // cannot be shared, on-demand
+	def->stratum_thickness = stratum_thickness;
+
+	return def;
+}
+
+
 void OreStratum::generate(MMVManip *vm, int mapseed, u32 blockseed,
 	v3s16 nmin, v3s16 nmax, u8 *biomemap)
 {

+ 20 - 0
src/mapgen/mg_ore.h

@@ -74,12 +74,17 @@ public:
 	size_t placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
 	virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
 		v3s16 nmin, v3s16 nmax, u8 *biomemap) = 0;
+
+protected:
+	void cloneTo(Ore *def) const;
 };
 
 class OreScatter : public Ore {
 public:
 	static const bool NEEDS_NOISE = false;
 
+	ObjDef *clone() const;
+
 	virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
 		v3s16 nmin, v3s16 nmax, u8 *biomemap);
 };
@@ -88,6 +93,8 @@ class OreSheet : public Ore {
 public:
 	static const bool NEEDS_NOISE = true;
 
+	ObjDef *clone() const;
+
 	u16 column_height_min;
 	u16 column_height_max;
 	float column_midpoint_factor;
@@ -100,6 +107,8 @@ class OrePuff : public Ore {
 public:
 	static const bool NEEDS_NOISE = true;
 
+	ObjDef *clone() const;
+
 	NoiseParams np_puff_top;
 	NoiseParams np_puff_bottom;
 	Noise *noise_puff_top = nullptr;
@@ -116,6 +125,8 @@ class OreBlob : public Ore {
 public:
 	static const bool NEEDS_NOISE = true;
 
+	ObjDef *clone() const;
+
 	virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
 		v3s16 nmin, v3s16 nmax, u8 *biomemap);
 };
@@ -124,6 +135,8 @@ class OreVein : public Ore {
 public:
 	static const bool NEEDS_NOISE = true;
 
+	ObjDef *clone() const;
+
 	float random_factor;
 	Noise *noise2 = nullptr;
 	int sizey_prev = 0;
@@ -139,6 +152,8 @@ class OreStratum : public Ore {
 public:
 	static const bool NEEDS_NOISE = false;
 
+	ObjDef *clone() const;
+
 	NoiseParams np_stratum_thickness;
 	Noise *noise_stratum_thickness = nullptr;
 	u16 stratum_thickness;
@@ -155,6 +170,8 @@ public:
 	OreManager(IGameDef *gamedef);
 	virtual ~OreManager() = default;
 
+	OreManager *clone() const;
+
 	const char *getObjectTitle() const
 	{
 		return "ore";
@@ -183,4 +200,7 @@ public:
 	void clear();
 
 	size_t placeAllOres(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
+
+private:
+	OreManager() {};
 };

+ 7 - 1
src/mapgen/mg_schematic.cpp

@@ -77,6 +77,11 @@ Schematic::~Schematic()
 	delete []slice_probs;
 }
 
+ObjDef *Schematic::clone() const
+{
+	FATAL_ERROR("not cloneable");
+}
+
 
 void Schematic::resolveNodeNames()
 {
@@ -93,6 +98,7 @@ void Schematic::resolveNodeNames()
 
 void Schematic::blitToVManip(MMVManip *vm, v3s16 p, Rotation rot, bool force_place)
 {
+	assert(schemdata && slice_probs);
 	sanity_check(m_ndef != NULL);
 
 	int xstride = 1;
@@ -177,7 +183,7 @@ bool Schematic::placeOnVManip(MMVManip *vm, v3s16 p, u32 flags,
 	Rotation rot, bool force_place)
 {
 	assert(vm != NULL);
-	assert(schemdata != NULL);
+	assert(schemdata && slice_probs);
 	sanity_check(m_ndef != NULL);
 
 	//// Determine effective rotation and effective schematic dimensions

+ 4 - 0
src/mapgen/mg_schematic.h

@@ -95,6 +95,8 @@ public:
 	Schematic();
 	virtual ~Schematic();
 
+	ObjDef *clone() const;
+
 	virtual void resolveNodeNames();
 
 	bool loadSchematicFromFile(const std::string &filename,
@@ -128,6 +130,8 @@ public:
 	SchematicManager(Server *server);
 	virtual ~SchematicManager() = default;
 
+	// not cloneable
+
 	virtual void clear();
 
 	const char *getObjectTitle() const

+ 12 - 0
src/nodedef.cpp

@@ -1572,6 +1572,18 @@ NodeResolver::~NodeResolver()
 }
 
 
+void NodeResolver::cloneTo(NodeResolver *res) const
+{
+	FATAL_ERROR_IF(!m_resolve_done, "NodeResolver can only be cloned"
+		" after resolving has completed");
+	/* We don't actually do anything significant. Since the node resolving has
+	 * already completed, the class that called us will already have the
+	 * resolved IDs in its data structures (which it copies on its own) */
+	res->m_ndef = m_ndef;
+	res->m_resolve_done = true;
+}
+
+
 void NodeResolver::nodeResolveInternal()
 {
 	m_nodenames_idx   = 0;

+ 3 - 0
src/nodedef.h

@@ -739,6 +739,9 @@ public:
 	virtual ~NodeResolver();
 	virtual void resolveNodeNames() = 0;
 
+	// required because this class is used as mixin for ObjDef
+	void cloneTo(NodeResolver *res) const;
+
 	bool getIdFromNrBacklog(content_t *result_out,
 		const std::string &node_alt, content_t c_fallback,
 		bool error_on_fallback = true);

+ 19 - 0
src/objdef.cpp

@@ -182,3 +182,22 @@ bool ObjDefManager::decodeHandle(ObjDefHandle handle, u32 *index,
 	*uid   = get_bits(handle, 24, 7);
 	return true;
 }
+
+// Cloning
+
+void ObjDef::cloneTo(ObjDef *def) const
+{
+	def->index = index;
+	def->uid = uid;
+	def->handle = handle;
+	def->name = name;
+}
+
+void ObjDefManager::cloneTo(ObjDefManager *mgr) const
+{
+	mgr->m_ndef = m_ndef;
+	mgr->m_objects.reserve(m_objects.size());
+	for (const auto &obj : m_objects)
+		mgr->m_objects.push_back(obj->clone());
+	mgr->m_objtype = m_objtype;
+}

+ 18 - 0
src/objdef.h

@@ -45,10 +45,22 @@ class ObjDef {
 public:
 	virtual ~ObjDef() = default;
 
+	// Only implemented by child classes (leafs in class hierarchy)
+	// Should create new object of its own type, call cloneTo() of parent class
+	// and copy its own instance variables over
+	virtual ObjDef *clone() const = 0;
+
 	u32 index;
 	u32 uid;
 	ObjDefHandle handle;
 	std::string name;
+
+protected:
+	// Only implemented by classes that have children themselves
+	// by copying the defintion and changing that argument type (!!!)
+	// Should defer to parent class cloneTo() if applicable and then copy
+	// over its own properties
+	void cloneTo(ObjDef *def) const;
 };
 
 // WARNING: Ownership of ObjDefs is transferred to the ObjDefManager it is
@@ -60,6 +72,8 @@ public:
 	virtual ~ObjDefManager();
 	DISABLE_CLASS_COPY(ObjDefManager);
 
+	// T *clone() const; // implemented in child class with correct type
+
 	virtual const char *getObjectTitle() const { return "ObjDef"; }
 
 	virtual void clear();
@@ -88,6 +102,10 @@ public:
 		ObjDefType *type, u32 *uid);
 
 protected:
+	ObjDefManager() {};
+	// Helper for child classes to implement clone()
+	void cloneTo(ObjDefManager *mgr) const;
+
 	const NodeDefManager *m_ndef;
 	std::vector<ObjDef *> m_objects;
 	ObjDefType m_objtype;

+ 72 - 4
src/unittest/test_objdef.cpp

@@ -32,6 +32,7 @@ public:
 
 	void testHandles();
 	void testAddGetSetClear();
+	void testClone();
 };
 
 static TestObjDef g_test_instance;
@@ -40,10 +41,42 @@ void TestObjDef::runTests(IGameDef *gamedef)
 {
 	TEST(testHandles);
 	TEST(testAddGetSetClear);
+	TEST(testClone);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
+/* Minimal implementation of ObjDef and ObjDefManager subclass */
+
+class MyObjDef : public ObjDef
+{
+public:
+	ObjDef *clone() const
+	{
+		auto def = new MyObjDef();
+		ObjDef::cloneTo(def);
+		def->testvalue = testvalue;
+		return def;
+	};
+
+	u32 testvalue;
+};
+
+class MyObjDefManager : public ObjDefManager
+{
+public:
+	MyObjDefManager(ObjDefType type) : ObjDefManager(NULL, type){};
+	MyObjDefManager *clone() const
+	{
+		auto mgr = new MyObjDefManager();
+		ObjDefManager::cloneTo(mgr);
+		return mgr;
+	};
+
+protected:
+	MyObjDefManager(){};
+};
+
 void TestObjDef::testHandles()
 {
 	u32 uid = 0;
@@ -69,25 +102,25 @@ void TestObjDef::testAddGetSetClear()
 
 	UASSERTEQ(ObjDefType, testmgr.getType(), OBJDEF_GENERIC);
 
-	obj0 = new ObjDef;
+	obj0 = new MyObjDef;
 	obj0->name = "foobar";
 	hObj0 = testmgr.add(obj0);
 	UASSERT(hObj0 != OBJDEF_INVALID_HANDLE);
 	UASSERTEQ(u32, obj0->index, 0);
 
-	obj1 = new ObjDef;
+	obj1 = new MyObjDef;
 	obj1->name = "FooBaz";
 	hObj1 = testmgr.add(obj1);
 	UASSERT(hObj1 != OBJDEF_INVALID_HANDLE);
 	UASSERTEQ(u32, obj1->index, 1);
 
-	obj2 = new ObjDef;
+	obj2 = new MyObjDef;
 	obj2->name = "asdf";
 	hObj2 = testmgr.add(obj2);
 	UASSERT(hObj2 != OBJDEF_INVALID_HANDLE);
 	UASSERTEQ(u32, obj2->index, 2);
 
-	obj3 = new ObjDef;
+	obj3 = new MyObjDef;
 	obj3->name = "foobaz";
 	hObj3 = testmgr.add(obj3);
 	UASSERT(hObj3 == OBJDEF_INVALID_HANDLE);
@@ -104,3 +137,38 @@ void TestObjDef::testAddGetSetClear()
 	testmgr.clear();
 	UASSERTEQ(size_t, testmgr.getNumObjects(), 0);
 }
+
+void TestObjDef::testClone()
+{
+	MyObjDefManager testmgr(OBJDEF_GENERIC);
+	ObjDefManager *mgrcopy;
+	MyObjDef *obj, *temp2;
+	ObjDef *temp1;
+	ObjDefHandle hObj;
+
+	obj = new MyObjDef;
+	obj->testvalue = 0xee00ff11;
+	hObj = testmgr.add(obj);
+	UASSERT(hObj != OBJDEF_INVALID_HANDLE);
+
+	mgrcopy = testmgr.clone();
+	UASSERT(mgrcopy);
+	UASSERTEQ(ObjDefType, mgrcopy->getType(), testmgr.getType());
+	UASSERTEQ(size_t, mgrcopy->getNumObjects(), testmgr.getNumObjects());
+
+	// 1) check that the same handle is still valid on the copy
+	temp1 = mgrcopy->get(hObj);
+	UASSERT(temp1);
+	UASSERT(temp1 == mgrcopy->getRaw(0));
+	// 2) check that the copy has the correct C++ class
+	temp2 = dynamic_cast<MyObjDef *>(temp1);
+	UASSERT(temp2);
+	// 3) check that it was correctly copied
+	UASSERTEQ(u32, obj->testvalue, temp2->testvalue);
+	// 4) check that it was copied AT ALL (not the same)
+	UASSERT(obj != temp2);
+
+	testmgr.clear();
+	mgrcopy->clear();
+	delete mgrcopy;
+}