diff options
Diffstat (limited to '')
-rw-r--r-- | MCServer/Plugins/APIDump/APIDesc.lua | 41 | ||||
-rw-r--r-- | MCServer/Plugins/Debuggers/Debuggers.lua | 27 | ||||
-rw-r--r-- | src/Bindings/ManualBindings.cpp | 46 | ||||
-rw-r--r-- | src/BlockArea.cpp | 64 | ||||
-rw-r--r-- | src/BlockArea.h | 10 | ||||
-rw-r--r-- | src/Blocks/BlockMobHead.h | 108 | ||||
-rw-r--r-- | src/Mobs/Wither.cpp | 26 | ||||
-rw-r--r-- | src/Mobs/Wither.h | 4 | ||||
-rw-r--r-- | src/Protocol/Protocol125.cpp | 8 | ||||
-rw-r--r-- | src/Protocol/Protocol17x.cpp | 10 | ||||
-rw-r--r-- | src/StringCompression.cpp | 8 | ||||
-rw-r--r-- | src/StringCompression.h | 4 | ||||
-rw-r--r-- | src/StringUtils.h | 4 | ||||
-rw-r--r-- | src/WorldStorage/SchematicFileSerializer.cpp | 33 |
14 files changed, 375 insertions, 18 deletions
diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index 28e7744ed..451b7364a 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -200,6 +200,8 @@ g_APIDesc = msFillAir = { Notes = "Dst is overwritten by Src only where Src has air blocks" }, msImprint = { Notes = "Src overwrites Dst anywhere where Dst has non-air blocks" }, msLake = { Notes = "Special mode for merging lake images" }, + msSpongePrint = { Notes = "Similar to msImprint, sponge block doesn't overwrite anything, all other blocks overwrite everything"}, + msMask = { Notes = "The blocks that are exactly the same are kept in Dst, all differing blocks are replaced by air"}, }, ConstantGroups = { @@ -247,6 +249,9 @@ g_APIDesc = <tr> <td> A </td><td> B </td><td> B </td><td> A </td><td> B </td> </tr> + <tr> + <td> A </td><td> A </td><td> A </td><td> A </td><td> A </td> + </td> </tbody></table> <p> @@ -260,7 +265,20 @@ g_APIDesc = <h3>Special strategies</h3> <p>For each strategy, evaluate the table rows from top downwards, the first match wins.</p> - + + <p> + <strong>msDifference</strong> - changes all the blocks which are the same to air. Otherwise the source block gets placed. + </p> + <table><tbody<tr> + <th colspan="2"> area block </th><th> </th><th> Notes </th> + </tr><tr> + <td> * </td><td> B </td><td> B </td><td> The blocks are different so we use block B </td> + </tr><tr> + <td> B </td><td> B </td><td> Air </td><td> The blocks are the same so we get air. </td> + </tr> + </tbody></table> + + <p> <strong>msLake</strong> - used for merging areas with lava and water lakes, in the appropriate generator. </p> @@ -293,7 +311,6 @@ g_APIDesc = </tr> </tbody></table> - <p> <strong>msSpongePrint</strong> - used for most prefab-generators to merge the prefabs. Similar to msImprint, but uses the sponge block as the NOP block instead, so that the prefabs may carve out air @@ -306,10 +323,26 @@ g_APIDesc = </tr><tr> <td> A </td><td> sponge </td><td> A </td><td> Sponge is the NOP block </td> </tr><tr> - <td> * </td><td> B </td><td> B </td><td> Everything else overwrites anything </td> + <td> * </td><td> B </td><td> B </td><td> Everything else overwrites anything </td> </tr> </tbody></table> - ]], + + <p> + <strong>msMask</strong> - the blocks that are the same in the other area are kept, all the + differing blocks are replaced with air. Meta is used in the comparison, too, two blocks of the + same type but different meta are considered different and thus replaced with air. + </p> + <table><tbody><tr> + <th colspan="2"> area block </th><th> </th><th> Notes </th> + </tr><tr> + <th> this </th><th> Src </th><th> result </th><th> </th> + </tr><tr> + <td> A </td><td> A </td><td> A </td><td> Same blocks are kept </td> + </tr><tr> + <td> A </td><td> non-A </td><td> air </td><td> Differing blocks are replaced with air </td> + </tr> + </tbody></table> +]], }, -- Merge strategies }, -- AdditionalInfo }, -- cBlockArea diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua index 2619bd6c4..064d5d772 100644 --- a/MCServer/Plugins/Debuggers/Debuggers.lua +++ b/MCServer/Plugins/Debuggers/Debuggers.lua @@ -69,12 +69,13 @@ function Initialize(Plugin) LOG("Initialized " .. Plugin:GetName() .. " v." .. Plugin:GetVersion()) - -- TestBlockAreas(); - -- TestSQLiteBindings(); - -- TestExpatBindings(); - -- TestPluginCalls(); + -- TestBlockAreas() + -- TestSQLiteBindings() + -- TestExpatBindings() + -- TestPluginCalls() TestBlockAreasString() + TestStringBase64() --[[ -- Test cCompositeChat usage in console-logging: @@ -252,6 +253,24 @@ end +function TestStringBase64() + -- Create a binary string: + local s = "" + for i = 0, 255 do + s = s .. string.char(i) + end + + -- Roundtrip through Base64: + local Base64 = Base64Encode(s) + local UnBase64 = Base64Decode(Base64) + + assert(UnBase64 == s) +end + + + + + function TestSQLiteBindings() LOG("Testing SQLite bindings..."); diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 9d1a367df..51b9f3e27 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -190,6 +190,50 @@ static int tolua_LOGERROR(lua_State * tolua_S) +static int tolua_Base64Encode(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if ( + !L.CheckParamString(1) || + !L.CheckParamEnd(2) + ) + { + return 0; + } + + AString Src; + L.GetStackValue(1, Src); + AString res = Base64Encode(Src); + L.Push(res); + return 1; +} + + + + + +static int tolua_Base64Decode(lua_State * tolua_S) +{ + cLuaState L(tolua_S); + if ( + !L.CheckParamString(1) || + !L.CheckParamEnd(2) + ) + { + return 0; + } + + AString Src; + L.GetStackValue(1, Src); + AString res = Base64Decode(Src); + L.Push(res); + return 1; +} + + + + + cPluginLua * GetLuaPlugin(lua_State * L) { // Get the plugin identification out of LuaState: @@ -2869,6 +2913,8 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "LOGWARN", tolua_LOGWARN); tolua_function(tolua_S, "LOGWARNING", tolua_LOGWARN); tolua_function(tolua_S, "LOGERROR", tolua_LOGERROR); + tolua_function(tolua_S, "Base64Encode", tolua_Base64Encode); + tolua_function(tolua_S, "Base64Decode", tolua_Base64Decode); tolua_beginmodule(tolua_S, "cFile"); tolua_function(tolua_S, "GetFolderContents", tolua_cFile_GetFolderContents); diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp index 2b950378a..60e4f11e5 100644 --- a/src/BlockArea.cpp +++ b/src/BlockArea.cpp @@ -173,6 +173,40 @@ static inline void MergeCombinatorSpongePrint(BLOCKTYPE & a_DstType, BLOCKTYPE a +/** Combinator used for cBlockArea::msDifference merging */ +static inline void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) +{ + if ((a_DstType == a_SrcType) && (a_DstMeta == a_SrcMeta)) + { + a_DstType = E_BLOCK_AIR; + a_DstMeta = 0; + } + else + { + a_DstType = a_SrcType; + a_DstMeta = a_SrcMeta; + } +} + + + + + +/** Combinator used for cBlockArea::msMask merging */ +static inline void MergeCombinatorMask(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) +{ + // If the blocks are the same, keep the dest; otherwise replace with air + if ((a_SrcType != a_DstType) || (a_SrcMeta != a_DstMeta)) + { + a_DstType = E_BLOCK_AIR; + a_DstMeta = 0; + } +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cBlockArea: @@ -709,6 +743,36 @@ void cBlockArea::Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_R ); break; } // case msSpongePrint + + case msDifference: + { + InternalMergeBlocks( + m_BlockTypes, a_Src.GetBlockTypes(), + DstMetas, SrcMetas, + SizeX, SizeY, SizeZ, + SrcOffX, SrcOffY, SrcOffZ, + DstOffX, DstOffY, DstOffZ, + a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), + m_Size.x, m_Size.y, m_Size.z, + MergeCombinatorDifference + ); + break; + } // case msDifference + + case msMask: + { + InternalMergeBlocks( + m_BlockTypes, a_Src.GetBlockTypes(), + DstMetas, SrcMetas, + SizeX, SizeY, SizeZ, + SrcOffX, SrcOffY, SrcOffZ, + DstOffX, DstOffY, DstOffZ, + a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), + m_Size.x, m_Size.y, m_Size.z, + MergeCombinatorMask + ); + break; + } // case msMask default: { diff --git a/src/BlockArea.h b/src/BlockArea.h index d37f0d182..c48175b8c 100644 --- a/src/BlockArea.h +++ b/src/BlockArea.h @@ -52,6 +52,8 @@ public: msImprint, msLake, msSpongePrint, + msDifference, + msMask, } ; cBlockArea(void); @@ -152,6 +154,14 @@ public: +----------+--------+--------+ | A | sponge | A | Sponge is the NOP block | * | B | B | Everything else overwrites anything + + msMask: + Combines two areas, the blocks that are the same are kept, differing ones are reset to air + | area block | | + | this | Src | result | + +------+-------+--------+ + | A | A | A | Same blocks are kept + | A | non-A | air | Everything else is replaced with air */ void Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy); diff --git a/src/Blocks/BlockMobHead.h b/src/Blocks/BlockMobHead.h index c4f41ba34..acd1c88fb 100644 --- a/src/Blocks/BlockMobHead.h +++ b/src/Blocks/BlockMobHead.h @@ -22,14 +22,99 @@ public: a_Pickups.push_back(cItem(E_ITEM_HEAD, 1, 0)); } - bool TrySpawnWither(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ) + bool TrySpawnWither(cChunkInterface & a_ChunkInterface, cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ) { if (a_BlockY < 2) { return false; } + + class cCallback : public cMobHeadCallback + { + bool m_IsWither; + + virtual bool Item (cMobHeadEntity * a_MobHeadEntity) + { + m_IsWither = (a_MobHeadEntity->GetType() == SKULL_TYPE_WITHER); + + return false; + } + + public: + cCallback () : m_IsWither(false) {} + + bool IsWither(void) const { return m_IsWither; } + + void Reset(void) { m_IsWither = false; } + } CallbackA, CallbackB; + + a_World->DoWithMobHeadAt(a_BlockX, a_BlockY, a_BlockZ, CallbackA); + + if (!CallbackA.IsWither()) + { + return false; + } + + CallbackA.Reset(); + + BLOCKTYPE BlockY1 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); + BLOCKTYPE BlockY2 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ); + + if ((BlockY1 != E_BLOCK_SOULSAND) || (BlockY2 != E_BLOCK_SOULSAND)) + { + return false; + } + + a_World->DoWithMobHeadAt(a_BlockX - 1, a_BlockY, a_BlockZ, CallbackA); + a_World->DoWithMobHeadAt(a_BlockX + 1, a_BlockY, a_BlockZ, CallbackB); + + BLOCKTYPE Block1 = a_ChunkInterface.GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ); + BLOCKTYPE Block2 = a_ChunkInterface.GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ); + + if ((Block1 == E_BLOCK_SOULSAND) && (Block2 == E_BLOCK_SOULSAND) && CallbackA.IsWither() && CallbackB.IsWither()) + { + a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_ChunkInterface.FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_ChunkInterface.FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); + + // Block entities + a_World->SetBlock(a_BlockX + 1, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + a_World->SetBlock(a_BlockX - 1, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + + // Spawn the wither: + a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtWither); + + return true; + } + + CallbackA.Reset(); + CallbackB.Reset(); - // TODO 2014-03-24 xdot + a_World->DoWithMobHeadAt(a_BlockX, a_BlockY, a_BlockZ - 1, CallbackA); + a_World->DoWithMobHeadAt(a_BlockX, a_BlockY, a_BlockZ + 1, CallbackB); + + Block1 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1); + Block2 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1); + + if ((Block1 == E_BLOCK_SOULSAND) && (Block2 == E_BLOCK_SOULSAND) && CallbackA.IsWither() && CallbackB.IsWither()) + { + a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); + a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1, E_BLOCK_AIR, 0); + a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1, E_BLOCK_AIR, 0); + a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); + + // Block entities + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ + 1, E_BLOCK_AIR, 0); + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ - 1, E_BLOCK_AIR, 0); + + // Spawn the wither: + a_World->SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, cMonster::mtWither); + + return true; + } return false; } @@ -75,7 +160,24 @@ public: World->DoWithMobHeadAt(a_BlockX, a_BlockY, a_BlockZ, Callback); a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta); - TrySpawnWither(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); + if (a_BlockMeta == SKULL_TYPE_WITHER) + { + static const Vector3i Coords[] = + { + Vector3i( 0, 0, 0), + Vector3i( 1, 0, 0), + Vector3i(-1, 0, 0), + Vector3i( 0, 0, 1), + Vector3i( 0, 0, -1), + }; + for (size_t i = 0; i < ARRAYCOUNT(Coords); ++i) + { + if (TrySpawnWither(a_ChunkInterface, World, a_BlockX + Coords[i].x, a_BlockY, a_BlockZ + Coords[i].z)) + { + break; + } + } // for i - Coords[] + } } } ; diff --git a/src/Mobs/Wither.cpp b/src/Mobs/Wither.cpp index 0e42194ac..8f5d28b68 100644 --- a/src/Mobs/Wither.cpp +++ b/src/Mobs/Wither.cpp @@ -13,8 +13,27 @@ cWither::cWither(void) : m_InvulnerableTicks(220) { SetMaxHealth(300); +} + + + + + +bool cWither::IsArmored(void) const +{ + return GetHealth() <= (GetMaxHealth() / 2); +} + + + + +bool cWither::Initialize(cWorld * a_World) +{ + // Set health before BroadcastSpawnEntity() SetHealth(GetMaxHealth() / 3); + + return super::Initialize(a_World); } @@ -33,6 +52,11 @@ void cWither::DoTakeDamage(TakeDamageInfo & a_TDI) return; } + if (IsArmored() && (a_TDI.DamageType == dtRangedAttack)) + { + return; + } + super::DoTakeDamage(a_TDI); } @@ -60,6 +84,8 @@ void cWither::Tick(float a_Dt, cChunk & a_Chunk) Heal(10); } } + + m_World->BroadcastEntityMetadata(*this); } diff --git a/src/Mobs/Wither.h b/src/Mobs/Wither.h index d09e3607a..bc78bfaad 100644 --- a/src/Mobs/Wither.h +++ b/src/Mobs/Wither.h @@ -20,8 +20,12 @@ public: unsigned int GetNumInvulnerableTicks(void) const { return m_InvulnerableTicks; } void SetNumInvulnerableTicks(unsigned int a_Ticks) { m_InvulnerableTicks = a_Ticks; } + + /** Returns whether the wither is invulnerable to arrows. */ + bool IsArmored(void) const; // cEntity overrides + virtual bool Initialize(cWorld * a_World) override; virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp index ea844c044..fe6280218 100644 --- a/src/Protocol/Protocol125.cpp +++ b/src/Protocol/Protocol125.cpp @@ -1951,6 +1951,14 @@ void cProtocol125::WriteMobMetadata(const cMonster & a_Mob) WriteByte(((const cWitch &)a_Mob).IsAngry() ? 1 : 0); // Aggravated? Doesn't seem to do anything break; } + case cMonster::mtWither: + { + WriteByte(0x54); // Int at index 20 + WriteInt(((const cWither &)a_Mob).GetNumInvulnerableTicks()); + WriteByte(0x66); // Float at index 6 + WriteFloat((float)(a_Mob.GetHealth())); + break; + } case cMonster::mtSlime: case cMonster::mtMagmaCube: { diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 721ed349e..c678fc9a0 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -2535,6 +2535,7 @@ void cProtocol172::cPacketizer::WriteEntityMetadata(const cEntity & a_Entity) WriteByte(Frame.GetRotation()); break; } + default: break; } } @@ -2659,6 +2660,15 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob) WriteByte(((const cWitch &)a_Mob).IsAngry() ? 1 : 0); break; } + + case cMonster::mtWither: + { + WriteByte(0x54); // Int at index 20 + WriteInt(((const cWither &)a_Mob).GetNumInvulnerableTicks()); + WriteByte(0x66); // Float at index 6 + WriteFloat((float)(a_Mob.GetHealth())); + break; + } case cMonster::mtSlime: { diff --git a/src/StringCompression.cpp b/src/StringCompression.cpp index 5b9a3bb0a..2a85649a1 100644 --- a/src/StringCompression.cpp +++ b/src/StringCompression.cpp @@ -53,7 +53,7 @@ int UncompressString(const char * a_Data, int a_Length, AString & a_Uncompressed -int CompressStringGZIP(const char * a_Data, int a_Length, AString & a_Compressed) +int CompressStringGZIP(const char * a_Data, size_t a_Length, AString & a_Compressed) { // Compress a_Data into a_Compressed using GZIP; return Z_XXX error constants same as zlib's compress2() @@ -83,6 +83,7 @@ int CompressStringGZIP(const char * a_Data, int a_Length, AString & a_Compressed { // Some data has been compressed. Consume the buffer and continue compressing a_Compressed.append(Buffer, sizeof(Buffer) - strm.avail_out); + strm.next_out = (Bytef *)Buffer; strm.avail_out = sizeof(Buffer); if (strm.avail_in == 0) { @@ -116,7 +117,7 @@ int CompressStringGZIP(const char * a_Data, int a_Length, AString & a_Compressed -extern int UncompressStringGZIP(const char * a_Data, int a_Length, AString & a_Uncompressed) +extern int UncompressStringGZIP(const char * a_Data, size_t a_Length, AString & a_Uncompressed) { // Uncompresses a_Data into a_Uncompressed using GZIP; returns Z_OK for success or Z_XXX error constants same as zlib @@ -139,13 +140,14 @@ extern int UncompressStringGZIP(const char * a_Data, int a_Length, AString & a_U for (;;) { - res = inflate(&strm, Z_FINISH); + res = inflate(&strm, Z_NO_FLUSH); switch (res) { case Z_OK: { // Some data has been uncompressed. Consume the buffer and continue uncompressing a_Uncompressed.append(Buffer, sizeof(Buffer) - strm.avail_out); + strm.next_out = (Bytef *)Buffer; strm.avail_out = sizeof(Buffer); if (strm.avail_in == 0) { diff --git a/src/StringCompression.h b/src/StringCompression.h index 3f4e12d2d..c3a9eca91 100644 --- a/src/StringCompression.h +++ b/src/StringCompression.h @@ -16,10 +16,10 @@ extern int CompressString(const char * a_Data, int a_Length, AString & a_Compres extern int UncompressString(const char * a_Data, int a_Length, AString & a_Uncompressed, int a_UncompressedSize); /// Compresses a_Data into a_Compressed using GZIP; returns Z_OK for success or Z_XXX error constants same as zlib -extern int CompressStringGZIP(const char * a_Data, int a_Length, AString & a_Compressed); +extern int CompressStringGZIP(const char * a_Data, size_t a_Length, AString & a_Compressed); /// Uncompresses a_Data into a_Uncompressed using GZIP; returns Z_OK for success or Z_XXX error constants same as zlib -extern int UncompressStringGZIP(const char * a_Data, int a_Length, AString & a_Uncompressed); +extern int UncompressStringGZIP(const char * a_Data, size_t a_Length, AString & a_Uncompressed); diff --git a/src/StringUtils.h b/src/StringUtils.h index 4feff7553..da395e5b5 100644 --- a/src/StringUtils.h +++ b/src/StringUtils.h @@ -79,10 +79,10 @@ extern AString URLDecode(const AString & a_String); // Cannot export to Lua aut extern AString ReplaceAllCharOccurrences(const AString & a_String, char a_From, char a_To); // Needn't export to Lua, since Lua doesn't have chars anyway /// Decodes a Base64-encoded string into the raw data -extern AString Base64Decode(const AString & a_Base64String); +extern AString Base64Decode(const AString & a_Base64String); // Exported manually due to embedded NULs and extra parameter /// Encodes a string into Base64 -extern AString Base64Encode(const AString & a_Input); +extern AString Base64Encode(const AString & a_Input); // Exported manually due to embedded NULs and extra parameter /// Reads two bytes from the specified memory location and interprets them as BigEndian short extern short GetBEShort(const char * a_Mem); diff --git a/src/WorldStorage/SchematicFileSerializer.cpp b/src/WorldStorage/SchematicFileSerializer.cpp index d8531d965..9d594a084 100644 --- a/src/WorldStorage/SchematicFileSerializer.cpp +++ b/src/WorldStorage/SchematicFileSerializer.cpp @@ -14,6 +14,39 @@ +#ifdef SELF_TEST + +static class cSchematicStringSelfTest +{ +public: + cSchematicStringSelfTest(void) + { + cBlockArea ba; + ba.Create(21, 256, 21); + ba.RelLine(0, 0, 0, 9, 8, 7, cBlockArea::baTypes | cBlockArea::baMetas, E_BLOCK_WOODEN_STAIRS, 1); + AString Schematic; + if (!cSchematicFileSerializer::SaveToSchematicString(ba, Schematic)) + { + assert_test(!"Schematic failed to save!"); + } + cBlockArea ba2; + if (!cSchematicFileSerializer::LoadFromSchematicString(ba2, Schematic)) + { + assert_test(!"Schematic failed to load!"); + } + } +} g_SelfTest; + +#endif + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSchematicFileSerializer: + bool cSchematicFileSerializer::LoadFromSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName) { // Un-GZip the contents: |