summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MCServer/Plugins/APIDump/APIDesc.lua90
-rw-r--r--MCServer/Plugins/APIDump/Hooks/OnServerPing.lua4
-rw-r--r--Tools/QtBiomeVisualiser/BiomeView.cpp97
-rw-r--r--Tools/QtBiomeVisualiser/BiomeView.h26
-rw-r--r--Tools/QtBiomeVisualiser/ChunkSource.cpp143
-rw-r--r--Tools/QtBiomeVisualiser/MainWindow.cpp132
-rw-r--r--Tools/QtBiomeVisualiser/MainWindow.h31
-rw-r--r--Tools/QtBiomeVisualiser/QtChunk.cpp159
-rw-r--r--Tools/QtBiomeVisualiser/QtChunk.h15
-rw-r--r--src/Bindings/ManualBindings.cpp14
-rw-r--r--src/BlockEntities/BeaconEntity.cpp62
-rw-r--r--src/BlockEntities/BeaconEntity.h2
-rw-r--r--src/BlockEntities/BlockEntity.h2
-rw-r--r--src/BlockEntities/ChestEntity.cpp42
-rw-r--r--src/BlockEntities/ChestEntity.h3
-rw-r--r--src/BlockEntities/CommandBlockEntity.cpp32
-rw-r--r--src/BlockEntities/CommandBlockEntity.h3
-rw-r--r--src/BlockEntities/DropSpenserEntity.cpp48
-rw-r--r--src/BlockEntities/DropSpenserEntity.h3
-rw-r--r--src/BlockEntities/EnderChestEntity.h1
-rw-r--r--src/BlockEntities/FlowerPotEntity.cpp31
-rw-r--r--src/BlockEntities/FlowerPotEntity.h3
-rw-r--r--src/BlockEntities/FurnaceEntity.cpp54
-rw-r--r--src/BlockEntities/FurnaceEntity.h3
-rw-r--r--src/BlockEntities/HopperEntity.cpp11
-rw-r--r--src/BlockEntities/HopperEntity.h1
-rw-r--r--src/BlockEntities/JukeboxEntity.cpp28
-rw-r--r--src/BlockEntities/JukeboxEntity.h3
-rw-r--r--src/BlockEntities/MobHeadEntity.cpp32
-rw-r--r--src/BlockEntities/MobHeadEntity.h3
-rw-r--r--src/BlockEntities/NoteEntity.cpp29
-rw-r--r--src/BlockEntities/NoteEntity.h3
-rw-r--r--src/BlockEntities/SignEntity.cpp35
-rw-r--r--src/BlockEntities/SignEntity.h3
-rw-r--r--src/BlockID.h72
-rw-r--r--src/BlockInfo.cpp12
-rw-r--r--src/Blocks/BlockDirt.h10
-rw-r--r--src/Blocks/BlockDoor.h18
-rw-r--r--src/Blocks/BlockPiston.h4
-rw-r--r--src/Blocks/BlockTrapdoor.h6
-rw-r--r--src/ClientHandle.h14
-rw-r--r--src/Entities/Player.h8
-rw-r--r--src/Items/ItemHandler.cpp5
-rw-r--r--src/Items/ItemHoe.h27
-rw-r--r--src/Items/ItemPickaxe.h39
-rw-r--r--src/Root.h15
-rw-r--r--src/Simulator/SandSimulator.cpp1
-rw-r--r--src/World.cpp4
-rw-r--r--src/WorldStorage/CMakeLists.txt2
-rw-r--r--src/WorldStorage/WSSCompact.cpp1066
-rw-r--r--src/WorldStorage/WSSCompact.h157
-rw-r--r--src/WorldStorage/WorldStorage.cpp2
52 files changed, 650 insertions, 1960 deletions
diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua
index 9bc6299d9..bf5003035 100644
--- a/MCServer/Plugins/APIDump/APIDesc.lua
+++ b/MCServer/Plugins/APIDump/APIDesc.lua
@@ -524,6 +524,7 @@ end
Functions =
{
GenerateOfflineUUID = { Params = "Username", Return = "string", Notes = "(STATIC) Generates an UUID based on the player name provided. This is used for the offline (non-auth) mode, when there's no UUID source. Each username generates a unique and constant UUID, so that when the player reconnects with the same name, their UUID is the same. Returns a 32-char UUID (no dashes)." },
+ GetClientBrand = { Params = "", Return = "string", Notes = "Returns the brand that the client has sent in their MC|Brand plugin message." },
GetIPString = { Params = "", Return = "string", Notes = "Returns the IP address of the connection, as a string. Only the address part is returned, without the port number." },
GetLocale = { Params = "", Return = "Locale", Notes = "Returns the locale string that the client sends as part of the protocol handshake. Can be used to provide localized strings." },
GetPing = { Params = "", Return = "number", Notes = "Returns the ping time, in ms" },
@@ -536,10 +537,14 @@ end
IsUUIDOnline = { Params = "UUID", Return = "bool", Notes = "(STATIC) Returns true if the UUID is generated by online auth, false if it is an offline-generated UUID. We use Version-3 UUIDs for offline UUIDs, online UUIDs are Version-4, thus we can tell them apart. Accepts both 32-char and 36-char UUIDs (with and without dashes). If the string given is not a valid UUID, returns false."},
Kick = { Params = "Reason", Return = "", Notes = "Kicks the user with the specified reason" },
SendPluginMessage = { Params = "Channel, Message", Return = "", Notes = "Sends the plugin message on the specified channel." },
+ SetClientBrand = { Params = "ClientBrand", Return = "", Notes = "Sets the value of the client's brand. Normally this value is received from the client by a MC|Brand plugin message, this function lets plugins overwrite the value." },
SetLocale = { Params = "Locale", Return = "", Notes = "Sets the locale that MCServer keeps on record. Initially the locale is initialized in protocol handshake, this function allows plugins to override the stored value (but only server-side and only until the user disconnects)." },
SetUsername = { Params = "Name", Return = "", Notes = "Sets the username" },
SetViewDistance = { Params = "ViewDistance", Return = "", Notes = "Sets the viewdistance (number of chunks loaded for the player in each direction)" },
SendBlockChange = { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta", Return = "", Notes = "Sends a BlockChange packet to the client. This can be used to create fake blocks only for that player." },
+ SendEntityAnimation = { Params = "{{cEntity|Entity}}, AnimationNumber", Return = "", Notes = "Sends the specified animation of the specified entity to the client. The AnimationNumber is protocol-specific." },
+ SendSoundEffect = { Params = "SoundName, X, Y, Z, Volume, Pitch", Return = "", Notes = "Sends a sound effect request to the client. The sound is played at the specified coords, with the specified volume (a float, 1.0 is full volume, can be more) and pitch (0-255, 63 is 100%)" },
+ SendTimeUpdate = { Params = "WorldAge, TimeOfDay, DoDaylightCycle", Return = "", Notes = "Sends the specified time update to the client. WorldAge is the total age of the world, in ticks. TimeOfDay is the current day's time, in ticks (0 - 24000). DoDaylightCycle is a bool that specifies whether the client should automatically move the sun (true) or keep it in the same place (false)." },
},
Constants =
{
@@ -555,9 +560,9 @@ end
and commands suggested on click. The chat message can be sent by the regular chat-sending functions,
{{cPlayer}}:SendMessage(), {{cWorld}}:BroadcastChat() and {{cRoot}}:BroadcastChat().</p>
<p>
- Note that most of the functions in this class are so-called modifiers - they modify the object and
- then return the object itself, so that they can be chained one after another. See the Chaining
- example below for details.</p>
+ Note that most of the functions in this class are so-called chaining modifiers - they modify the
+ object and then return the object itself, so that they can be chained one after another. See the
+ Chaining example below for details.</p>
<p>
Each part of the composite chat message takes a "Style" parameter, this is a string that describes
the formatting. It uses the following strings, concatenated together:
@@ -581,14 +586,17 @@ end
{ Params = "Text", Return = "", Notes = "Creates a chat message containing the specified text, parsed by the ParseText() function. This allows easy migration from old chat messages." },
},
AddRunCommandPart = { Params = "Text, Command, [Style]", Return = "self", Notes = "Adds a text which, when clicked, runs the specified command. Chaining." },
+ AddShowAchievementPart = { Params = "PlayerName, AchievementName, [Style]", Return = "", Notes = "Adds a text that represents the 'Achievement get' message." },
AddSuggestCommandPart = { Params = "Text, Command, [Style]", Return = "self", Notes = "Adds a text which, when clicked, puts the specified command into the player's chat input area. Chaining." },
AddTextPart = { Params = "Text, [Style]", Return = "self", Notes = "Adds a regular text. Chaining." },
AddUrlPart = { Params = "Text, Url, [Style]", Return = "self", Notes = "Adds a text which, when clicked, opens up a browser at the specified URL. Chaining." },
Clear = { Params = "", Return = "", Notes = "Removes all parts from this object" },
+ CreateJsonString = { Params = "[AddPrefixes]", Return = "string", Notes = "Returns the entire object serialized into JSON, as it would be sent to a client. AddPrefixes specifies whether the chat prefixes should be prepended to the message, true by default." },
ExtractText = { Params = "", Return = "string", Notes = "Returns the text from the parts that comprises the human-readable data. Used for older protocols that don't support composite chat and for console-logging." },
+ GetAdditionalMessageTypeData = { Params = "", Return = "string", Notes = "Returns the AdditionalData associated with the message, such as the sender's name for mtPrivateMessage" },
GetMessageType = { Params = "", Return = "MessageType", Notes = "Returns the MessageType (mtXXX constant) that is associated with this message. When sent to a player, the message will be formatted according to this message type and the player's settings (adding \"[INFO]\" prefix etc.)" },
ParseText = { Params = "Text", Return = "self", Notes = "Adds text, while recognizing http and https URLs and old-style formatting codes (\"@2\"). Chaining." },
- SetMessageType = { Params = "MessageType", Return = "self", Notes = "Sets the MessageType (mtXXX constant) that is associated with this message. When sent to a player, the message will be formatted according to this message type and the player's settings (adding \"[INFO]\" prefix etc.) Chaining." },
+ SetMessageType = { Params = "MessageType, AdditionalData", Return = "self", Notes = "Sets the MessageType (mtXXX constant) that is associated with this message. Also sets the additional data (string) associated with the message, which is specific for the message type - such as the sender's name for mtPrivateMessage. When sent to a player, the message will be formatted according to this message type and the player's settings (adding \"[INFO]\" prefix etc.). Chaining." },
UnderlineUrls = { Params = "", Return = "self", Notes = "Makes all URL parts contained in the message underlined. Doesn't affect parts added in the future. Chaining." },
},
@@ -777,7 +785,9 @@ end</pre>
AddSpeedX = { Params = "AddX", Return = "", Notes = "Adds the specified amount of speed in the X axis direction." },
AddSpeedY = { Params = "AddY", Return = "", Notes = "Adds the specified amount of speed in the Y axis direction." },
AddSpeedZ = { Params = "AddZ", Return = "", Notes = "Adds the specified amount of speed in the Z axis direction." },
+ ArmorCoversAgainst = { Params = "{{cEntity|AttackerEntity}}, DamageType, RawDamage", Return = "number", Notes = "Returns the points out of a_RawDamage that the currently equipped armor would cover." },
Destroy = { Params = "", Return = "", Notes = "Schedules the entity to be destroyed" },
+ GetAirLevel = { Params = "", Return = "number", Notes = "Returns the air level (number of ticks of air left). Note, this function is only updated with mobs or players." },
GetArmorCoverAgainst = { Params = "AttackerEntity, DamageType, RawDamage", Return = "number", Notes = "Returns the number of hitpoints out of RawDamage that the currently equipped armor would cover. See {{TakeDamageInfo}} for more information on attack damage." },
GetChunkX = { Params = "", Return = "number", Notes = "Returns the X-coord of the chunk in which the entity is placed" },
GetChunkZ = { Params = "", Return = "number", Notes = "Returns the Z-coord of the chunk in which the entity is placed" },
@@ -793,6 +803,7 @@ end</pre>
GetHeadYaw = { Params = "", Return = "number", Notes = "Returns the pitch of the entity's head (FIXME: Rename to GetHeadPitch() )." },
GetHealth = { Params = "", Return = "number", Notes = "Returns the current health of the entity." },
GetHeight = { Params = "", Return = "number", Notes = "Returns the height (Y size) of the entity" },
+ GetInvulnerableTicks = { Params = "", Return = "number", Notes = "Returns the number of ticks that this entity will be invulnerable for. This is used for after-hit recovery - the entities are invulnerable for half a second after being hit." },
GetKnockbackAmountAgainst = { Params = "ReceiverEntity", Return = "number", Notes = "Returns the amount of knockback that the currently equipped items would cause when attacking the ReceiverEntity." },
GetLookVector = { Params = "", Return = "{{Vector3f}}", Notes = "Returns the vector that defines the direction in which the entity is looking" },
GetMass = { Params = "", Return = "number", Notes = "Returns the mass of the entity. Currently unused." },
@@ -810,23 +821,29 @@ end</pre>
GetSpeedX = { Params = "", Return = "number", Notes = "Returns the X-part of the speed vector" },
GetSpeedY = { Params = "", Return = "number", Notes = "Returns the Y-part of the speed vector" },
GetSpeedZ = { Params = "", Return = "number", Notes = "Returns the Z-part of the speed vector" },
+ GetTicksAlive = { Params = "", Return = "number", Notes = "Returns the number of ticks that this entity has been alive for." },
GetUniqueID = { Params = "", Return = "number", Notes = "Returns the ID that uniquely identifies the entity within the running server. Note that this ID is not persisted to the data files." },
GetWidth = { Params = "", Return = "number", Notes = "Returns the width (X and Z size) of the entity." },
GetWorld = { Params = "", Return = "{{cWorld}}", Notes = "Returns the world where the entity resides" },
GetYaw = { Params = "", Return = "number", Notes = "Returns the yaw (direction) of the entity. Measured in degrees, values range from -180 to +180. 0 means ZP, 90 means XM, -180 means ZM, -90 means XP." },
+ HandleSpeedFromAttachee = { Params = "ForwardAmount, SidewaysAmount", Return = "", Notes = "Updates the entity's speed based on the attachee exerting the specified force forward and sideways. Used for entities being driven by other entities attached to them - usually players driving minecarts and boats." },
Heal = { Params = "Hitpoints", Return = "", Notes = "Heals the specified number of hitpoints. Hitpoints is expected to be a positive number." },
IsA = { Params = "ClassName", Return = "bool", Notes = "Returns true if the entity class is a descendant of the specified class name, or the specified class itself" },
IsBoat = { Params = "", Return = "bool", Notes = "Returns true if the entity is a {{cBoat|boat}}." },
IsCrouched = { Params = "", Return = "bool", Notes = "Returns true if the entity is crouched. Always false for entities that don't support crouching." },
IsDestroyed = { Params = "", Return = "bool", Notes = "Returns true if the entity has been destroyed and is awaiting removal from the internal structures." },
+ IsEnderCrystal = { Params = "", Return = "bool", Notes = "Returns true if the entity is an ender crystal." },
IsExpOrb = { Params = "", Return = "bool", Notes = "Returns true if the entity represents an experience orb" },
IsFallingBlock = { Params = "", Return = "bool", Notes = "Returns true if the entity represents a {{cFallingBlock}} entity." },
+ IsFireproof = { Params = "", Return = "bool", Notes = "Returns true if the entity takes no damage from being on fire." },
IsFloater = { Params = "", Return = "bool", Notes = "Returns true if the entity represents a fishing rod floater" },
IsInvisible = { Params = "", Return = "bool", Notes = "Returns true if the entity is invisible" },
+ IsItemFrame = { Params = "", Return = "bool", Notes = "Returns true if the entity is an item frame." },
IsMinecart = { Params = "", Return = "bool", Notes = "Returns true if the entity represents a {{cMinecart|minecart}}" },
IsMob = { Params = "", Return = "bool", Notes = "Returns true if the entity represents any {{cMonster|mob}}." },
IsOnFire = { Params = "", Return = "bool", Notes = "Returns true if the entity is on fire" },
IsPainting = { Params = "", Return = "bool", Notes = "Returns if this entity is a painting." },
+ IsPawn = { Params = "", Return = "bool", Notes = "Returns true if the entity is a {{cPawn}} descendant." },
IsPickup = { Params = "", Return = "bool", Notes = "Returns true if the entity represents a {{cPickup|pickup}}." },
IsPlayer = { Params = "", Return = "bool", Notes = "Returns true if the entity represents a {{cPlayer|player}}" },
IsProjectile = { Params = "", Return = "bool", Notes = "Returns true if the entity is a {{cProjectileEntity}} descendant." },
@@ -836,12 +853,19 @@ end</pre>
IsSubmerged = { Params = "", Return = "bool", Notes = "Returns true if the mob or player is submerged in water (head is in a water block). Note, this function is only updated with mobs or players." },
IsSwimming = { Params = "", Return = "bool", Notes = "Returns true if the mob or player is swimming in water (feet are in a water block). Note, this function is only updated with mobs or players." },
IsTNT = { Params = "", Return = "bool", Notes = "Returns true if the entity represents a {{cTNTEntity|TNT entity}}" },
+ Killed = { Params = "{{cEntity|Victim}}", Return = "", Notes = "This entity has killed another entity (the Victim). For players, adds the scoreboard statistics about the kill." },
KilledBy = { Notes = "FIXME: Remove this from API" },
- GetAirLevel = { Params = "", Return = "number", Notes = "Returns the air level (number of ticks of air left). Note, this function is only updated with mobs or players." },
+ MoveToWorld =
+ {
+ { Params = "{{cWorld|World}}, [ShouldSendRespawn]", Return = "bool", Notes = "Removes the entity from this world and starts moving it to the specified world. Note that to avoid deadlocks, the move is asynchronous - the entity is moved into a queue and will be moved from that queue into the destination world at some (unpredictable) time in the future. ShouldSendRespawn is used only for players, it specifies whether the player should be sent a Repawn packet upon leaving the world (The client handles respawns only between different dimensions)." },
+ { Params = "WorldName, [ShouldSendRespawn]", Return = "bool", Notes = "Removes the entity from this world and starts moving it to the specified world. Note that to avoid deadlocks, the move is asynchronous - the entity is moved into a queue and will be moved from that queue into the destination world at some (unpredictable) time in the future. ShouldSendRespawn is used only for players, it specifies whether the player should be sent a Repawn packet upon leaving the world (The client handles respawns only between different dimensions)." },
+ },
SetGravity = { Params = "Gravity", Return = "", Notes = "Sets the number that is used as the gravity for physics simulation. 1G (9.78) by default." },
SetHeadYaw = { Params = "HeadPitch", Return = "", Notes = "Sets the head pitch (FIXME: Rename to SetHeadPitch() )." },
SetHealth = { Params = "Hitpoints", Return = "", Notes = "Sets the entity's health to the specified amount of hitpoints. Doesn't broadcast any hurt animation. Doesn't kill the entity if health drops below zero. Use the TakeDamage() function instead for taking damage." },
SetHeight = { Params = "", Return = "", Notes = "FIXME: Remove this from API" },
+ SetInvulnerableTicks = { Params = "NumTicks", Return = "", Notes = "Sets the amount of ticks for which the entity will not receive any damage from other entities." },
+ SetIsFireproof = { Params = "IsFireproof", Return = "", Notes = "Sets whether the entity receives damage from being on fire." },
SetMass = { Params = "Mass", Return = "", Notes = "Sets the mass of the entity. Currently unused." },
SetMaxHealth = { Params = "MaxHitpoints", Return = "", Notes = "Sets the maximum hitpoints of the entity. If current health is above MaxHitpoints, it is capped to MaxHitpoints." },
SetPitch = { Params = "number", Return = "", Notes = "Sets the pitch (nose-down rotation) of the entity" },
@@ -856,7 +880,7 @@ end</pre>
SetPosZ = { Params = "number", Return = "", Notes = "Sets the Z-coord of the entity's pivot" },
SetRoll = { Params = "number", Return = "", Notes = "Sets the roll (sideways rotation) of the entity. Currently unused." },
SetRot = { Params = "{{Vector3f|Rotation}}", Return = "", Notes = "Sets the entire rotation vector (Yaw, Pitch, Roll)" },
- SetYawFromSpeed = { Params = "", Return = "", Notes = "Sets the entity's yaw to match its current speed (entity looking forwards as it moves). (FIXME: Rename to SetYawFromSpeed)" },
+ SetYawFromSpeed = { Params = "", Return = "", Notes = "Sets the entity's yaw to match its current speed (entity looking forwards as it moves)." },
SetSpeed =
{
{ Params = "SpeedX, SpeedY, SpeedZ", Return = "", Notes = "Sets the current speed of the entity" },
@@ -882,18 +906,20 @@ end</pre>
Constants =
{
etBoat = { Notes = "The entity is a {{cBoat}}" },
+ etEnderCrystal = { Notes = "" },
etEntity = { Notes = "No further specialization available" },
etExpOrb = { Notes = "The entity is a {{cExpOrb}}" },
etFallingBlock = { Notes = "The entity is a {{cFallingBlock}}" },
etFloater = { Notes = "The entity is a fishing rod floater" },
+ etItemFrame = { Notes = "" },
+ etMinecart = { Notes = "The entity is a {{cMinecart}} descendant" },
etMob = { Notes = "The entity is a {{cMonster}} descendant" },
etMonster = { Notes = "The entity is a {{cMonster}} descendant" },
- etMinecart = { Notes = "The entity is a {{cMinecart}} descendant" },
- etPlayer = { Notes = "The entity is a {{cPlayer}}" },
+ etPainting = { Notes = "The entity is a {{cPainting}}" },
etPickup = { Notes = "The entity is a {{cPickup}}" },
+ etPlayer = { Notes = "The entity is a {{cPlayer}}" },
etProjectile = { Notes = "The entity is a {{cProjectileEntity}} descendant" },
etTNT = { Notes = "The entity is a {{cTNTEntity}}" },
- etPainting = { Notes = "The entity is a {{cPainting}}" },
},
ConstantGroups =
{
@@ -1043,6 +1069,7 @@ ValueName0=SomeOtherValue
GetValueSetB = { Params = "KeyName, ValueName, Default", Return = "bool", Notes = "Returns the value of the specified name under the specified key, as a bool. If the value doesn't exist, creates it with the specified default." },
GetValueSetF = { Params = "KeyName, ValueName, Default", Return = "number", Notes = "Returns the value of the specified name under the specified key, as a floating-point number. If the value doesn't exist, creates it with the specified default." },
GetValueSetI = { Params = "KeyName, ValueName, Default", Return = "number", Notes = "Returns the value of the specified name under the specified key, as an integer. If the value doesn't exist, creates it with the specified default." },
+ HasValue = { Params = "KeyName, ValueName", Return = "bool", Notes = "Returns true if the specified value is present." },
ReadFile = { Params = "FileName, [AllowExampleFallback]", Return = "bool", Notes = "Reads the values from the specified file. Previous in-memory contents are lost. If the file cannot be opened, and AllowExample is true, another file, \"filename.example.ini\", is loaded and then saved as \"filename.ini\". Returns true if successful, false if not." },
SetValue =
{
@@ -1740,6 +1767,7 @@ a_Player:OpenWindow(Window);
GetItem = { Params = "", Return = "{{cItem|cItem}}", Notes = "Returns the item represented by this pickup" },
IsCollected = { Params = "", Return = "bool", Notes = "Returns true if this pickup has already been collected (is waiting to be destroyed)" },
IsPlayerCreated = { Params = "", Return = "bool", Notes = "Returns true if the pickup was created by a player" },
+ SetAge = { Params = "AgeTicks", Return = "", Notes = "Sets the pickup's age, in ticks." },
},
Inherits = "cEntity",
}, -- cPickup
@@ -1764,8 +1792,8 @@ a_Player:OpenWindow(Window);
ForceSetSpeed = { Params = "{{Vector3d|Direction}}", Notes = "Forces the player to move to the given direction." },
GetClientHandle = { Params = "", Return = "{{cClientHandle}}", Notes = "Returns the client handle representing the player's connection. May be nil (AI players)." },
GetColor = { Return = "string", Notes = "Returns the full color code to be used for this player's messages (based on their rank). Prefix player messages with this code." },
- GetPlayerListName = { Return = "string", Notes = "Returns the name that is used in the playerlist." },
GetCurrentXp = { Params = "", Return = "number", Notes = "Returns the current amount of XP" },
+ GetCustomName = { Params = "", Return = "string", Notes = "Returns the custom name of this player. If the player hasn't a custom name, it will return an empty string." },
GetEffectiveGameMode = { Params = "", Return = "{{Globals#GameMode|GameMode}}", Notes = "(OBSOLETE) Returns the current resolved game mode of the player. If the player is set to inherit the world's gamemode, returns that instead. See also GetGameMode() and IsGameModeXXX() functions. Note that this function is the same as GetGameMode(), use that function instead." },
GetEquippedItem = { Params = "", Return = "{{cItem}}", Notes = "Returns the item that the player is currently holding; empty item if holding nothing." },
GetEyeHeight = { Return = "number", Notes = "Returns the height of the player's eyes, in absolute coords" },
@@ -1780,18 +1808,24 @@ a_Player:OpenWindow(Window);
GetGameMode = { Return = "{{Globals#GameMode|GameMode}}", Notes = "Returns the player's gamemode. The player may have their gamemode unassigned, in which case they inherit the gamemode from the current {{cWorld|world}}.<br /> <b>NOTE:</b> Instead of comparing the value returned by this function to the gmXXX constants, use the IsGameModeXXX() functions. These functions handle the gamemode inheritance automatically."},
GetIP = { Return = "string", Notes = "Returns the IP address of the player, if available. Returns an empty string if there's no IP to report."},
GetInventory = { Return = "{{cInventory|Inventory}}", Notes = "Returns the player's inventory"},
+ GetLastBedPos = { Params = "", Return = "{{Vector3i}}", Notes = "Returns the position of the last bed the player has slept in, or the world's spawn if no such position was recorded." },
GetMaxSpeed = { Params = "", Return = "number", Notes = "Returns the player's current maximum speed, relative to the game default speed. Takes into account the sprinting / flying status." },
GetName = { Return = "string", Notes = "Returns the player's name" },
GetNormalMaxSpeed = { Params = "", Return = "number", Notes = "Returns the player's maximum walking speed, relative to the game default speed. Defaults to 1, but plugins may modify it for faster or slower walking." },
+ GetPermissions = { Params = "", Return = "array-table of strings", Notes = "Returns the list of all permissions that the player has assigned to them through their rank." },
+ GetPlayerListName = { Return = "string", Notes = "Returns the name that is used in the playerlist." },
GetResolvedPermissions = { Return = "array-table of string", Notes = "Returns all the player's permissions, as a table. The permissions are stored in the array part of the table, beginning with index 1." },
GetSprintingMaxSpeed = { Params = "", Return = "number", Notes = "Returns the player's maximum sprinting speed, relative to the game default speed. Defaults to 1.3, but plugins may modify it for faster or slower sprinting." },
GetStance = { Return = "number", Notes = "Returns the player's stance (Y-pos of player's eyes)" },
+ GetTeam = { Params = "", Return = "{{cTeam}}", Notes = "Returns the team that the player belongs to, or nil if none." },
GetThrowSpeed = { Params = "SpeedCoeff", Return = "{{Vector3d}}", Notes = "Returns the speed vector for an object thrown with the specified speed coeff. Basically returns the normalized look vector multiplied by the coeff, with a slight random variation." },
GetThrowStartPos = { Params = "", Return = "{{Vector3d}}", Notes = "Returns the position where the projectiles should start when thrown by this player." },
+ GetUUID = { Params = "", Return = "string", Notes = "Returns the (short) UUID that the player is using. Could be empty string for players that don't have a Mojang account assigned to them (in the future, bots for example)." },
GetWindow = { Params = "", Return = "{{cWindow}}", Notes = "Returns the currently open UI window. If the player doesn't have any UI window open, returns the inventory window." },
GetXpLevel = { Params = "", Return = "number", Notes = "Returns the current XP level (based on current XP amount)." },
GetXpLifetimeTotal = { Params = "", Return = "number", Notes = "Returns the amount of XP that has been accumulated throughout the player's lifetime." },
GetXpPercentage = { Params = "", Return = "number", Notes = "Returns the percentage of the experience bar - the amount of XP towards the next XP level. Between 0 and 1." },
+ HasCustomName = { Params = "", Return = "bool", Notes = "Returns true if the player has a custom name." },
HasPermission = { Params = "PermissionString", Return = "bool", Notes = "Returns true if the player has the specified permission" },
Heal = { Params = "HitPoints", Return = "", Notes = "Heals the player by the specified amount of HPs. Only positive amounts are expected. Sends a health update to the client." },
IsEating = { Params = "", Return = "bool", Notes = "Returns true if the player is currently eating the item in their hand." },
@@ -1799,7 +1833,9 @@ a_Player:OpenWindow(Window);
IsFlying = { Return = "bool", Notes = "Returns true if the player is flying." },
IsGameModeAdventure = { Params = "", Return = "bool", Notes = "Returns true if the player is in the gmAdventure gamemode, or has their gamemode unset and the world is a gmAdventure world." },
IsGameModeCreative = { Params = "", Return = "bool", Notes = "Returns true if the player is in the gmCreative gamemode, or has their gamemode unset and the world is a gmCreative world." },
+ IsGameModeSpectator = { Params = "", Return = "bool", Notes = "Returns true if the player is in the gmSpectator gamemode, or has their gamemode unset and the world is a gmSpectator world." },
IsGameModeSurvival = { Params = "", Return = "bool", Notes = "Returns true if the player is in the gmSurvival gamemode, or has their gamemode unset and the world is a gmSurvival world." },
+ IsInBed = { Params = "", Return = "bool", Notes = "Returns true if the player is currently lying in a bed." },
IsOnGround = { Params = "", Return = "bool", Notes = "Returns true if the player is on ground (not falling, not jumping, not flying)" },
IsSatiated = { Params = "", Return = "bool", Notes = "Returns true if the player is satiated (cannot eat)." },
IsVisible = { Params = "", Return = "bool", Notes = "Returns true if the player is visible to other players" },
@@ -1807,6 +1843,7 @@ a_Player:OpenWindow(Window);
MoveTo = { Params = "{{Vector3d|NewPosition}}", Return = "Tries to move the player into the specified position." },
MoveToWorld = { Params = "WorldName", Return = "bool", Return = "Moves the player to the specified world. Returns true if successful." },
OpenWindow = { Params = "{{cWindow|Window}}", Return = "", Notes = "Opens the specified UI window for the player." },
+ PermissionMatches = { Params = "Permission, Template", Return = "bool", Notes = "(STATIC) Returns true if the specified permission matches the specified template. The template may contain wildcards." },
Respawn = { Params = "", Return = "", Notes = "Restores the health, extinguishes fire, makes visible and sends the Respawn packet." },
SendMessage = { Params = "Message", Return = "", Notes = "Sends the specified message to the player." },
SendMessageFailure = { Params = "Message", Return = "", Notes = "Prepends Rose [INFO] / colours entire text (depending on ShouldUseChatPrefixes()) and sends message to player. For a command that failed to run because of insufficient permissions, etc." },
@@ -1815,12 +1852,12 @@ a_Player:OpenWindow(Window);
SendMessagePrivateMsg = { Params = "Message, SenderName", Return = "", Notes = "Prepends Light Blue [MSG: *SenderName*] / prepends SenderName and colours entire text (depending on ShouldUseChatPrefixes()) and sends message to player. For private messaging." },
SendMessageSuccess = { Params = "Message", Return = "", Notes = "Prepends Green [INFO] / colours entire text (depending on ShouldUseChatPrefixes()) and sends message to player. Success notification." },
SendMessageWarning = { Params = "Message, Sender", Return = "", Notes = "Prepends Rose [WARN] / colours entire text (depending on ShouldUseChatPrefixes()) and sends message to player. Denotes that something concerning, such as plugin reload, is about to happen." },
- HasCustomName = { Params = "", Return = "bool", Notes = "Returns true if the player has a custom name." },
- GetCustomName = { Params = "", Return = "string", Notes = "Returns the custom name of this player. If the player hasn't a custom name, it will return an empty string." },
- SetCustomName = { Params = "string", Return = "", Notes = "Sets the custom name of this player. If you want to disable the custom name, simply set an empty string. The custom name will be used in the tab-list, in the player nametag and in the tab-completion." },
+ SendRotation = { Params = "YawDegrees, PitchDegrees", Return = "", Notes = "Sends the specified rotation to the player, forcing them to look that way" },
+ SetBedPos = { Params = "{{Vector3i|Position}}", Return = "", Notes = "Sets the internal representation of the last bed position the player has slept in. The player will respawn at this position if they die." },
SetCanFly = { Params = "CanFly", Notes = "Sets if the player can fly or not." },
SetCrouch = { Params = "IsCrouched", Return = "", Notes = "Sets the crouch state, broadcasts the change to other players." },
SetCurrentExperience = { Params = "XPAmount", Return = "", Notes = "Sets the current amount of experience (and indirectly, the XP level)." },
+ SetCustomName = { Params = "string", Return = "", Notes = "Sets the custom name of this player. If you want to disable the custom name, simply set an empty string. The custom name will be used in the tab-list, in the player nametag and in the tab-completion." },
SetFlying = { Params = "IsFlying", Notes = "Sets if the player is flying or not." },
SetFlyingMaxSpeed = { Params = "FlyingMaxSpeed", Return = "", Notes = "Sets the flying maximum speed, relative to the game default speed. The default value is 1. Sends the updated speed to the client." },
SetFoodExhaustionLevel = { Params = "ExhaustionLevel", Return = "", Notes = "Sets the food exhaustion to the specified level." },
@@ -1834,7 +1871,11 @@ a_Player:OpenWindow(Window);
SetNormalMaxSpeed = { Params = "NormalMaxSpeed", Return = "", Notes = "Sets the normal (walking) maximum speed, relative to the game default speed. The default value is 1. Sends the updated speed to the client, if appropriate." },
SetSprint = { Params = "IsSprinting", Return = "", Notes = "Sets whether the player is sprinting or not." },
SetSprintingMaxSpeed = { Params = "SprintingMaxSpeed", Return = "", Notes = "Sets the sprinting maximum speed, relative to the game default speed. The default value is 1.3. Sends the updated speed to the client, if appropriate." },
+ SetTeam = { Params = "{{cTeam|Team}}", Return = "", Notes = "Moves the player to the specified team." },
SetVisible = { Params = "IsVisible", Return = "", Notes = "Sets the player visibility to other players" },
+ TossEquippedItem = { Params = "[Amount]", Return = "", Notes = "Tosses the item that the player has selected in their hotbar. Amount defaults to 1." },
+ TossHeldItem = { Params = "[Amount]", Return = "", Notes = "Tosses the item held by the cursor, then the player is in a UI window. Amount defaults to 1." },
+ TossPickup = { Params = "{{cItem|Item}}", Return = "", Notes = "Tosses a pickup newly created from the specified item." },
XpForLevel = { Params = "XPLevel", Return = "number", Notes = "(STATIC) Returns the total amount of XP needed for the specified XP level. Inverse of CalcLevelFromXp()." },
},
Constants =
@@ -2037,7 +2078,7 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
ClearPlayerRanks = { Params = "", Return = "", Notes = "Removes all player ranks from the database. Note that this doesn't change the cPlayer instances for the already connected players, you need to update all the instances manually." },
GetAllGroups = { Params = "", Return = "array-table of groups' names", Notes = "Returns an array-table containing the names of all the groups that are known to the manager." },
GetAllPermissions = { Params = "", Return = "array-table of permissions", Notes = "Returns an array-table containing all the permissions that are known to the manager." },
- GetAllPlayers = { Params = "", Return = "array-table of uuids", Notes = "Returns the short uuids of all defined players, sorted by the players' names (case insensitive)." },
+ GetAllPlayerUUIDs = { Params = "", Return = "array-table of uuids", Notes = "Returns the short uuids of all players stored in the rank DB, sorted by the players' names (case insensitive)." },
GetAllRanks = { Params = "", Return = "array-table of ranks' names", Notes = "Returns an array-table containing the names of all the ranks that are known to the manager." },
GetDefaultRank = { Params = "", Return = "string", Notes = "Returns the name of the default rank. " },
GetGroupPermissions = { Params = "GroupName", Return = "array-table of permissions", Notes = "Returns an array-table containing the permissions that the specified group contains." },
@@ -2080,13 +2121,20 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
]],
Functions =
{
- BroadcastChat = { Params = "Message", Return = "", Notes = "Broadcasts a message to every player in the server. No formatting is done by the server." },
- BroadcastChatFailure = { Params = "Message", Return = "", Notes = "Prepends Rose [INFO] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For a command that failed to run because of insufficient permissions, etc." },
- BroadcastChatFatal = { Params = "Message", Return = "", Notes = "Prepends Red [FATAL] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For a plugin that crashed, or similar." },
- BroadcastChatInfo = { Params = "Message", Return = "", Notes = "Prepends Yellow [INFO] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For informational messages, such as command usage." },
- BroadcastChatSuccess = { Params = "Message", Return = "", Notes = "Prepends Green [INFO] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For success messages." },
- BroadcastChatWarning = { Params = "Message", Return = "", Notes = "Prepends Rose [WARN] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For concerning events, such as plugin reload etc." },
- CreateAndInitializeWorld = { Params = "WorldName", Return = "{{cWorld|cWorld}}", Notes = "Creates a new world and initializes it. If there is a world whith the same name it returns nil." },
+ BroadcastChat =
+ {
+ { Params = "MessageText, MessageType", Return = "", Notes = "Broadcasts a message to all players, with its message type set to MessageType (default: mtCustom)." },
+ { Params = "{{cCompositeChat|CompositeChat}}", Return = "", Notes = "Broadcasts a {{cCompositeChat|composite chat message} to all players." },
+ },
+ BroadcastChatDeath = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtDeath. Use for when a player has died." },
+ BroadcastChatFailure = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtFailure. Use for a command that failed to run because of insufficient permissions, etc." },
+ BroadcastChatFatal = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtFatal. Use for a plugin that crashed, or similar." },
+ BroadcastChatInfo = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtInfo. Use for informational messages, such as command usage." },
+ BroadcastChatJoin = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtJoin. Use for players joining the server." },
+ BroadcastChatLeave = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtLeave. Use for players leaving the server." },
+ BroadcastChatSuccess = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtSuccess. Use for success messages." },
+ BroadcastChatWarning = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtWarning. Use for concerning events, such as plugin reload etc." },
+ CreateAndInitializeWorld = { Params = "WorldName", Return = "{{cWorld|cWorld}}", Notes = "Creates a new world and initializes it. If there is a world whith the same name it returns nil.<br/><br/><b>NOTE</b>This function is currently unsafe, do not use!" },
FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "", Notes = "Calls the given callback function for all players with names partially (or fully) matching the name string provided." },
ForEachPlayer = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each player. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|cPlayer}})</pre>" },
ForEachWorld = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each world. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cWorld|cWorld}})</pre>" },
diff --git a/MCServer/Plugins/APIDump/Hooks/OnServerPing.lua b/MCServer/Plugins/APIDump/Hooks/OnServerPing.lua
index 6d2325fe6..76b6d1517 100644
--- a/MCServer/Plugins/APIDump/Hooks/OnServerPing.lua
+++ b/MCServer/Plugins/APIDump/Hooks/OnServerPing.lua
@@ -34,9 +34,9 @@ function OnServerPing(ClientHandle, ServerDescription, OnlinePlayers, MaxPlayers
MaxPlayers = 0
-- Change favicon
- if (cFile:IsFile("my-favicon.png")) then
+ if cFile:IsFile("my-favicon.png") then
local FaviconData = cFile:ReadWholeFile("my-favicon.png")
- if (FaviconData != "") then
+ if (FaviconData ~= "") and (FaviconData ~= nil) then
Favicon = Base64Encode(FaviconData)
end
end
diff --git a/Tools/QtBiomeVisualiser/BiomeView.cpp b/Tools/QtBiomeVisualiser/BiomeView.cpp
index ce5a870cd..b44b935d7 100644
--- a/Tools/QtBiomeVisualiser/BiomeView.cpp
+++ b/Tools/QtBiomeVisualiser/BiomeView.cpp
@@ -42,8 +42,9 @@ BiomeView::BiomeView(QWidget * parent) :
// Add a chunk-update callback mechanism:
connect(&m_Cache, SIGNAL(chunkAvailable(int, int)), this, SLOT(chunkAvailable(int, int)));
- // Allow keyboard interaction:
+ // Allow mouse and keyboard interaction:
setFocusPolicy(Qt::StrongFocus);
+ setMouseTracking(true);
}
@@ -80,6 +81,27 @@ void BiomeView::setChunkSource(std::shared_ptr<ChunkSource> a_ChunkSource)
+void BiomeView::setPosition(int a_BlockX, int a_BlockZ)
+{
+ m_X = a_BlockX;
+ m_Z = a_BlockZ;
+ redraw();
+}
+
+
+
+
+
+void BiomeView::setZoomLevel(double a_ZoomLevel)
+{
+ m_Zoom = a_ZoomLevel;
+ redraw();
+}
+
+
+
+
+
void BiomeView::redraw()
{
if (!hasData())
@@ -275,6 +297,12 @@ void BiomeView::mousePressEvent(QMouseEvent * a_Event)
void BiomeView::mouseMoveEvent(QMouseEvent * a_Event)
{
+ // If there's no data displayed, bail out:
+ if (!hasData())
+ {
+ return;
+ }
+
if (m_IsMouseDragging)
{
// The user is dragging the mouse, move the view around:
@@ -286,7 +314,15 @@ void BiomeView::mouseMoveEvent(QMouseEvent * a_Event)
return;
}
- // TODO: Update the status bar info for the biome currently pointed at
+ // Update the status bar info text:
+ int blockX = floor((a_Event->x() - width() / 2) / m_Zoom + m_X);
+ int blockZ = floor((a_Event->y() - height() / 2) / m_Zoom + m_Z);
+ int chunkX, chunkZ;
+ int relX = blockX, relY, relZ = blockZ;
+ cChunkDef::AbsoluteToRelative(relX, relY, relZ, chunkX, chunkZ);
+ auto chunk = m_Cache.fetch(chunkX, chunkZ);
+ int biome = (chunk.get() != nullptr) ? chunk->getBiome(relX, relZ) : biInvalidBiome;
+ emit hoverChanged(blockX, blockZ, biome);
}
@@ -307,12 +343,12 @@ void BiomeView::wheelEvent(QWheelEvent * a_Event)
m_MouseWheelDelta += a_Event->delta();
while (m_MouseWheelDelta >= DELTA_STEP)
{
- increaseZoom();
+ emit wheelUp();
m_MouseWheelDelta -= DELTA_STEP;
}
while (m_MouseWheelDelta <= -DELTA_STEP)
{
- decreaseZoom();
+ emit wheelDown();
m_MouseWheelDelta += DELTA_STEP;
}
}
@@ -360,14 +396,14 @@ void BiomeView::keyPressEvent(QKeyEvent * a_Event)
case Qt::Key_PageUp:
case Qt::Key_Q:
{
- increaseZoom();
+ emit increaseZoom();
break;
}
case Qt::Key_PageDown:
case Qt::Key_E:
{
- decreaseZoom();
+ emit decreaseZoom();
break;
}
}
@@ -376,52 +412,3 @@ void BiomeView::keyPressEvent(QKeyEvent * a_Event)
-
-void BiomeView::decreaseZoom()
-{
- if (m_Zoom > 1.001)
- {
- m_Zoom--;
- if (m_Zoom < 1.0)
- {
- // Just crossed the 100%, fixate the 100% threshold:
- m_Zoom = 1.0;
- }
- }
- else if (m_Zoom > 0.01)
- {
- m_Zoom = m_Zoom / 2;
- }
- redraw();
-}
-
-
-
-
-
-void BiomeView::increaseZoom()
-{
- if (m_Zoom > 0.99)
- {
- if (m_Zoom > 20.0)
- {
- // Zoom too large
- return;
- }
- m_Zoom++;
- }
- else
- {
- m_Zoom = m_Zoom * 2;
- if (m_Zoom > 1.0)
- {
- // Just crossed the 100%, fixate the 100% threshold:
- m_Zoom = 1.0;
- }
- }
- redraw();
-}
-
-
-
-
diff --git a/Tools/QtBiomeVisualiser/BiomeView.h b/Tools/QtBiomeVisualiser/BiomeView.h
index f0521571d..40d8b96ae 100644
--- a/Tools/QtBiomeVisualiser/BiomeView.h
+++ b/Tools/QtBiomeVisualiser/BiomeView.h
@@ -25,7 +25,27 @@ public:
The entire view is then invalidated and regenerated. */
void setChunkSource(std::shared_ptr<ChunkSource> a_ChunkSource);
+ /** Sets the position of the central pixel of the map to the specified point and redraws the view. */
+ void setPosition(int a_BlockX, int a_BlockZ);
+
+ /** Sets the zoom level to the specified value and redraws the view. */
+ void setZoomLevel(double a_ZoomLevel);
+
signals:
+ /** Signalled when the user uses the wheel to scroll upwards. */
+ void wheelUp();
+
+ /** Signalled when the user uses the wheel to scroll downwards. */
+ void wheelDown();
+
+ /** Signalled when the user presses a key to increase zoom. */
+ void increaseZoom();
+
+ /** Signalled when the user presses a key to decrease zoom. */
+ void decreaseZoom();
+
+ /** Emitted when the user moves the mouse, to reflect the current block under the cursor. */
+ void hoverChanged(int a_BlockX, int a_BlockZ, int a_Biome);
public slots:
/** Redraw the entire widget area. */
@@ -86,12 +106,6 @@ protected:
/** Called when the user presses a key. */
virtual void keyPressEvent(QKeyEvent * a_Event) override;
-
- /** Decreases the zoom level and queues a redraw. */
- void decreaseZoom();
-
- /** Increases the zoom level and queues a redraw. */
- void increaseZoom();
};
diff --git a/Tools/QtBiomeVisualiser/ChunkSource.cpp b/Tools/QtBiomeVisualiser/ChunkSource.cpp
index 2d180f00f..e5cd7a75a 100644
--- a/Tools/QtBiomeVisualiser/ChunkSource.cpp
+++ b/Tools/QtBiomeVisualiser/ChunkSource.cpp
@@ -10,135 +10,6 @@
-/** Map for converting biome values to colors. Initialized from biomeColors[]. */
-static uchar biomeToColor[256 * 4];
-
-/** Map for converting biome values to colors. Used to initialize biomeToColor[].*/
-static struct
-{
- EMCSBiome m_Biome;
- uchar m_Color[3];
-} biomeColors[] =
-{
- { biOcean, { 0x00, 0x00, 0x70 }, },
- { biPlains, { 0x8d, 0xb3, 0x60 }, },
- { biDesert, { 0xfa, 0x94, 0x18 }, },
- { biExtremeHills, { 0x60, 0x60, 0x60 }, },
- { biForest, { 0x05, 0x66, 0x21 }, },
- { biTaiga, { 0x0b, 0x66, 0x59 }, },
- { biSwampland, { 0x2f, 0xff, 0xda }, },
- { biRiver, { 0x30, 0x30, 0xaf }, },
- { biHell, { 0x7f, 0x00, 0x00 }, },
- { biSky, { 0x00, 0x7f, 0xff }, },
- { biFrozenOcean, { 0xa0, 0xa0, 0xdf }, },
- { biFrozenRiver, { 0xa0, 0xa0, 0xff }, },
- { biIcePlains, { 0xff, 0xff, 0xff }, },
- { biIceMountains, { 0xa0, 0xa0, 0xa0 }, },
- { biMushroomIsland, { 0xff, 0x00, 0xff }, },
- { biMushroomShore, { 0xa0, 0x00, 0xff }, },
- { biBeach, { 0xfa, 0xde, 0x55 }, },
- { biDesertHills, { 0xd2, 0x5f, 0x12 }, },
- { biForestHills, { 0x22, 0x55, 0x1c }, },
- { biTaigaHills, { 0x16, 0x39, 0x33 }, },
- { biExtremeHillsEdge, { 0x7f, 0x8f, 0x7f }, },
- { biJungle, { 0x53, 0x7b, 0x09 }, },
- { biJungleHills, { 0x2c, 0x42, 0x05 }, },
-
- { biJungleEdge, { 0x62, 0x8b, 0x17 }, },
- { biDeepOcean, { 0x00, 0x00, 0x30 }, },
- { biStoneBeach, { 0xa2, 0xa2, 0x84 }, },
- { biColdBeach, { 0xfa, 0xf0, 0xc0 }, },
- { biBirchForest, { 0x30, 0x74, 0x44 }, },
- { biBirchForestHills, { 0x1f, 0x5f, 0x32 }, },
- { biRoofedForest, { 0x40, 0x51, 0x1a }, },
- { biColdTaiga, { 0x31, 0x55, 0x4a }, },
- { biColdTaigaHills, { 0x59, 0x7d, 0x72 }, },
- { biMegaTaiga, { 0x59, 0x66, 0x51 }, },
- { biMegaTaigaHills, { 0x59, 0x66, 0x59 }, },
- { biExtremeHillsPlus, { 0x50, 0x70, 0x50 }, },
- { biSavanna, { 0xbd, 0xb2, 0x5f }, },
- { biSavannaPlateau, { 0xa7, 0x9d, 0x64 }, },
- { biMesa, { 0xd9, 0x45, 0x15 }, },
- { biMesaPlateauF, { 0xb0, 0x97, 0x65 }, },
- { biMesaPlateau, { 0xca, 0x8c, 0x65 }, },
-
- // M variants:
- { biSunflowerPlains, { 0xb5, 0xdb, 0x88 }, },
- { biDesertM, { 0xff, 0xbc, 0x40 }, },
- { biExtremeHillsM, { 0x88, 0x88, 0x88 }, },
- { biFlowerForest, { 0x2d, 0x8e, 0x49 }, },
- { biTaigaM, { 0x33, 0x8e, 0x81 }, },
- { biSwamplandM, { 0x07, 0xf9, 0xb2 }, },
- { biIcePlainsSpikes, { 0xb4, 0xdc, 0xdc }, },
- { biJungleM, { 0x7b, 0xa3, 0x31 }, },
- { biJungleEdgeM, { 0x62, 0x8b, 0x17 }, },
- { biBirchForestM, { 0x58, 0x9c, 0x6c }, },
- { biBirchForestHillsM, { 0x47, 0x87, 0x5a }, },
- { biRoofedForestM, { 0x68, 0x79, 0x42 }, },
- { biColdTaigaM, { 0x24, 0x3f, 0x36 }, },
- { biMegaSpruceTaiga, { 0x45, 0x4f, 0x3e }, },
- { biMegaSpruceTaigaHills, { 0x45, 0x4f, 0x4e }, },
- { biExtremeHillsPlusM, { 0x78, 0x98, 0x78 }, },
- { biSavannaM, { 0xe5, 0xda, 0x87 }, },
- { biSavannaPlateauM, { 0xa7, 0x9d, 0x74 }, },
- { biMesaBryce, { 0xff, 0x6d, 0x3d }, },
- { biMesaPlateauFM, { 0xd8, 0xbf, 0x8d }, },
- { biMesaPlateauM, { 0xf2, 0xb4, 0x8d }, },
-} ;
-
-
-
-
-
-static class BiomeColorsInitializer
-{
-public:
- BiomeColorsInitializer(void)
- {
- // Reset all colors to gray:
- for (size_t i = 0; i < ARRAYCOUNT(biomeToColor); i++)
- {
- biomeToColor[i] = 0x7f;
- }
-
- // Set known biomes to their colors:
- for (size_t i = 0; i < ARRAYCOUNT(biomeColors); i++)
- {
- uchar * color = &biomeToColor[4 * biomeColors[i].m_Biome];
- color[0] = biomeColors[i].m_Color[2];
- color[1] = biomeColors[i].m_Color[1];
- color[2] = biomeColors[i].m_Color[0];
- color[3] = 0xff;
- }
- }
-} biomeColorInitializer;
-
-
-
-
-
-/** Converts biomes in an array into the chunk image data. */
-static void biomesToImage(cChunkDef::BiomeMap & a_Biomes, Chunk::Image & a_Image)
-{
- // Make sure the two arrays are of the same size, compile-time.
- // Note that a_Image is actually 4 items per pixel, so the array is 4 times bigger:
- static const char Check1[4 * ARRAYCOUNT(a_Biomes) - ARRAYCOUNT(a_Image) + 1] = {};
- static const char Check2[ARRAYCOUNT(a_Image) - 4 * ARRAYCOUNT(a_Biomes) + 1] = {};
-
- // Convert the biomes into color:
- for (size_t i = 0; i < ARRAYCOUNT(a_Biomes); i++)
- {
- a_Image[4 * i + 0] = biomeToColor[4 * a_Biomes[i] + 0];
- a_Image[4 * i + 1] = biomeToColor[4 * a_Biomes[i] + 1];
- a_Image[4 * i + 2] = biomeToColor[4 * a_Biomes[i] + 2];
- a_Image[4 * i + 3] = biomeToColor[4 * a_Biomes[i] + 3];
- }
-}
-
-
-
-
-
////////////////////////////////////////////////////////////////////////////////
// BioGenSource:
@@ -160,9 +31,7 @@ void BioGenSource::getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChu
QMutexLocker lock(&m_Mtx);
m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, biomes);
}
- Chunk::Image img;
- biomesToImage(biomes, img);
- a_DestChunk->setImage(img);
+ a_DestChunk->setBiomes(biomes);
}
@@ -331,10 +200,7 @@ void AnvilSource::getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChun
{
biomeMap[i] = (EMCSBiome)GetBEInt(beBiomes + 4 * i);
}
- // Render the biomes:
- Chunk::Image img;
- biomesToImage(biomeMap, img);
- a_DestChunk->setImage(img);
+ a_DestChunk->setBiomes(biomeMap);
return;
}
@@ -350,10 +216,7 @@ void AnvilSource::getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChun
{
biomeMap[i] = EMCSBiome(vanillaBiomes[i]);
}
- // Render the biomes:
- Chunk::Image img;
- biomesToImage(biomeMap, img);
- a_DestChunk->setImage(img);
+ a_DestChunk->setBiomes(biomeMap);
}
diff --git a/Tools/QtBiomeVisualiser/MainWindow.cpp b/Tools/QtBiomeVisualiser/MainWindow.cpp
index eb45690c1..63d72f992 100644
--- a/Tools/QtBiomeVisualiser/MainWindow.cpp
+++ b/Tools/QtBiomeVisualiser/MainWindow.cpp
@@ -7,6 +7,7 @@
#include <QFileDialog>
#include <QSettings>
#include <QDirIterator>
+#include <QStatusBar>
#include "inifile/iniFile.h"
#include "ChunkSource.h"
#include "src/Generating/BioGen.h"
@@ -18,6 +19,15 @@
+const double MainWindow::m_ViewZooms[] =
+{
+ 0.0625, 0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 24,
+};
+
+
+
+
+
MainWindow::MainWindow(QWidget * parent) :
QMainWindow(parent),
m_GeneratorSetup(nullptr),
@@ -26,6 +36,20 @@ MainWindow::MainWindow(QWidget * parent) :
initMinecraftPath();
m_BiomeView = new BiomeView();
+ connect(m_BiomeView, SIGNAL(increaseZoom()), this, SLOT(increaseZoom()));
+ connect(m_BiomeView, SIGNAL(decreaseZoom()), this, SLOT(decreaseZoom()));
+ connect(m_BiomeView, SIGNAL(wheelUp()), this, SLOT(increaseZoom()));
+ connect(m_BiomeView, SIGNAL(wheelDown()), this, SLOT(decreaseZoom()));
+
+ m_StatusBar = new QStatusBar();
+ this->setStatusBar(m_StatusBar);
+ m_StatusBlockX = new QLabel(tr("X"));
+ m_StatusBlockZ = new QLabel(tr("Z"));
+ m_StatusBiome = new QLabel(tr("B"));
+ m_StatusBar->addPermanentWidget(m_StatusBlockX);
+ m_StatusBar->addPermanentWidget(m_StatusBlockZ);
+ m_StatusBar->addPermanentWidget(m_StatusBiome);
+
m_MainLayout = new QHBoxLayout();
m_MainLayout->addWidget(m_BiomeView, 1);
m_MainLayout->setMenuBar(menuBar());
@@ -36,6 +60,8 @@ MainWindow::MainWindow(QWidget * parent) :
createActions();
createMenus();
+
+ connect(m_BiomeView, SIGNAL(hoverChanged(int, int, int)), this, SLOT(hoverChanged(int, int, int)));
}
@@ -129,6 +155,79 @@ void MainWindow::openVanillaWorld()
+void MainWindow::centerView()
+{
+ m_BiomeView->setPosition(0, 0);
+}
+
+
+
+
+
+void MainWindow::setViewZoom()
+{
+ // The zoom level is stored in the sender action's data, retrieve it:
+ QAction * action = qobject_cast<QAction *>(sender());
+ if (action == nullptr)
+ {
+ return;
+ }
+ double newZoom = m_ViewZooms[action->data().toInt()];
+ m_BiomeView->setZoomLevel(newZoom);
+ action->setChecked(true);
+}
+
+
+
+
+
+void MainWindow::increaseZoom()
+{
+ // If already at max zoom, bail out:
+ if (m_CurrentZoomLevel >= ARRAYCOUNT(m_ViewZooms) - 1)
+ {
+ return;
+ }
+
+ // Increase the zoom level:
+ m_CurrentZoomLevel += 1;
+ m_actViewZoom[m_CurrentZoomLevel]->setChecked(true);
+ m_BiomeView->setZoomLevel(m_ViewZooms[m_CurrentZoomLevel]);
+}
+
+
+
+
+
+void MainWindow::decreaseZoom()
+{
+ // If already at min zoom, bail out:
+ if (m_CurrentZoomLevel == 0)
+ {
+ return;
+ }
+
+ // Decrease the zoom level:
+ m_CurrentZoomLevel -= 1;
+ m_actViewZoom[m_CurrentZoomLevel]->setChecked(true);
+ m_BiomeView->setZoomLevel(m_ViewZooms[m_CurrentZoomLevel]);
+}
+
+
+
+
+
+void MainWindow::hoverChanged(int a_BlockX, int a_BlockZ, int a_Biome)
+{
+ m_StatusBlockX->setText(tr("X: %1").arg(a_BlockX));
+ m_StatusBlockZ->setText(tr("Z: %1").arg(a_BlockZ));
+ m_StatusBiome->setText (tr("B: %1 (%2)").arg(BiomeToString(a_Biome).c_str()).arg(a_Biome));
+}
+
+
+
+
+
void MainWindow::initMinecraftPath()
{
#ifdef Q_OS_MAC
@@ -147,6 +246,7 @@ void MainWindow::initMinecraftPath()
void MainWindow::createActions()
{
+ // Map menu:
createWorldActions();
m_actNewGen = new QAction(tr("&New generator"), this);
@@ -173,6 +273,26 @@ void MainWindow::createActions()
m_actExit->setShortcut(tr("Alt+X"));
m_actExit->setStatusTip(tr("Exit %1").arg(QApplication::instance()->applicationName()));
connect(m_actExit, SIGNAL(triggered()), this, SLOT(close()));
+
+ // View menu:
+ m_actViewCenter = new QAction(tr("&Reset to center"), this);
+ m_actViewCenter->setStatusTip(tr("Scrolls the view back to the map center"));
+ connect(m_actViewCenter, SIGNAL(triggered()), this, SLOT(centerView()));
+
+ QActionGroup * zoomGroup = new QActionGroup(this);
+ for (int i = 0; i < ARRAYCOUNT(m_ViewZooms); i++)
+ {
+ m_actViewZoom[i] = new QAction(tr("&Zoom %1%").arg(std::floor(m_ViewZooms[i] * 100)), this);
+ m_actViewZoom[i]->setCheckable(true);
+ if ((int)(m_ViewZooms[i] * 16) == 16)
+ {
+ m_actViewZoom[i]->setChecked(true);
+ m_CurrentZoomLevel = i;
+ }
+ m_actViewZoom[i]->setData(QVariant(i));
+ zoomGroup->addAction(m_actViewZoom[i]);
+ connect(m_actViewZoom[i], SIGNAL(triggered()), this, SLOT(setViewZoom()));
+ }
}
@@ -220,11 +340,12 @@ void MainWindow::createWorldActions()
void MainWindow::createMenus()
{
+ // Map menu:
QMenu * file = menuBar()->addMenu(tr("&Map"));
file->addAction(m_actNewGen);
file->addAction(m_actOpenGen);
file->addSeparator();
- QMenu * worlds = file->addMenu(tr("Open existing"));
+ QMenu * worlds = file->addMenu(tr("Open &existing"));
worlds->addActions(m_WorldActions);
if (m_WorldActions.empty())
{
@@ -235,6 +356,15 @@ void MainWindow::createMenus()
file->addAction(m_actReload);
file->addSeparator();
file->addAction(m_actExit);
+
+ // View menu:
+ QMenu * view = menuBar()->addMenu(tr("&View"));
+ view->addAction(m_actViewCenter);
+ view->addSeparator();
+ for (size_t i = 0; i < ARRAYCOUNT(m_actViewZoom); i++)
+ {
+ view->addAction(m_actViewZoom[i]);
+ }
}
diff --git a/Tools/QtBiomeVisualiser/MainWindow.h b/Tools/QtBiomeVisualiser/MainWindow.h
index 6490a937f..27faae7a8 100644
--- a/Tools/QtBiomeVisualiser/MainWindow.h
+++ b/Tools/QtBiomeVisualiser/MainWindow.h
@@ -4,6 +4,7 @@
#include <QList>
#include <QMainWindow>
#include <QHBoxLayout>
+#include <QLabel>
#include "BiomeView.h"
@@ -39,13 +40,33 @@ private slots:
/** Opens a vanilla world that is specified by the calling action. */
void openVanillaWorld();
+ /** Moves the view to the map's center. */
+ void centerView();
+
+ /** Sets the zoom level specified in the triggering action. */
+ void setViewZoom();
+
+ /** Sets a zoom level one step larger than current, if allowed. */
+ void increaseZoom();
+
+ /** Sets a zoom level one step smaller than current, if allowed. */
+ void decreaseZoom();
+
+ /** Updates the statusbar for the specified info about the current block under the cursor. */
+ void hoverChanged(int a_BlockX, int a_BlockZ, int a_Biome);
+
protected:
+ /** The zoom levels */
+ static const double m_ViewZooms[10];
+
// Actions:
QAction * m_actNewGen;
QAction * m_actOpenGen;
QAction * m_actOpenWorld;
QAction * m_actReload;
QAction * m_actExit;
+ QAction * m_actViewCenter;
+ QAction * m_actViewZoom[ARRAYCOUNT(m_ViewZooms)];
/** List of actions that open the specific vanilla world. */
QList<QAction *> m_WorldActions;
@@ -62,9 +83,19 @@ protected:
/** The layout for the window. */
QHBoxLayout * m_MainLayout;
+ /** The status bar that displays the current hover information. */
+ QStatusBar * m_StatusBar;
+
+ QLabel * m_StatusBlockX;
+ QLabel * m_StatusBlockZ;
+ QLabel * m_StatusBiome;
+
/** The separator line between biome view and generator setup. */
QWidget * m_LineSeparator;
+ /** Index into m_ViewZooms[] for the current zoom level. */
+ size_t m_CurrentZoomLevel;
+
/** Initializes the m_MinecraftPath based on the proper MC path */
void initMinecraftPath();
diff --git a/Tools/QtBiomeVisualiser/QtChunk.cpp b/Tools/QtBiomeVisualiser/QtChunk.cpp
index 80109b2f8..031aa3654 100644
--- a/Tools/QtBiomeVisualiser/QtChunk.cpp
+++ b/Tools/QtBiomeVisualiser/QtChunk.cpp
@@ -5,6 +5,138 @@
+/** Map for converting biome values to colors. Initialized from biomeColors[]. */
+static uchar biomeToColor[256 * 4];
+
+/** Map for converting biome values to colors. Used to initialize biomeToColor[].*/
+static struct
+{
+ EMCSBiome m_Biome;
+ uchar m_Color[3];
+} biomeColors[] =
+{
+ { biOcean, { 0x00, 0x00, 0x70 }, },
+ { biPlains, { 0x8d, 0xb3, 0x60 }, },
+ { biDesert, { 0xfa, 0x94, 0x18 }, },
+ { biExtremeHills, { 0x60, 0x60, 0x60 }, },
+ { biForest, { 0x05, 0x66, 0x21 }, },
+ { biTaiga, { 0x0b, 0x66, 0x59 }, },
+ { biSwampland, { 0x2f, 0xff, 0xda }, },
+ { biRiver, { 0x30, 0x30, 0xaf }, },
+ { biHell, { 0x7f, 0x00, 0x00 }, },
+ { biSky, { 0x00, 0x7f, 0xff }, },
+ { biFrozenOcean, { 0xa0, 0xa0, 0xdf }, },
+ { biFrozenRiver, { 0xa0, 0xa0, 0xff }, },
+ { biIcePlains, { 0xff, 0xff, 0xff }, },
+ { biIceMountains, { 0xa0, 0xa0, 0xa0 }, },
+ { biMushroomIsland, { 0xff, 0x00, 0xff }, },
+ { biMushroomShore, { 0xa0, 0x00, 0xff }, },
+ { biBeach, { 0xfa, 0xde, 0x55 }, },
+ { biDesertHills, { 0xd2, 0x5f, 0x12 }, },
+ { biForestHills, { 0x22, 0x55, 0x1c }, },
+ { biTaigaHills, { 0x16, 0x39, 0x33 }, },
+ { biExtremeHillsEdge, { 0x7f, 0x8f, 0x7f }, },
+ { biJungle, { 0x53, 0x7b, 0x09 }, },
+ { biJungleHills, { 0x2c, 0x42, 0x05 }, },
+
+ { biJungleEdge, { 0x62, 0x8b, 0x17 }, },
+ { biDeepOcean, { 0x00, 0x00, 0x30 }, },
+ { biStoneBeach, { 0xa2, 0xa2, 0x84 }, },
+ { biColdBeach, { 0xfa, 0xf0, 0xc0 }, },
+ { biBirchForest, { 0x30, 0x74, 0x44 }, },
+ { biBirchForestHills, { 0x1f, 0x5f, 0x32 }, },
+ { biRoofedForest, { 0x40, 0x51, 0x1a }, },
+ { biColdTaiga, { 0x31, 0x55, 0x4a }, },
+ { biColdTaigaHills, { 0x59, 0x7d, 0x72 }, },
+ { biMegaTaiga, { 0x59, 0x66, 0x51 }, },
+ { biMegaTaigaHills, { 0x59, 0x66, 0x59 }, },
+ { biExtremeHillsPlus, { 0x50, 0x70, 0x50 }, },
+ { biSavanna, { 0xbd, 0xb2, 0x5f }, },
+ { biSavannaPlateau, { 0xa7, 0x9d, 0x64 }, },
+ { biMesa, { 0xd9, 0x45, 0x15 }, },
+ { biMesaPlateauF, { 0xb0, 0x97, 0x65 }, },
+ { biMesaPlateau, { 0xca, 0x8c, 0x65 }, },
+
+ // M variants:
+ { biSunflowerPlains, { 0xb5, 0xdb, 0x88 }, },
+ { biDesertM, { 0xff, 0xbc, 0x40 }, },
+ { biExtremeHillsM, { 0x88, 0x88, 0x88 }, },
+ { biFlowerForest, { 0x2d, 0x8e, 0x49 }, },
+ { biTaigaM, { 0x33, 0x8e, 0x81 }, },
+ { biSwamplandM, { 0x07, 0xf9, 0xb2 }, },
+ { biIcePlainsSpikes, { 0xb4, 0xdc, 0xdc }, },
+ { biJungleM, { 0x7b, 0xa3, 0x31 }, },
+ { biJungleEdgeM, { 0x62, 0x8b, 0x17 }, },
+ { biBirchForestM, { 0x58, 0x9c, 0x6c }, },
+ { biBirchForestHillsM, { 0x47, 0x87, 0x5a }, },
+ { biRoofedForestM, { 0x68, 0x79, 0x42 }, },
+ { biColdTaigaM, { 0x24, 0x3f, 0x36 }, },
+ { biMegaSpruceTaiga, { 0x45, 0x4f, 0x3e }, },
+ { biMegaSpruceTaigaHills, { 0x45, 0x4f, 0x4e }, },
+ { biExtremeHillsPlusM, { 0x78, 0x98, 0x78 }, },
+ { biSavannaM, { 0xe5, 0xda, 0x87 }, },
+ { biSavannaPlateauM, { 0xa7, 0x9d, 0x74 }, },
+ { biMesaBryce, { 0xff, 0x6d, 0x3d }, },
+ { biMesaPlateauFM, { 0xd8, 0xbf, 0x8d }, },
+ { biMesaPlateauM, { 0xf2, 0xb4, 0x8d }, },
+} ;
+
+
+
+
+
+static class BiomeColorsInitializer
+{
+public:
+ BiomeColorsInitializer(void)
+ {
+ // Reset all colors to gray:
+ for (size_t i = 0; i < ARRAYCOUNT(biomeToColor); i++)
+ {
+ biomeToColor[i] = 0x7f;
+ }
+
+ // Set known biomes to their colors:
+ for (size_t i = 0; i < ARRAYCOUNT(biomeColors); i++)
+ {
+ uchar * color = &biomeToColor[4 * biomeColors[i].m_Biome];
+ color[0] = biomeColors[i].m_Color[2];
+ color[1] = biomeColors[i].m_Color[1];
+ color[2] = biomeColors[i].m_Color[0];
+ color[3] = 0xff;
+ }
+ }
+} biomeColorInitializer;
+
+
+
+
+
+/** Converts biomes in an array into the chunk image data. */
+static void biomesToImage(const cChunkDef::BiomeMap & a_Biomes, Chunk::Image & a_Image)
+{
+ // Make sure the two arrays are of the same size, compile-time.
+ // Note that a_Image is actually 4 items per pixel, so the array is 4 times bigger:
+ static const char Check1[4 * ARRAYCOUNT(a_Biomes) - ARRAYCOUNT(a_Image) + 1] = {};
+ static const char Check2[ARRAYCOUNT(a_Image) - 4 * ARRAYCOUNT(a_Biomes) + 1] = {};
+
+ // Convert the biomes into color:
+ for (size_t i = 0; i < ARRAYCOUNT(a_Biomes); i++)
+ {
+ a_Image[4 * i + 0] = biomeToColor[4 * a_Biomes[i] + 0];
+ a_Image[4 * i + 1] = biomeToColor[4 * a_Biomes[i] + 1];
+ a_Image[4 * i + 2] = biomeToColor[4 * a_Biomes[i] + 2];
+ a_Image[4 * i + 3] = biomeToColor[4 * a_Biomes[i] + 3];
+ }
+}
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Chunk:
+
Chunk::Chunk() :
m_IsValid(false)
{
@@ -24,12 +156,35 @@ const uchar * Chunk::getImage(void) const
-void Chunk::setImage(const Image & a_Image)
+void Chunk::setBiomes(const cChunkDef::BiomeMap & a_Biomes)
{
- memcpy(m_Image, a_Image, sizeof(a_Image));
+ memcpy(m_Biomes, a_Biomes, sizeof(m_Biomes));
+ renderBiomes();
m_IsValid = true;
}
+
+EMCSBiome Chunk::getBiome(int a_RelX, int a_RelZ)
+{
+ if (!m_IsValid)
+ {
+ return biInvalidBiome;
+ }
+ return cChunkDef::GetBiome(m_Biomes, a_RelX, a_RelZ);
+}
+
+
+
+
+
+void Chunk::renderBiomes()
+{
+ biomesToImage(m_Biomes, m_Image);
+}
+
+
+
+
diff --git a/Tools/QtBiomeVisualiser/QtChunk.h b/Tools/QtBiomeVisualiser/QtChunk.h
index 03e7bd1b3..74321577a 100644
--- a/Tools/QtBiomeVisualiser/QtChunk.h
+++ b/Tools/QtBiomeVisualiser/QtChunk.h
@@ -21,8 +21,12 @@ public:
/** Returns the image of the chunk's biomes. Assumes that the chunk is valid. */
const uchar * getImage(void) const;
- /** Sets the image data for this chunk. */
- void setImage(const Image & a_Image);
+ /** Sets the biomes to m_Biomes and renders them into m_Image. */
+ void setBiomes(const cChunkDef::BiomeMap & a_Biomes);
+
+ /** Returns the biome at the specified relative coords, or biInvalidBiome if not valid.
+ Coords must be valid inside this chunk. */
+ EMCSBiome getBiome(int a_RelX, int a_RelZ);
protected:
/** Flag that specifies if the chunk data is valid - loaded or generated. */
@@ -30,6 +34,13 @@ protected:
/** Cached rendered image of this chunk's biomes. Updated in render(). */
Image m_Image;
+
+ /** Biomes comprising the chunk, in the X + 16 * Z ordering. */
+ cChunkDef::BiomeMap m_Biomes;
+
+
+ /** Renders biomes from m_Biomes into m_Image. */
+ void renderBiomes();
};
typedef std::shared_ptr<Chunk> ChunkPtr;
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index d8134f159..f4764447c 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -1957,26 +1957,20 @@ static int tolua_cPlayer_PermissionMatches(lua_State * tolua_S)
// Check the params:
cLuaState L(tolua_S);
if (
- !L.CheckParamUserType(1, "cPlayer") ||
- !L.CheckParamString (2, 3) ||
- !L.CheckParamEnd (4)
+ !L.CheckParamUserTable(1, "cPlayer") ||
+ !L.CheckParamString (2, 3) ||
+ !L.CheckParamEnd (4)
)
{
return 0;
}
// Get the params:
- cPlayer * self = (cPlayer *)tolua_tousertype(tolua_S, 1, NULL);
- if (self == NULL)
- {
- LOGWARNING("%s: invalid self (%p)", __FUNCTION__, self);
- return 0;
- }
AString Permission, Template;
L.GetStackValues(2, Permission, Template);
// Push the result of the match:
- L.Push(self->PermissionMatches(StringSplit(Permission, "."), StringSplit(Template, ".")));
+ L.Push(cPlayer::PermissionMatches(StringSplit(Permission, "."), StringSplit(Template, ".")));
return 1;
}
diff --git a/src/BlockEntities/BeaconEntity.cpp b/src/BlockEntities/BeaconEntity.cpp
index dcf659f47..02f45a097 100644
--- a/src/BlockEntities/BeaconEntity.cpp
+++ b/src/BlockEntities/BeaconEntity.cpp
@@ -303,68 +303,6 @@ void cBeaconEntity::UsedBy(cPlayer * a_Player)
-bool cBeaconEntity::LoadFromJson(const Json::Value & a_Value)
-{
- m_PosX = a_Value.get("x", 0).asInt();
- m_PosY = a_Value.get("y", 0).asInt();
- m_PosZ = a_Value.get("z", 0).asInt();
-
- Json::Value AllSlots = a_Value.get("Slots", 0);
- int SlotIdx = 0;
- for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr)
- {
- cItem Item;
- Item.FromJson(*itr);
- SetSlot(SlotIdx, Item);
- SlotIdx++;
- }
-
- m_BeaconLevel = (char)a_Value.get("Level", 0).asInt();
- int PrimaryEffect = a_Value.get("PrimaryEffect", 0).asInt();
- int SecondaryEffect = a_Value.get("SecondaryEffect", 0).asInt();
-
- if ((PrimaryEffect >= 0) && (PrimaryEffect <= (int)cEntityEffect::effSaturation))
- {
- m_PrimaryEffect = (cEntityEffect::eType)PrimaryEffect;
- }
-
- if ((SecondaryEffect >= 0) && (SecondaryEffect <= (int)cEntityEffect::effSaturation))
- {
- m_SecondaryEffect = (cEntityEffect::eType)SecondaryEffect;
- }
-
- return true;
-}
-
-
-
-
-
-void cBeaconEntity::SaveToJson(Json::Value& a_Value)
-{
- a_Value["x"] = m_PosX;
- a_Value["y"] = m_PosY;
- a_Value["z"] = m_PosZ;
-
- Json::Value AllSlots;
- int NumSlots = m_Contents.GetNumSlots();
- for (int i = 0; i < NumSlots; i++)
- {
- Json::Value Slot;
- m_Contents.GetSlot(i).GetJson(Slot);
- AllSlots.append(Slot);
- }
- a_Value["Slots"] = AllSlots;
-
- a_Value["Level"] = m_BeaconLevel;
- a_Value["PrimaryEffect"] = (int)m_PrimaryEffect;
- a_Value["SecondaryEffect"] = (int)m_SecondaryEffect;
-}
-
-
-
-
-
void cBeaconEntity::SendTo(cClientHandle & a_Client)
{
a_Client.SendUpdateBlockEntity(*this);
diff --git a/src/BlockEntities/BeaconEntity.h b/src/BlockEntities/BeaconEntity.h
index 0d7150aef..8c2dad254 100644
--- a/src/BlockEntities/BeaconEntity.h
+++ b/src/BlockEntities/BeaconEntity.h
@@ -34,9 +34,7 @@ public:
cBeaconEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
- bool LoadFromJson(const Json::Value & a_Value);
// cBlockEntity overrides:
- virtual void SaveToJson(Json::Value& a_Value) override;
virtual void SendTo(cClientHandle & a_Client) override;
virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
virtual void UsedBy(cPlayer * a_Player) override;
diff --git a/src/BlockEntities/BlockEntity.h b/src/BlockEntities/BlockEntity.h
index 066bbc696..54ab40f3e 100644
--- a/src/BlockEntities/BlockEntity.h
+++ b/src/BlockEntities/BlockEntity.h
@@ -74,8 +74,6 @@ public:
int GetRelZ(void) const { return m_RelZ; }
// tolua_end
-
- virtual void SaveToJson (Json::Value & a_Value) = 0;
/// Called when a player uses this entity; should open the UI window
virtual void UsedBy( cPlayer * a_Player) = 0;
diff --git a/src/BlockEntities/ChestEntity.cpp b/src/BlockEntities/ChestEntity.cpp
index a89e5747e..19d88b646 100644
--- a/src/BlockEntities/ChestEntity.cpp
+++ b/src/BlockEntities/ChestEntity.cpp
@@ -33,48 +33,6 @@ cChestEntity::~cChestEntity()
-bool cChestEntity::LoadFromJson(const Json::Value & a_Value)
-{
- m_PosX = a_Value.get("x", 0).asInt();
- m_PosY = a_Value.get("y", 0).asInt();
- m_PosZ = a_Value.get("z", 0).asInt();
-
- Json::Value AllSlots = a_Value.get("Slots", 0);
- int SlotIdx = 0;
- for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr)
- {
- cItem Item;
- Item.FromJson(*itr);
- SetSlot(SlotIdx, Item);
- SlotIdx++;
- }
- return true;
-}
-
-
-
-
-
-void cChestEntity::SaveToJson(Json::Value & a_Value)
-{
- a_Value["x"] = m_PosX;
- a_Value["y"] = m_PosY;
- a_Value["z"] = m_PosZ;
-
- Json::Value AllSlots;
- for (int i = m_Contents.GetNumSlots() - 1; i >= 0; i--)
- {
- Json::Value Slot;
- m_Contents.GetSlot(i).GetJson(Slot);
- AllSlots.append(Slot);
- }
- a_Value["Slots"] = AllSlots;
-}
-
-
-
-
-
void cChestEntity::SendTo(cClientHandle & a_Client)
{
// The chest entity doesn't need anything sent to the client when it's created / gets in the viewdistance
diff --git a/src/BlockEntities/ChestEntity.h b/src/BlockEntities/ChestEntity.h
index fa36e08be..af5d851a8 100644
--- a/src/BlockEntities/ChestEntity.h
+++ b/src/BlockEntities/ChestEntity.h
@@ -39,11 +39,8 @@ public:
virtual ~cChestEntity();
static const char * GetClassStatic(void) { return "cChestEntity"; }
-
- bool LoadFromJson(const Json::Value & a_Value);
// cBlockEntity overrides:
- virtual void SaveToJson(Json::Value & a_Value) override;
virtual void SendTo(cClientHandle & a_Client) override;
virtual void UsedBy(cPlayer * a_Player) override;
diff --git a/src/BlockEntities/CommandBlockEntity.cpp b/src/BlockEntities/CommandBlockEntity.cpp
index 318874a9b..1a5a3f01e 100644
--- a/src/BlockEntities/CommandBlockEntity.cpp
+++ b/src/BlockEntities/CommandBlockEntity.cpp
@@ -152,38 +152,6 @@ void cCommandBlockEntity::SendTo(cClientHandle & a_Client)
-bool cCommandBlockEntity::LoadFromJson(const Json::Value & a_Value)
-{
- m_PosX = a_Value.get("x", 0).asInt();
- m_PosY = a_Value.get("y", 0).asInt();
- m_PosZ = a_Value.get("z", 0).asInt();
-
- m_Command = a_Value.get("Command", "").asString();
- m_LastOutput = a_Value.get("LastOutput", "").asString();
- m_Result = (NIBBLETYPE)a_Value.get("SuccessCount", 0).asInt();
-
- return true;
-}
-
-
-
-
-
-void cCommandBlockEntity::SaveToJson(Json::Value & a_Value)
-{
- a_Value["x"] = m_PosX;
- a_Value["y"] = m_PosY;
- a_Value["z"] = m_PosZ;
-
- a_Value["Command"] = m_Command;
- a_Value["LastOutput"] = m_LastOutput;
- a_Value["SuccessCount"] = m_Result;
-}
-
-
-
-
-
void cCommandBlockEntity::Execute()
{
ASSERT(m_World != NULL); // Execute should not be called before the command block is attached to a world
diff --git a/src/BlockEntities/CommandBlockEntity.h b/src/BlockEntities/CommandBlockEntity.h
index 22cd44322..939f38610 100644
--- a/src/BlockEntities/CommandBlockEntity.h
+++ b/src/BlockEntities/CommandBlockEntity.h
@@ -39,9 +39,6 @@ public:
/// Creates a new empty command block entity
cCommandBlockEntity(int a_X, int a_Y, int a_Z, cWorld * a_World);
- bool LoadFromJson( const Json::Value& a_Value);
- virtual void SaveToJson(Json::Value& a_Value) override;
-
virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
virtual void SendTo(cClientHandle & a_Client) override;
virtual void UsedBy(cPlayer * a_Player) override;
diff --git a/src/BlockEntities/DropSpenserEntity.cpp b/src/BlockEntities/DropSpenserEntity.cpp
index 05c7403e7..dac951b27 100644
--- a/src/BlockEntities/DropSpenserEntity.cpp
+++ b/src/BlockEntities/DropSpenserEntity.cpp
@@ -142,54 +142,6 @@ bool cDropSpenserEntity::Tick(float a_Dt, cChunk & a_Chunk)
-bool cDropSpenserEntity::LoadFromJson(const Json::Value & a_Value)
-{
- m_PosX = a_Value.get("x", 0).asInt();
- m_PosY = a_Value.get("y", 0).asInt();
- m_PosZ = a_Value.get("z", 0).asInt();
-
- Json::Value AllSlots = a_Value.get("Slots", 0);
- int SlotIdx = 0;
- for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr)
- {
- cItem Contents;
- Contents.FromJson(*itr);
- m_Contents.SetSlot(SlotIdx, Contents);
- SlotIdx++;
- if (SlotIdx >= m_Contents.GetNumSlots())
- {
- return true;
- }
- }
-
- return true;
-}
-
-
-
-
-
-void cDropSpenserEntity::SaveToJson(Json::Value & a_Value)
-{
- a_Value["x"] = m_PosX;
- a_Value["y"] = m_PosY;
- a_Value["z"] = m_PosZ;
-
- Json::Value AllSlots;
- int NumSlots = m_Contents.GetNumSlots();
- for (int i = 0; i < NumSlots; i++)
- {
- Json::Value Slot;
- m_Contents.GetSlot(i).GetJson(Slot);
- AllSlots.append(Slot);
- }
- a_Value["Slots"] = AllSlots;
-}
-
-
-
-
-
void cDropSpenserEntity::SendTo(cClientHandle & a_Client)
{
// Nothing needs to be sent
diff --git a/src/BlockEntities/DropSpenserEntity.h b/src/BlockEntities/DropSpenserEntity.h
index c70cd0531..23f0ae89a 100644
--- a/src/BlockEntities/DropSpenserEntity.h
+++ b/src/BlockEntities/DropSpenserEntity.h
@@ -49,11 +49,8 @@ public:
virtual ~cDropSpenserEntity();
static const char * GetClassStatic(void) { return "cDropSpenserEntity"; }
-
- bool LoadFromJson(const Json::Value & a_Value);
// cBlockEntity overrides:
- virtual void SaveToJson(Json::Value & a_Value) override;
virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
virtual void SendTo(cClientHandle & a_Client) override;
virtual void UsedBy(cPlayer * a_Player) override;
diff --git a/src/BlockEntities/EnderChestEntity.h b/src/BlockEntities/EnderChestEntity.h
index 311af8d76..2719eb5e4 100644
--- a/src/BlockEntities/EnderChestEntity.h
+++ b/src/BlockEntities/EnderChestEntity.h
@@ -25,7 +25,6 @@ public:
// cBlockEntity overrides:
virtual void UsedBy(cPlayer * a_Player) override;
- virtual void SaveToJson(Json::Value & a_Value) override { UNUSED(a_Value); }
virtual void SendTo(cClientHandle & a_Client) override { UNUSED(a_Client); }
static void LoadFromJson(const Json::Value & a_Value, cItemGrid & a_Grid);
diff --git a/src/BlockEntities/FlowerPotEntity.cpp b/src/BlockEntities/FlowerPotEntity.cpp
index d2bc59d34..01560f814 100644
--- a/src/BlockEntities/FlowerPotEntity.cpp
+++ b/src/BlockEntities/FlowerPotEntity.cpp
@@ -72,37 +72,6 @@ void cFlowerPotEntity::Destroy(void)
-bool cFlowerPotEntity::LoadFromJson(const Json::Value & a_Value)
-{
- m_PosX = a_Value.get("x", 0).asInt();
- m_PosY = a_Value.get("y", 0).asInt();
- m_PosZ = a_Value.get("z", 0).asInt();
-
- m_Item = cItem();
- m_Item.FromJson(a_Value.get("Item", 0));
-
- return true;
-}
-
-
-
-
-
-void cFlowerPotEntity::SaveToJson(Json::Value & a_Value)
-{
- a_Value["x"] = m_PosX;
- a_Value["y"] = m_PosY;
- a_Value["z"] = m_PosZ;
-
- Json::Value Item;
- m_Item.GetJson(Item);
- a_Value["Item"] = Item;
-}
-
-
-
-
-
bool cFlowerPotEntity::IsFlower(short m_ItemType, short m_ItemData)
{
switch (m_ItemType)
diff --git a/src/BlockEntities/FlowerPotEntity.h b/src/BlockEntities/FlowerPotEntity.h
index b86f9c840..b68d3b118 100644
--- a/src/BlockEntities/FlowerPotEntity.h
+++ b/src/BlockEntities/FlowerPotEntity.h
@@ -38,9 +38,6 @@ public:
/** Creates a new flowerpot entity at the specified block coords. a_World may be NULL */
cFlowerPotEntity(int a_BlocX, int a_BlockY, int a_BlockZ, cWorld * a_World);
-
- bool LoadFromJson( const Json::Value& a_Value);
- virtual void SaveToJson(Json::Value& a_Value) override;
virtual void Destroy(void) override;
diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp
index fb88e9b35..4452fc00a 100644
--- a/src/BlockEntities/FurnaceEntity.cpp
+++ b/src/BlockEntities/FurnaceEntity.cpp
@@ -129,60 +129,6 @@ bool cFurnaceEntity::Tick(float a_Dt, cChunk & a_Chunk)
-bool cFurnaceEntity::LoadFromJson(const Json::Value & a_Value)
-{
- m_PosX = a_Value.get("x", 0).asInt();
- m_PosY = a_Value.get("y", 0).asInt();
- m_PosZ = a_Value.get("z", 0).asInt();
-
- Json::Value AllSlots = a_Value.get("Slots", 0);
- int SlotIdx = 0;
- for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr)
- {
- cItem Item;
- Item.FromJson(*itr);
- SetSlot(SlotIdx, Item);
- SlotIdx++;
- }
-
- m_NeedCookTime = (int)(a_Value.get("CookTime", 0).asDouble() / 50);
- m_TimeCooked = (int)(a_Value.get("TimeCooked", 0).asDouble() / 50);
- m_FuelBurnTime = (int)(a_Value.get("BurnTime", 0).asDouble() / 50);
- m_TimeBurned = (int)(a_Value.get("TimeBurned", 0).asDouble() / 50);
-
- return true;
-}
-
-
-
-
-
-void cFurnaceEntity::SaveToJson( Json::Value& a_Value)
-{
- a_Value["x"] = m_PosX;
- a_Value["y"] = m_PosY;
- a_Value["z"] = m_PosZ;
-
- Json::Value AllSlots;
- int NumSlots = m_Contents.GetNumSlots();
- for (int i = 0; i < NumSlots; i++)
- {
- Json::Value Slot;
- m_Contents.GetSlot(i).GetJson(Slot);
- AllSlots.append(Slot);
- }
- a_Value["Slots"] = AllSlots;
-
- a_Value["CookTime"] = m_NeedCookTime * 50;
- a_Value["TimeCooked"] = m_TimeCooked * 50;
- a_Value["BurnTime"] = m_FuelBurnTime * 50;
- a_Value["TimeBurned"] = m_TimeBurned * 50;
-}
-
-
-
-
-
void cFurnaceEntity::SendTo(cClientHandle & a_Client)
{
// Nothing needs to be sent
diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h
index 7ac25cf52..ed3317af6 100644
--- a/src/BlockEntities/FurnaceEntity.h
+++ b/src/BlockEntities/FurnaceEntity.h
@@ -44,11 +44,8 @@ public:
virtual ~cFurnaceEntity();
static const char * GetClassStatic() { return "cFurnaceEntity"; }
-
- bool LoadFromJson(const Json::Value & a_Value);
// cBlockEntity overrides:
- virtual void SaveToJson(Json::Value & a_Value) override;
virtual void SendTo(cClientHandle & a_Client) override;
virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
virtual void UsedBy(cPlayer * a_Player) override;
diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp
index 444378c92..103f516fc 100644
--- a/src/BlockEntities/HopperEntity.cpp
+++ b/src/BlockEntities/HopperEntity.cpp
@@ -70,17 +70,6 @@ bool cHopperEntity::Tick(float a_Dt, cChunk & a_Chunk)
-void cHopperEntity::SaveToJson(Json::Value & a_Value)
-{
- UNUSED(a_Value);
- // TODO
- LOGWARNING("%s: Not implemented yet", __FUNCTION__);
-}
-
-
-
-
-
void cHopperEntity::SendTo(cClientHandle & a_Client)
{
// The hopper entity doesn't need anything sent to the client when it's created / gets in the viewdistance
diff --git a/src/BlockEntities/HopperEntity.h b/src/BlockEntities/HopperEntity.h
index 8e856fcda..5d06581c2 100644
--- a/src/BlockEntities/HopperEntity.h
+++ b/src/BlockEntities/HopperEntity.h
@@ -49,7 +49,6 @@ protected:
// cBlockEntity overrides:
virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
- virtual void SaveToJson(Json::Value & a_Value) override;
virtual void SendTo(cClientHandle & a_Client) override;
virtual void UsedBy(cPlayer * a_Player) override;
diff --git a/src/BlockEntities/JukeboxEntity.cpp b/src/BlockEntities/JukeboxEntity.cpp
index 1131b99b0..bb9b335e0 100644
--- a/src/BlockEntities/JukeboxEntity.cpp
+++ b/src/BlockEntities/JukeboxEntity.cpp
@@ -117,31 +117,3 @@ void cJukeboxEntity::SetRecord(int a_Record)
-
-bool cJukeboxEntity::LoadFromJson(const Json::Value & a_Value)
-{
- m_PosX = a_Value.get("x", 0).asInt();
- m_PosY = a_Value.get("y", 0).asInt();
- m_PosZ = a_Value.get("z", 0).asInt();
-
- m_Record = a_Value.get("Record", 0).asInt();
-
- return true;
-}
-
-
-
-
-
-void cJukeboxEntity::SaveToJson(Json::Value & a_Value)
-{
- a_Value["x"] = m_PosX;
- a_Value["y"] = m_PosY;
- a_Value["z"] = m_PosZ;
-
- a_Value["Record"] = m_Record;
-}
-
-
-
-
diff --git a/src/BlockEntities/JukeboxEntity.h b/src/BlockEntities/JukeboxEntity.h
index a6fdf3c7e..49d2faa89 100644
--- a/src/BlockEntities/JukeboxEntity.h
+++ b/src/BlockEntities/JukeboxEntity.h
@@ -29,9 +29,6 @@ public:
cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
virtual ~cJukeboxEntity();
- bool LoadFromJson(const Json::Value & a_Value);
- virtual void SaveToJson(Json::Value & a_Value) override;
-
// tolua_begin
int GetRecord(void);
diff --git a/src/BlockEntities/MobHeadEntity.cpp b/src/BlockEntities/MobHeadEntity.cpp
index 9a1a40abe..67e13ffb2 100644
--- a/src/BlockEntities/MobHeadEntity.cpp
+++ b/src/BlockEntities/MobHeadEntity.cpp
@@ -77,35 +77,3 @@ void cMobHeadEntity::SendTo(cClientHandle & a_Client)
-
-bool cMobHeadEntity::LoadFromJson(const Json::Value & a_Value)
-{
- m_PosX = a_Value.get("x", 0).asInt();
- m_PosY = a_Value.get("y", 0).asInt();
- m_PosZ = a_Value.get("z", 0).asInt();
-
- m_Type = static_cast<eMobHeadType>(a_Value.get("Type", 0).asInt());
- m_Rotation = static_cast<eMobHeadRotation>(a_Value.get("Rotation", 0).asInt());
- m_Owner = a_Value.get("Owner", "").asString();
-
- return true;
-}
-
-
-
-
-
-void cMobHeadEntity::SaveToJson(Json::Value & a_Value)
-{
- a_Value["x"] = m_PosX;
- a_Value["y"] = m_PosY;
- a_Value["z"] = m_PosZ;
-
- a_Value["Type"] = m_Type;
- a_Value["Rotation"] = m_Rotation;
- a_Value["Owner"] = m_Owner;
-}
-
-
-
-
diff --git a/src/BlockEntities/MobHeadEntity.h b/src/BlockEntities/MobHeadEntity.h
index 45fa0d951..fcdeaa8a6 100644
--- a/src/BlockEntities/MobHeadEntity.h
+++ b/src/BlockEntities/MobHeadEntity.h
@@ -37,9 +37,6 @@ public:
/** Creates a new mob head entity at the specified block coords. a_World may be NULL */
cMobHeadEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
- bool LoadFromJson( const Json::Value& a_Value);
- virtual void SaveToJson(Json::Value& a_Value) override;
-
// tolua_begin
/** Set the Type */
diff --git a/src/BlockEntities/NoteEntity.cpp b/src/BlockEntities/NoteEntity.cpp
index ecbedbcb1..a9af13c55 100644
--- a/src/BlockEntities/NoteEntity.cpp
+++ b/src/BlockEntities/NoteEntity.cpp
@@ -124,32 +124,3 @@ void cNoteEntity::IncrementPitch(void)
-
-bool cNoteEntity::LoadFromJson(const Json::Value & a_Value)
-{
-
- m_PosX = a_Value.get("x", 0).asInt();
- m_PosY = a_Value.get("y", 0).asInt();
- m_PosZ = a_Value.get("z", 0).asInt();
-
- m_Pitch = (char)a_Value.get("p", 0).asInt();
-
- return true;
-}
-
-
-
-
-
-void cNoteEntity::SaveToJson(Json::Value & a_Value)
-{
- a_Value["x"] = m_PosX;
- a_Value["y"] = m_PosY;
- a_Value["z"] = m_PosZ;
-
- a_Value["p"] = m_Pitch;
-}
-
-
-
-
diff --git a/src/BlockEntities/NoteEntity.h b/src/BlockEntities/NoteEntity.h
index f538de060..d1ffa126a 100644
--- a/src/BlockEntities/NoteEntity.h
+++ b/src/BlockEntities/NoteEntity.h
@@ -44,9 +44,6 @@ public:
cNoteEntity(int a_X, int a_Y, int a_Z, cWorld * a_World);
virtual ~cNoteEntity() {}
- bool LoadFromJson(const Json::Value & a_Value);
- virtual void SaveToJson(Json::Value & a_Value) override;
-
// tolua_begin
char GetPitch(void);
diff --git a/src/BlockEntities/SignEntity.cpp b/src/BlockEntities/SignEntity.cpp
index 423d254d2..d048d0218 100644
--- a/src/BlockEntities/SignEntity.cpp
+++ b/src/BlockEntities/SignEntity.cpp
@@ -22,7 +22,6 @@ cSignEntity::cSignEntity(BLOCKTYPE a_BlockType, int a_X, int a_Y, int a_Z, cWorl
-// It don't do anything when 'used'
void cSignEntity::UsedBy(cPlayer * a_Player)
{
UNUSED(a_Player);
@@ -80,37 +79,3 @@ void cSignEntity::SendTo(cClientHandle & a_Client)
-
-bool cSignEntity::LoadFromJson(const Json::Value & a_Value)
-{
- m_PosX = a_Value.get("x", 0).asInt();
- m_PosY = a_Value.get("y", 0).asInt();
- m_PosZ = a_Value.get("z", 0).asInt();
-
- m_Line[0] = a_Value.get("Line1", "").asString();
- m_Line[1] = a_Value.get("Line2", "").asString();
- m_Line[2] = a_Value.get("Line3", "").asString();
- m_Line[3] = a_Value.get("Line4", "").asString();
-
- return true;
-}
-
-
-
-
-
-void cSignEntity::SaveToJson(Json::Value & a_Value)
-{
- a_Value["x"] = m_PosX;
- a_Value["y"] = m_PosY;
- a_Value["z"] = m_PosZ;
-
- a_Value["Line1"] = m_Line[0];
- a_Value["Line2"] = m_Line[1];
- a_Value["Line3"] = m_Line[2];
- a_Value["Line4"] = m_Line[3];
-}
-
-
-
-
diff --git a/src/BlockEntities/SignEntity.h b/src/BlockEntities/SignEntity.h
index 33af100a4..53c43b758 100644
--- a/src/BlockEntities/SignEntity.h
+++ b/src/BlockEntities/SignEntity.h
@@ -37,9 +37,6 @@ public:
/// Creates a new empty sign entity at the specified block coords and block type (wall or standing). a_World may be NULL
cSignEntity(BLOCKTYPE a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World);
- bool LoadFromJson( const Json::Value& a_Value);
- virtual void SaveToJson(Json::Value& a_Value) override;
-
// tolua_begin
/// Sets all the sign's lines
diff --git a/src/BlockID.h b/src/BlockID.h
index 9a8830710..69b5e2fe0 100644
--- a/src/BlockID.h
+++ b/src/BlockID.h
@@ -186,6 +186,9 @@ enum ENUM_BLOCK_ID
E_BLOCK_BLOCK_OF_COAL = 173,
E_BLOCK_PACKED_ICE = 174,
E_BLOCK_BIG_FLOWER = 175,
+ E_BLOCK_STANDING_BANNER = 176,
+ E_BLOCK_WALL_BANNER = 177,
+ E_BLOCK_INVERTED_DAYLIGHT_SENSOR = 178,
E_BLOCK_RED_SANDSTONE = 179,
E_BLOCK_RED_SANDSTONE_STAIRS = 180,
E_BLOCK_DOUBLE_NEW_STONE_SLAB= 181,
@@ -442,7 +445,7 @@ enum
////////////////////////////////////////////////////////////////////////////////
// Block metas:
- // E_BLOCK_BIG_FLOWER metas
+ // E_BLOCK_BIG_FLOWER metas:
E_META_BIG_FLOWER_SUNFLOWER = 0,
E_META_BIG_FLOWER_LILAC = 1,
E_META_BIG_FLOWER_DOUBLE_TALL_GRASS = 2,
@@ -477,6 +480,7 @@ enum
// E_BLOCK_DIRT metas:
E_META_DIRT_NORMAL = 0,
E_META_DIRT_GRASSLESS = 1,
+ E_META_DIRT_COARSE = 1,
E_META_DIRT_PODZOL = 2,
// E_BLOCK_DISPENSER / E_BLOCK_DROPPER metas:
@@ -497,7 +501,7 @@ enum
E_META_DOUBLE_STONE_SLAB_NETHER_BRICK = 6,
E_META_DOUBLE_STONE_SLAB_STONE_SECRET = 7,
- // E_BLOCK_FLOWER metas
+ // E_BLOCK_FLOWER metas:
E_META_FLOWER_POPPY = 0,
E_META_FLOWER_BLUE_ORCHID = 1,
E_META_FLOWER_ALLIUM = 2,
@@ -507,7 +511,7 @@ enum
E_META_FLOWER_PINK_TULIP = 7,
E_META_FLOWER_OXEYE_DAISY = 8,
- // E_BLOCK_JUKEBOX metas
+ // E_BLOCK_JUKEBOX metas:
E_META_JUKEBOX_OFF = 0,
E_META_JUKEBOX_ON = 1,
@@ -531,30 +535,37 @@ enum
E_META_LOG_BIRCH = 2,
E_META_LOG_JUNGLE = 3,
- // E_BLOCK_NEW_LEAVES metas
+ // E_BLOCK_NEW_LEAVES metas:
E_META_NEW_LEAVES_ACACIA_WOOD = 0,
E_META_NEW_LEAVES_DARK_OAK_WOOD = 1,
- // E_BLOCK_NEW_LOG metas
+ // E_BLOCK_NEW_LOG metas:
E_META_NEW_LOG_ACACIA_WOOD = 0,
E_META_NEW_LOG_DARK_OAK_WOOD = 1,
// E_BLOCK_PLANKS metas:
- E_META_PLANKS_APPLE = 0,
- E_META_PLANKS_CONIFER = 1,
- E_META_PLANKS_BIRCH = 2,
- E_META_PLANKS_JUNGLE = 3,
+ E_META_PLANKS_APPLE = 0,
+ E_META_PLANKS_CONIFER = 1,
+ E_META_PLANKS_BIRCH = 2,
+ E_META_PLANKS_JUNGLE = 3,
+ E_META_PLANKS_ACACIA = 4,
+ E_META_PLANKS_DARK_OAK = 5,
// E_BLOCK_(XXX_WEIGHTED)_PRESSURE_PLATE metas:
E_META_PRESSURE_PLATE_RAISED = 0,
E_META_PRESSURE_PLATE_DEPRESSED = 1,
+ // E_BLOCK_PRISMARINE_BLOCK metas:
+ E_META_PRISMARINE_BLOCK_ROUGH = 0,
+ E_META_PRISMARINE_BLOCK_BRICKS = 1,
+ E_META_PRISMARINE_BLOCK_DARK = 2,
+
// E_BLOCK_QUARTZ_BLOCK metas:
E_META_QUARTZ_NORMAL = 0,
E_META_QUARTZ_CHISELLED = 1,
E_META_QUARTZ_PILLAR = 2,
- // E_BLOCK_RAIL metas
+ // E_BLOCK_RAIL metas:
E_META_RAIL_ZM_ZP = 0,
E_META_RAIL_XM_XP = 1,
E_META_RAIL_ASCEND_XP = 2,
@@ -566,6 +577,11 @@ enum
E_META_RAIL_CURVED_ZM_XM = 8,
E_META_RAIL_CURVED_ZM_XP = 9,
+ // E_BLOCK_RED_SANDSTONE metas:
+ E_META_RED_SANDSTONE_NORMAL = 0,
+ E_META_RED_SANDSTONE_ORNAMENT = 1,
+ E_META_RED_SANDSTONE_SMOOTH = 2,
+
// E_BLOCK_SAND metas:
E_META_SAND_NORMAL = 0,
E_META_SAND_RED = 1,
@@ -598,7 +614,7 @@ enum
E_META_SNOW_LAYER_SEVEN = 6,
E_META_SNOW_LAYER_EIGHT = 7,
- // E_BLOCK_STAINED_CLAY metas
+ // E_BLOCK_STAINED_CLAY metas:
E_META_STAINED_CLAY_WHITE = 0,
E_META_STAINED_CLAY_ORANGE = 1,
E_META_STAINED_CLAY_MAGENTA = 2,
@@ -616,7 +632,7 @@ enum
E_META_STAINED_CLAY_RED = 14,
E_META_STAINED_CLAY_BLACK = 15,
- // E_BLOCK_STAINED_GLASS metas
+ // E_BLOCK_STAINED_GLASS metas:
E_META_STAINED_GLASS_WHITE = 0,
E_META_STAINED_GLASS_ORANGE = 1,
E_META_STAINED_GLASS_MAGENTA = 2,
@@ -634,7 +650,7 @@ enum
E_META_STAINED_GLASS_RED = 14,
E_META_STAINED_GLASS_BLACK = 15,
- // E_BLOCK_STAINED_GLASS_PANE metas
+ // E_BLOCK_STAINED_GLASS_PANE metas:
E_META_STAINED_GLASS_PANE_WHITE = 0,
E_META_STAINED_GLASS_PANE_ORANGE = 1,
E_META_STAINED_GLASS_PANE_MAGENTA = 2,
@@ -655,8 +671,11 @@ enum
// E_BLOCK_STONE metas:
E_META_STONE = 0,
E_META_STONE_GRANITE = 1,
+ E_META_STONE_POLISHED_GRANITE = 2,
E_META_STONE_DIORITE = 3,
+ E_META_STONE_POLISHED_DIORITE = 4,
E_META_STONE_ANDESITE = 5,
+ E_META_STONE_POLISHED_ANDESITE = 6,
// E_BLOCK_STONE_SLAB metas:
E_META_STONE_SLAB_STONE = 0,
@@ -727,6 +746,24 @@ enum
////////////////////////////////////////////////////////////////////////////////
// Item metas:
+ // E_ITEM_BANNER metas:
+ E_META_BANNER_BLACK = 0,
+ E_META_BANNER_RED = 1,
+ E_META_BANNER_GREEN = 2,
+ E_META_BANNER_BROWN = 3,
+ E_META_BANNER_BLUE = 4,
+ E_META_BANNER_PURPLE = 5,
+ E_META_BANNER_CYAN = 6,
+ E_META_BANNER_LIGHTGRAY = 7,
+ E_META_BANNER_GRAY = 8,
+ E_META_BANNER_PINK = 9,
+ E_META_BANNER_LIGHTGREEN = 10,
+ E_META_BANNER_YELLOW = 11,
+ E_META_BANNER_LIGHTBLUE = 12,
+ E_META_BANNER_MAGENTA = 13,
+ E_META_BANNER_ORANGE = 14,
+ E_META_BANNER_WHITE = 15,
+
// E_ITEM_COAL metas:
E_META_COAL_NORMAL = 0,
E_META_COAL_CHARCOAL = 1,
@@ -753,6 +790,13 @@ enum
E_META_GOLDEN_APPLE_NORMAL = 0,
E_META_GOLDEN_APPLE_ENCHANTED = 1,
+ // E_ITEM_HEAD metas:
+ E_META_HEAD_SKELETON = 0,
+ E_META_HEAD_WITHER = 1,
+ E_META_HEAD_ZOMBIE = 2,
+ E_META_HEAD_PLAYER = 3,
+ E_META_HEAD_CREEPER = 4,
+
// E_ITEM_RAW_FISH metas:
E_META_RAW_FISH_FISH = 0,
E_META_RAW_FISH_SALMON = 1,
@@ -762,8 +806,6 @@ enum
// E_ITEM_COOKED_FISH metas:
E_META_COOKED_FISH_FISH = 0,
E_META_COOKED_FISH_SALMON = 1,
- E_META_COOKED_FISH_CLOWNFISH = 2,
- E_META_COOKED_FISH_PUFFERFISH = 3,
// E_ITEM_MINECART_TRACKS metas:
E_META_TRACKS_X = 1,
diff --git a/src/BlockInfo.cpp b/src/BlockInfo.cpp
index 05a1e13f8..bdd3a9c26 100644
--- a/src/BlockInfo.cpp
+++ b/src/BlockInfo.cpp
@@ -99,6 +99,7 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info)
a_Info[E_BLOCK_HOPPER ].m_SpreadLightFalloff = 1;
a_Info[E_BLOCK_ICE ].m_SpreadLightFalloff = 1;
a_Info[E_BLOCK_INACTIVE_COMPARATOR ].m_SpreadLightFalloff = 1;
+ a_Info[E_BLOCK_INVERTED_DAYLIGHT_SENSOR ].m_SpreadLightFalloff = 1;
a_Info[E_BLOCK_IRON_BARS ].m_SpreadLightFalloff = 1;
a_Info[E_BLOCK_IRON_DOOR ].m_SpreadLightFalloff = 1;
a_Info[E_BLOCK_IRON_TRAPDOOR ].m_SpreadLightFalloff = 1;
@@ -137,6 +138,7 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info)
a_Info[E_BLOCK_SPRUCE_FENCE_GATE ].m_SpreadLightFalloff = 1;
a_Info[E_BLOCK_STAINED_GLASS ].m_SpreadLightFalloff = 1;
a_Info[E_BLOCK_STAINED_GLASS_PANE ].m_SpreadLightFalloff = 1;
+ a_Info[E_BLOCK_STANDING_BANNER ].m_SpreadLightFalloff = 1;
a_Info[E_BLOCK_STICKY_PISTON ].m_SpreadLightFalloff = 1;
a_Info[E_BLOCK_STONE_BUTTON ].m_SpreadLightFalloff = 1;
a_Info[E_BLOCK_STONE_PRESSURE_PLATE].m_SpreadLightFalloff = 1;
@@ -149,6 +151,7 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info)
a_Info[E_BLOCK_TRIPWIRE ].m_SpreadLightFalloff = 1;
a_Info[E_BLOCK_TRIPWIRE_HOOK ].m_SpreadLightFalloff = 1;
a_Info[E_BLOCK_VINES ].m_SpreadLightFalloff = 1;
+ a_Info[E_BLOCK_WALL_BANNER ].m_SpreadLightFalloff = 1;
a_Info[E_BLOCK_WALLSIGN ].m_SpreadLightFalloff = 1;
a_Info[E_BLOCK_WOODEN_BUTTON ].m_SpreadLightFalloff = 1;
a_Info[E_BLOCK_WOODEN_DOOR ].m_SpreadLightFalloff = 1;
@@ -256,6 +259,7 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info)
a_Info[E_BLOCK_STAINED_GLASS_PANE ].m_Transparent = true;
a_Info[E_BLOCK_STATIONARY_LAVA ].m_Transparent = true;
a_Info[E_BLOCK_STATIONARY_WATER ].m_Transparent = true;
+ a_Info[E_BLOCK_STANDING_BANNER ].m_Transparent = true;
a_Info[E_BLOCK_STICKY_PISTON ].m_Transparent = true;
a_Info[E_BLOCK_STONE_BUTTON ].m_Transparent = true;
a_Info[E_BLOCK_STONE_PRESSURE_PLATE].m_Transparent = true;
@@ -268,6 +272,7 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info)
a_Info[E_BLOCK_TRIPWIRE ].m_Transparent = true;
a_Info[E_BLOCK_TRIPWIRE_HOOK ].m_Transparent = true;
a_Info[E_BLOCK_VINES ].m_Transparent = true;
+ a_Info[E_BLOCK_WALL_BANNER ].m_Transparent = true;
a_Info[E_BLOCK_WALLSIGN ].m_Transparent = true;
a_Info[E_BLOCK_WATER ].m_Transparent = true;
a_Info[E_BLOCK_WOODEN_BUTTON ].m_Transparent = true;
@@ -329,6 +334,7 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info)
a_Info[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE].m_PistonBreakable = true;
a_Info[E_BLOCK_INACTIVE_COMPARATOR ].m_PistonBreakable = true;
a_Info[E_BLOCK_IRON_DOOR ].m_PistonBreakable = true;
+ a_Info[E_BLOCK_IRON_TRAPDOOR ].m_PistonBreakable = true;
a_Info[E_BLOCK_JACK_O_LANTERN ].m_PistonBreakable = true;
a_Info[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE].m_PistonBreakable = true;
a_Info[E_BLOCK_LILY_PAD ].m_PistonBreakable = true;
@@ -474,12 +480,14 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info)
a_Info[E_BLOCK_SNOW ].m_IsSolid = false;
a_Info[E_BLOCK_STATIONARY_LAVA ].m_IsSolid = false;
a_Info[E_BLOCK_STATIONARY_WATER ].m_IsSolid = false;
+ a_Info[E_BLOCK_STANDING_BANNER ].m_IsSolid = false;
a_Info[E_BLOCK_STONE_BUTTON ].m_IsSolid = false;
a_Info[E_BLOCK_STONE_PRESSURE_PLATE].m_IsSolid = false;
a_Info[E_BLOCK_TALL_GRASS ].m_IsSolid = false;
a_Info[E_BLOCK_TORCH ].m_IsSolid = false;
a_Info[E_BLOCK_TRIPWIRE ].m_IsSolid = false;
a_Info[E_BLOCK_VINES ].m_IsSolid = false;
+ a_Info[E_BLOCK_WALL_BANNER ].m_IsSolid = false;
a_Info[E_BLOCK_WALLSIGN ].m_IsSolid = false;
a_Info[E_BLOCK_WATER ].m_IsSolid = false;
a_Info[E_BLOCK_WOODEN_BUTTON ].m_IsSolid = false;
@@ -735,7 +743,7 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info)
a_Info[E_BLOCK_NEW_LOG ].m_PlaceSound = "dig.wood";
a_Info[E_BLOCK_ACACIA_WOOD_STAIRS ].m_PlaceSound = "dig.wood";
a_Info[E_BLOCK_DARK_OAK_WOOD_STAIRS ].m_PlaceSound = "dig.wood";
- a_Info[E_BLOCK_SLIME_BLOCK ].m_PlaceSound = "dig.slime"; // TODO: Check that this is the correct name
+ a_Info[E_BLOCK_SLIME_BLOCK ].m_PlaceSound = "mob.slime.big";
a_Info[E_BLOCK_BARRIER ].m_PlaceSound = "dig.stone";
a_Info[E_BLOCK_IRON_TRAPDOOR ].m_PlaceSound = "dig.metal";
a_Info[E_BLOCK_PRISMARINE_BLOCK ].m_PlaceSound = "dig.stone";
@@ -746,6 +754,8 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info)
a_Info[E_BLOCK_BLOCK_OF_COAL ].m_PlaceSound = "dig.stone";
a_Info[E_BLOCK_PACKED_ICE ].m_PlaceSound = "dig.stone";
a_Info[E_BLOCK_BIG_FLOWER ].m_PlaceSound = "dig.grass";
+ a_Info[E_BLOCK_STANDING_BANNER ].m_PlaceSound = "dig.wood";
+ a_Info[E_BLOCK_WALL_BANNER ].m_PlaceSound = "dig.wood";
a_Info[E_BLOCK_RED_SANDSTONE ].m_PlaceSound = "dig.stone";
a_Info[E_BLOCK_RED_SANDSTONE_STAIRS ].m_PlaceSound = "dig.stone";
a_Info[E_BLOCK_NEW_STONE_SLAB ].m_PlaceSound = "dig.stone";
diff --git a/src/Blocks/BlockDirt.h b/src/Blocks/BlockDirt.h
index 60d81db72..19f889372 100644
--- a/src/Blocks/BlockDirt.h
+++ b/src/Blocks/BlockDirt.h
@@ -21,7 +21,15 @@ public:
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{
- a_Pickups.push_back(cItem(E_BLOCK_DIRT, 1, 0));
+ if (a_BlockMeta == E_META_DIRT_COARSE)
+ {
+ // Drop the coarse block (dirt, meta 1)
+ a_Pickups.Add(E_BLOCK_DIRT, 1, E_META_DIRT_COARSE);
+ }
+ else
+ {
+ a_Pickups.Add(E_BLOCK_DIRT, 1, E_META_DIRT_NORMAL);
+ }
}
diff --git a/src/Blocks/BlockDoor.h b/src/Blocks/BlockDoor.h
index 0ff8bcfc8..92ad8da12 100644
--- a/src/Blocks/BlockDoor.h
+++ b/src/Blocks/BlockDoor.h
@@ -173,7 +173,23 @@ public:
/** Returns true if the specified blocktype is any kind of door */
inline static bool IsDoor(BLOCKTYPE a_Block)
{
- return (a_Block == E_BLOCK_WOODEN_DOOR) || (a_Block == E_BLOCK_IRON_DOOR);
+ switch (a_Block)
+ {
+ case E_BLOCK_ACACIA_DOOR:
+ case E_BLOCK_BIRCH_DOOR:
+ case E_BLOCK_DARK_OAK_DOOR:
+ case E_BLOCK_IRON_DOOR:
+ case E_BLOCK_JUNGLE_DOOR:
+ case E_BLOCK_SPRUCE_DOOR:
+ case E_BLOCK_WOODEN_DOOR:
+ {
+ return true;
+ }
+ default:
+ {
+ return false;
+ }
+ }
}
diff --git a/src/Blocks/BlockPiston.h b/src/Blocks/BlockPiston.h
index 3a3fc7224..f868f4d8e 100644
--- a/src/Blocks/BlockPiston.h
+++ b/src/Blocks/BlockPiston.h
@@ -100,6 +100,7 @@ private:
case E_BLOCK_BREWING_STAND:
case E_BLOCK_CHEST:
case E_BLOCK_COMMAND_BLOCK:
+ case E_BLOCK_DAYLIGHT_SENSOR:
case E_BLOCK_DISPENSER:
case E_BLOCK_DROPPER:
case E_BLOCK_ENCHANTMENT_TABLE:
@@ -108,6 +109,7 @@ private:
// Notice the lack of an E_BLOCK_ENDER_CHEST here; its because ender chests can totally be pushed/pulled in MCS :)
case E_BLOCK_FURNACE:
case E_BLOCK_LIT_FURNACE:
+ case E_BLOCK_INVERTED_DAYLIGHT_SENSOR:
case E_BLOCK_HOPPER:
case E_BLOCK_JUKEBOX:
case E_BLOCK_MOB_SPAWNER:
@@ -115,7 +117,9 @@ private:
case E_BLOCK_NOTE_BLOCK:
case E_BLOCK_OBSIDIAN:
case E_BLOCK_PISTON_EXTENSION:
+ case E_BLOCK_STANDING_BANNER:
case E_BLOCK_TRAPPED_CHEST:
+ case E_BLOCK_WALL_BANNER:
{
return false;
}
diff --git a/src/Blocks/BlockTrapdoor.h b/src/Blocks/BlockTrapdoor.h
index 41256ae55..8c96de0f1 100644
--- a/src/Blocks/BlockTrapdoor.h
+++ b/src/Blocks/BlockTrapdoor.h
@@ -29,6 +29,12 @@ public:
virtual void OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override
{
+ if (m_BlockType == E_BLOCK_IRON_TRAPDOOR)
+ {
+ // Iron doors can only be toggled by redstone, not by right-clicking
+ return;
+ }
+
// Flip the ON bit on/off using the XOR bitwise operation
NIBBLETYPE Meta = (a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x04);
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta);
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index 1f22762c0..20592c190 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -145,7 +145,9 @@ public:
void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player);
void SendDestroyEntity (const cEntity & a_Entity);
void SendDisconnect (const AString & a_Reason);
+ void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display);
void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ);
+ void SendEntityAnimation (const cEntity & a_Entity, char a_Animation); // tolua_export
void SendEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration);
void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item);
void SendEntityHeadLook (const cEntity & a_Entity);
@@ -156,6 +158,8 @@ public:
void SendEntityRelMoveLook (const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ);
void SendEntityStatus (const cEntity & a_Entity, char a_Status);
void SendEntityVelocity (const cEntity & a_Entity);
+ void SendExperience (void);
+ void SendExperienceOrb (const cExpOrb & a_ExpOrb);
void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion);
void SendGameMode (eGameMode a_GameMode);
void SendHealth (void);
@@ -164,15 +168,14 @@ public:
void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators, unsigned int m_Scale);
void SendMapInfo (int a_ID, unsigned int a_Scale);
void SendPaintingSpawn (const cPainting & a_Painting);
- void SendPickupSpawn (const cPickup & a_Pickup);
- void SendEntityAnimation (const cEntity & a_Entity, char a_Animation); // tolua_export
void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount);
+ void SendPickupSpawn (const cPickup & a_Pickup);
void SendPlayerAbilities (void);
void SendPlayerListAddPlayer (const cPlayer & a_Player);
void SendPlayerListRemovePlayer (const cPlayer & a_Player);
+ void SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName);
void SendPlayerListUpdateGameMode (const cPlayer & a_Player);
void SendPlayerListUpdatePing (const cPlayer & a_Player);
- void SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName);
void SendPlayerMaxSpeed (void); ///< Informs the client of the maximum player speed (1.6.1+)
void SendPlayerMoveLook (void);
void SendPlayerPosition (void);
@@ -180,11 +183,8 @@ public:
void SendPluginMessage (const AString & a_Channel, const AString & a_Message); // Exported in ManualBindings.cpp
void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID);
void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks = false);
- void SendExperience (void);
- void SendExperienceOrb (const cExpOrb & a_ExpOrb);
- void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode);
void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode);
- void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display);
+ void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode);
void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch); // tolua_export
void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data);
void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock);
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index ffd0b7e03..22d6a2ae2 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -182,11 +182,11 @@ public:
/** Sets the player team, NULL if none */
void SetTeam(cTeam * a_Team);
+ // tolua_end
+
/** Forces the player to query the scoreboard for his team */
cTeam * UpdateTeam(void);
- // tolua_end
-
/** Return the associated statistic and achievement manager. */
cStatManager & GetStatManager() { return m_Stats; }
@@ -421,9 +421,13 @@ public:
/** Sets the player's bed (home) position */
void SetBedPos(const Vector3i & a_Pos) { m_LastBedPos = a_Pos; }
+
+ // tolua_end
/** Update movement-related statistics. */
void UpdateMovementStats(const Vector3d & a_DeltaPos);
+
+ // tolua_begin
/** Returns wheter the player can fly or not. */
virtual bool CanFly(void) const { return m_CanFly; }
diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp
index caa623abc..912dde022 100644
--- a/src/Items/ItemHandler.cpp
+++ b/src/Items/ItemHandler.cpp
@@ -574,6 +574,7 @@ bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockType)
case E_BLOCK_COBBLESTONE_WALL:
case E_BLOCK_DIAMOND_BLOCK:
case E_BLOCK_DIAMOND_ORE:
+ case E_BLOCK_DOUBLE_NEW_STONE_SLAB:
case E_BLOCK_DOUBLE_STONE_SLAB:
case E_BLOCK_EMERALD_ORE:
case E_BLOCK_ENCHANTMENT_TABLE:
@@ -587,6 +588,7 @@ bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockType)
case E_BLOCK_LAPIS_BLOCK:
case E_BLOCK_LAPIS_ORE:
case E_BLOCK_LIT_FURNACE:
+ case E_BLOCK_MOB_SPAWNER:
case E_BLOCK_MOSSY_COBBLESTONE:
case E_BLOCK_NETHER_BRICK:
case E_BLOCK_NETHER_BRICK_STAIRS:
@@ -594,6 +596,7 @@ bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockType)
case E_BLOCK_NETHERRACK:
case E_BLOCK_NEW_STONE_SLAB:
case E_BLOCK_OBSIDIAN:
+ case E_BLOCK_PACKED_ICE:
case E_BLOCK_PRISMARINE_BLOCK:
case E_BLOCK_RED_SANDSTONE:
case E_BLOCK_RED_SANDSTONE_STAIRS:
@@ -608,8 +611,6 @@ bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockType)
case E_BLOCK_STONE_PRESSURE_PLATE:
case E_BLOCK_STONE_SLAB:
case E_BLOCK_VINES:
- case E_BLOCK_PACKED_ICE:
- case E_BLOCK_MOB_SPAWNER:
{
return false;
}
diff --git a/src/Items/ItemHoe.h b/src/Items/ItemHoe.h
index de8b9a061..ae3723323 100644
--- a/src/Items/ItemHoe.h
+++ b/src/Items/ItemHoe.h
@@ -24,12 +24,35 @@ public:
{
return false;
}
- BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
BLOCKTYPE UpperBlock = a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ);
+ BLOCKTYPE Block;
+ NIBBLETYPE BlockMeta;
+ a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, Block, BlockMeta);
+
if (((Block == E_BLOCK_DIRT) || (Block == E_BLOCK_GRASS)) && (UpperBlock == E_BLOCK_AIR))
{
- a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FARMLAND, 0);
+ BLOCKTYPE NewBlock = E_BLOCK_FARMLAND;
+ if (Block == E_BLOCK_DIRT)
+ {
+ switch (BlockMeta)
+ {
+ case E_META_DIRT_COARSE:
+ {
+ // Transform to normal dirt
+ NewBlock = E_BLOCK_DIRT;
+ break;
+ }
+ case E_META_DIRT_PODZOL:
+ {
+ // You can't transform this block with a hoe in vanilla
+ return false;
+ }
+ default: break;
+ }
+ }
+
+ a_World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, NewBlock, 0);
a_World->BroadcastSoundEffect("dig.gravel", a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 1.0f, 0.8f);
a_Player->UseEquippedItem();
return true;
diff --git a/src/Items/ItemPickaxe.h b/src/Items/ItemPickaxe.h
index e0cf5d711..b5dc179f8 100644
--- a/src/Items/ItemPickaxe.h
+++ b/src/Items/ItemPickaxe.h
@@ -41,11 +41,11 @@ public:
case E_BLOCK_DIAMOND_BLOCK:
case E_BLOCK_DIAMOND_ORE:
+ case E_BLOCK_EMERALD_ORE:
case E_BLOCK_GOLD_BLOCK:
case E_BLOCK_GOLD_ORE:
case E_BLOCK_REDSTONE_ORE:
case E_BLOCK_REDSTONE_ORE_GLOWING:
- case E_BLOCK_EMERALD_ORE:
{
return PickaxeLevel() >= 3;
}
@@ -59,29 +59,34 @@ public:
}
case E_BLOCK_ANVIL:
- case E_BLOCK_ENCHANTMENT_TABLE:
- case E_BLOCK_FURNACE:
- case E_BLOCK_LIT_FURNACE:
+ case E_BLOCK_BRICK:
+ case E_BLOCK_CAULDRON:
case E_BLOCK_COAL_ORE:
- case E_BLOCK_STONE:
case E_BLOCK_COBBLESTONE:
+ case E_BLOCK_COBBLESTONE_STAIRS:
+ case E_BLOCK_COBBLESTONE_WALL:
+ case E_BLOCK_DOUBLE_NEW_STONE_SLAB:
+ case E_BLOCK_DOUBLE_STONE_SLAB:
+ case E_BLOCK_ENCHANTMENT_TABLE:
case E_BLOCK_END_STONE:
+ case E_BLOCK_FURNACE:
+ case E_BLOCK_LIT_FURNACE:
+ case E_BLOCK_MOB_SPAWNER:
case E_BLOCK_MOSSY_COBBLESTONE:
- case E_BLOCK_SANDSTONE_STAIRS:
- case E_BLOCK_SANDSTONE:
- case E_BLOCK_STONE_BRICKS:
case E_BLOCK_NETHER_BRICK:
+ case E_BLOCK_NETHER_BRICK_STAIRS:
case E_BLOCK_NETHERRACK:
- case E_BLOCK_STONE_SLAB:
- case E_BLOCK_DOUBLE_STONE_SLAB:
- case E_BLOCK_STONE_PRESSURE_PLATE:
- case E_BLOCK_BRICK:
- case E_BLOCK_COBBLESTONE_STAIRS:
- case E_BLOCK_COBBLESTONE_WALL:
+ case E_BLOCK_NEW_STONE_SLAB:
+ case E_BLOCK_PRISMARINE_BLOCK:
+ case E_BLOCK_RED_SANDSTONE:
+ case E_BLOCK_RED_SANDSTONE_STAIRS:
+ case E_BLOCK_SANDSTONE:
+ case E_BLOCK_SANDSTONE_STAIRS:
+ case E_BLOCK_STONE:
+ case E_BLOCK_STONE_BRICKS:
case E_BLOCK_STONE_BRICK_STAIRS:
- case E_BLOCK_NETHER_BRICK_STAIRS:
- case E_BLOCK_CAULDRON:
- case E_BLOCK_MOB_SPAWNER:
+ case E_BLOCK_STONE_PRESSURE_PLATE:
+ case E_BLOCK_STONE_SLAB:
{
return PickaxeLevel() >= 1;
}
diff --git a/src/Root.h b/src/Root.h
index 9a482556c..84c6a98ec 100644
--- a/src/Root.h
+++ b/src/Root.h
@@ -41,11 +41,12 @@ namespace Json
class cRoot
{
public:
- static bool m_TerminateEventRaised;
-
static cRoot * Get() { return s_Root; }
// tolua_end
+ static bool m_TerminateEventRaised;
+
+
cRoot(void);
~cRoot();
@@ -129,15 +130,15 @@ public:
/// Sends a chat message to all connected clients (in all worlds)
void BroadcastChat (const AString & a_Message, eMessageType a_ChatPrefix = mtCustom);
- void BroadcastChatInfo (const AString & a_Message) { BroadcastChat(a_Message, mtInformation); }
+ void BroadcastChat (const cCompositeChat & a_Message);
+ void BroadcastChatDeath (const AString & a_Message) { BroadcastChat(a_Message, mtDeath); }
void BroadcastChatFailure(const AString & a_Message) { BroadcastChat(a_Message, mtFailure); }
- void BroadcastChatSuccess(const AString & a_Message) { BroadcastChat(a_Message, mtSuccess); }
- void BroadcastChatWarning(const AString & a_Message) { BroadcastChat(a_Message, mtWarning); }
void BroadcastChatFatal (const AString & a_Message) { BroadcastChat(a_Message, mtFailure); }
+ void BroadcastChatInfo (const AString & a_Message) { BroadcastChat(a_Message, mtInformation); }
void BroadcastChatJoin (const AString & a_Message) { BroadcastChat(a_Message, mtJoin); }
void BroadcastChatLeave (const AString & a_Message) { BroadcastChat(a_Message, mtLeave); }
- void BroadcastChatDeath (const AString & a_Message) { BroadcastChat(a_Message, mtDeath); }
- void BroadcastChat (const cCompositeChat & a_Message);
+ void BroadcastChatSuccess(const AString & a_Message) { BroadcastChat(a_Message, mtSuccess); }
+ void BroadcastChatWarning(const AString & a_Message) { BroadcastChat(a_Message, mtWarning); }
/// Returns the textual description of the protocol version: 49 -> "1.4.4". Provided specifically for Lua API
static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum);
diff --git a/src/Simulator/SandSimulator.cpp b/src/Simulator/SandSimulator.cpp
index e8887ce59..aad41e463 100644
--- a/src/Simulator/SandSimulator.cpp
+++ b/src/Simulator/SandSimulator.cpp
@@ -160,6 +160,7 @@ bool cSandSimulator::CanContinueFallThrough(BLOCKTYPE a_BlockType)
case E_BLOCK_FIRE:
case E_BLOCK_FLOWER_POT:
case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE:
+ case E_BLOCK_IRON_TRAPDOOR:
case E_BLOCK_LAVA:
case E_BLOCK_LEVER:
case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE:
diff --git a/src/World.cpp b/src/World.cpp
index 6059b841f..a3c804b44 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -2954,7 +2954,7 @@ bool cWorld::IsTrapdoorOpen(int a_BlockX, int a_BlockY, int a_BlockZ)
BLOCKTYPE Block;
NIBBLETYPE Meta;
GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, Block, Meta);
- if (Block != E_BLOCK_TRAPDOOR)
+ if ((Block != E_BLOCK_TRAPDOOR) && (Block != E_BLOCK_IRON_TRAPDOOR))
{
return false;
}
@@ -2971,7 +2971,7 @@ bool cWorld::SetTrapdoorOpen(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_Op
BLOCKTYPE Block;
NIBBLETYPE Meta;
GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, Block, Meta);
- if (Block != E_BLOCK_TRAPDOOR)
+ if ((Block != E_BLOCK_TRAPDOOR) && (Block != E_BLOCK_IRON_TRAPDOOR))
{
return false;
}
diff --git a/src/WorldStorage/CMakeLists.txt b/src/WorldStorage/CMakeLists.txt
index a00ff3b2f..59193db2a 100644
--- a/src/WorldStorage/CMakeLists.txt
+++ b/src/WorldStorage/CMakeLists.txt
@@ -14,7 +14,6 @@ SET (SRCS
ScoreboardSerializer.cpp
StatSerializer.cpp
WSSAnvil.cpp
- WSSCompact.cpp
WorldStorage.cpp)
SET (HDRS
@@ -27,7 +26,6 @@ SET (HDRS
ScoreboardSerializer.h
StatSerializer.h
WSSAnvil.h
- WSSCompact.h
WorldStorage.h)
if(NOT MSVC)
diff --git a/src/WorldStorage/WSSCompact.cpp b/src/WorldStorage/WSSCompact.cpp
deleted file mode 100644
index 6760186b2..000000000
--- a/src/WorldStorage/WSSCompact.cpp
+++ /dev/null
@@ -1,1066 +0,0 @@
-
-// WSSCompact.cpp
-
-// Interfaces to the cWSSCompact class representing the "compact" storage schema (PAK-files)
-
-#include "Globals.h"
-#include "WSSCompact.h"
-#include "../World.h"
-#include "zlib/zlib.h"
-#include "json/json.h"
-#include "../StringCompression.h"
-#include "../BlockEntities/BeaconEntity.h"
-#include "../BlockEntities/ChestEntity.h"
-#include "../BlockEntities/CommandBlockEntity.h"
-#include "../BlockEntities/DispenserEntity.h"
-#include "../BlockEntities/FlowerPotEntity.h"
-#include "../BlockEntities/FurnaceEntity.h"
-#include "../BlockEntities/JukeboxEntity.h"
-#include "../BlockEntities/MobHeadEntity.h"
-#include "../BlockEntities/NoteEntity.h"
-#include "../BlockEntities/SignEntity.h"
-#include "../SetChunkData.h"
-
-
-
-
-
-#pragma pack(push, 1)
-/// The chunk header, as stored in the file:
-struct cWSSCompact::sChunkHeader
-{
- int m_ChunkX;
- int m_ChunkZ;
- int m_CompressedSize;
- int m_UncompressedSize;
-} ;
-#pragma pack(pop)
-
-
-
-
-
-/// The maximum number of PAK files that are cached
-const size_t MAX_PAK_FILES = 16;
-
-/// The maximum number of unsaved chunks before the cPAKFile saves them to disk
-const int MAX_DIRTY_CHUNKS = 16;
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cJsonChunkSerializer:
-
-cJsonChunkSerializer::cJsonChunkSerializer(void) :
- m_HasJsonData(false)
-{
-}
-
-
-
-
-
-void cJsonChunkSerializer::Entity(cEntity * a_Entity)
-{
- // TODO: a_Entity->SaveToJson(m_Root);
-}
-
-
-
-
-
-void cJsonChunkSerializer::BlockEntity(cBlockEntity * a_BlockEntity)
-{
- const char * SaveInto = NULL;
- switch (a_BlockEntity->GetBlockType())
- {
- case E_BLOCK_BEACON: SaveInto = "Beacons"; break;
- case E_BLOCK_CHEST: SaveInto = "Chests"; break;
- case E_BLOCK_DISPENSER: SaveInto = "Dispensers"; break;
- case E_BLOCK_DROPPER: SaveInto = "Droppers"; break;
- case E_BLOCK_FLOWER_POT: SaveInto = "FlowerPots"; break;
- case E_BLOCK_FURNACE: SaveInto = "Furnaces"; break;
- case E_BLOCK_SIGN_POST: SaveInto = "Signs"; break;
- case E_BLOCK_WALLSIGN: SaveInto = "Signs"; break;
- case E_BLOCK_NOTE_BLOCK: SaveInto = "Notes"; break;
- case E_BLOCK_JUKEBOX: SaveInto = "Jukeboxes"; break;
- case E_BLOCK_COMMAND_BLOCK: SaveInto = "CommandBlocks"; break;
- case E_BLOCK_HEAD: SaveInto = "MobHeads"; break;
-
- default:
- {
- ASSERT(!"Unhandled blocktype in BlockEntities list while saving to JSON");
- break;
- }
- } // switch (BlockEntity->GetBlockType())
- if (SaveInto == NULL)
- {
- return;
- }
-
- Json::Value val;
- a_BlockEntity->SaveToJson(val);
- m_Root[SaveInto].append(val);
- m_HasJsonData = true;
-}
-
-
-
-
-
-void cJsonChunkSerializer::LightIsValid(bool a_IsLightValid)
-{
- if (a_IsLightValid)
- {
- m_Root["IsLightValid"] = true;
- m_HasJsonData = true;
- }
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cWSSCompact:
-
-cWSSCompact::~cWSSCompact()
-{
- for (cPAKFiles::iterator itr = m_PAKFiles.begin(); itr != m_PAKFiles.end(); ++itr)
- {
- delete *itr;
- }
-}
-
-
-
-
-
-bool cWSSCompact::LoadChunk(const cChunkCoords & a_Chunk)
-{
- AString ChunkData;
- int UncompressedSize = 0;
- if (!GetChunkData(a_Chunk, UncompressedSize, ChunkData))
- {
- // The reason for failure is already printed in GetChunkData()
- return false;
- }
-
- return LoadChunkFromData(a_Chunk, UncompressedSize, ChunkData, m_World);
-}
-
-
-
-
-
-bool cWSSCompact::SaveChunk(const cChunkCoords & a_Chunk)
-{
- cCSLock Lock(m_CS);
-
- cPAKFile * f = LoadPAKFile(a_Chunk);
- if (f == NULL)
- {
- // For some reason we couldn't locate the file
- LOG("Cannot locate a proper PAK file for chunk [%d, %d]", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
- return false;
- }
- return f->SaveChunk(a_Chunk, m_World);
-}
-
-
-
-
-
-cWSSCompact::cPAKFile * cWSSCompact::LoadPAKFile(const cChunkCoords & a_Chunk)
-{
- // ASSUMES that m_CS has been locked
-
- // We need to retain this weird conversion code, because some edge chunks are in the wrong PAK file
- const int LayerX = FAST_FLOOR_DIV(a_Chunk.m_ChunkX, 32);
- const int LayerZ = FAST_FLOOR_DIV(a_Chunk.m_ChunkZ, 32);
-
- // Is it already cached?
- for (cPAKFiles::iterator itr = m_PAKFiles.begin(); itr != m_PAKFiles.end(); ++itr)
- {
- if (((*itr) != NULL) && ((*itr)->GetLayerX() == LayerX) && ((*itr)->GetLayerZ() == LayerZ))
- {
- // Move the file to front and return it:
- cPAKFile * f = *itr;
- if (itr != m_PAKFiles.begin())
- {
- m_PAKFiles.erase(itr);
- m_PAKFiles.push_front(f);
- }
- return f;
- }
- }
-
- // Load it anew:
- AString FileName;
- Printf(FileName, "%s/X%i_Z%i.pak", m_World->GetName().c_str(), LayerX, LayerZ);
- cPAKFile * f = new cPAKFile(FileName, LayerX, LayerZ, m_CompressionFactor);
- if (f == NULL)
- {
- return NULL;
- }
- m_PAKFiles.push_front(f);
-
- // If there are too many PAK files cached, delete the last one used:
- if (m_PAKFiles.size() > MAX_PAK_FILES)
- {
- delete m_PAKFiles.back();
- m_PAKFiles.pop_back();
- }
- return f;
-}
-
-
-
-
-
-bool cWSSCompact::GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data)
-{
- cCSLock Lock(m_CS);
- cPAKFile * f = LoadPAKFile(a_Chunk);
- if (f == NULL)
- {
- return false;
- }
- return f->GetChunkData(a_Chunk, a_UncompressedSize, a_Data);
-}
-
-
-
-
-
-/*
-// TODO: Rewrite saving to use the same principles as loading
-bool cWSSCompact::SetChunkData(const cChunkCoords & a_Chunk, int a_UncompressedSize, const AString & a_Data)
-{
- cCSLock Lock(m_CS);
- cPAKFile * f = LoadPAKFile(a_Chunk);
- if (f == NULL)
- {
- return false;
- }
- return f->SetChunkData(a_Chunk, a_UncompressedSize, a_Data);
-}
-*/
-
-
-
-
-
-bool cWSSCompact::EraseChunkData(const cChunkCoords & a_Chunk)
-{
- cCSLock Lock(m_CS);
- cPAKFile * f = LoadPAKFile(a_Chunk);
- if (f == NULL)
- {
- return false;
- }
- return f->EraseChunkData(a_Chunk);
-}
-
-
-
-
-
-void cWSSCompact::LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities, cWorld * a_World)
-{
- // Load beacon:
- Json::Value AllBeacons = a_Value.get("Beacons", Json::nullValue);
- if (!AllBeacons.empty())
- {
- for (Json::Value::iterator itr = AllBeacons.begin(); itr != AllBeacons.end(); ++itr)
- {
- std::auto_ptr<cBeaconEntity> BeaconEntity(new cBeaconEntity(0, 0, 0, a_World));
- if (!BeaconEntity->LoadFromJson(*itr))
- {
- LOGWARNING("ERROR READING BEACON FROM JSON!");
- }
- else
- {
- a_BlockEntities.push_back(BeaconEntity.release());
- }
- } // for itr - AllBeacons[]
- }
-
- // Load chests:
- Json::Value AllChests = a_Value.get("Chests", Json::nullValue);
- if (!AllChests.empty())
- {
- for (Json::Value::iterator itr = AllChests.begin(); itr != AllChests.end(); ++itr)
- {
- std::auto_ptr<cChestEntity> ChestEntity(new cChestEntity(0, 0, 0, a_World, E_BLOCK_CHEST));
- if (!ChestEntity->LoadFromJson(*itr))
- {
- LOGWARNING("ERROR READING CHEST FROM JSON!");
- }
- else
- {
- a_BlockEntities.push_back(ChestEntity.release());
- }
- } // for itr - AllChests[]
- }
-
- // Load dispensers:
- Json::Value AllDispensers = a_Value.get("Dispensers", Json::nullValue);
- for (Json::Value::iterator itr = AllDispensers.begin(); itr != AllDispensers.end(); ++itr)
- {
- std::auto_ptr<cDispenserEntity> DispenserEntity(new cDispenserEntity(0, 0, 0, a_World));
- if (!DispenserEntity->LoadFromJson(*itr))
- {
- LOGWARNING("ERROR READING DISPENSER FROM JSON!");
- }
- else
- {
- a_BlockEntities.push_back(DispenserEntity.release());
- }
- } // for itr - AllDispensers[]
-
- // Load Flowerpots:
- Json::Value AllFlowerPots = a_Value.get("FlowerPots", Json::nullValue);
- for (Json::Value::iterator itr = AllFlowerPots.begin(); itr != AllFlowerPots.end(); ++itr)
- {
- std::auto_ptr<cFlowerPotEntity> FlowerPotEntity(new cFlowerPotEntity(0, 0, 0, a_World));
- if (!FlowerPotEntity->LoadFromJson(*itr))
- {
- LOGWARNING("ERROR READING FLOWERPOT FROM JSON!");
- }
- else
- {
- a_BlockEntities.push_back(FlowerPotEntity.release());
- }
- } // for itr - AllFlowerPots[]
-
- // Load furnaces:
- Json::Value AllFurnaces = a_Value.get("Furnaces", Json::nullValue);
- for (Json::Value::iterator itr = AllFurnaces.begin(); itr != AllFurnaces.end(); ++itr)
- {
- // TODO: The block type and meta aren't correct, there's no way to get them here
- std::auto_ptr<cFurnaceEntity> FurnaceEntity(new cFurnaceEntity(0, 0, 0, E_BLOCK_FURNACE, 0, a_World));
- if (!FurnaceEntity->LoadFromJson(*itr))
- {
- LOGWARNING("ERROR READING FURNACE FROM JSON!");
- }
- else
- {
- a_BlockEntities.push_back(FurnaceEntity.release());
- }
- } // for itr - AllFurnaces[]
-
- // Load signs:
- Json::Value AllSigns = a_Value.get("Signs", Json::nullValue);
- for (Json::Value::iterator itr = AllSigns.begin(); itr != AllSigns.end(); ++itr)
- {
- std::auto_ptr<cSignEntity> SignEntity(new cSignEntity(E_BLOCK_SIGN_POST, 0, 0, 0, a_World));
- if (!SignEntity->LoadFromJson(*itr))
- {
- LOGWARNING("ERROR READING SIGN FROM JSON!");
- }
- else
- {
- a_BlockEntities.push_back(SignEntity.release());
- }
- } // for itr - AllSigns[]
-
- // Load note blocks:
- Json::Value AllNotes = a_Value.get("Notes", Json::nullValue);
- for (Json::Value::iterator itr = AllNotes.begin(); itr != AllNotes.end(); ++itr)
- {
- std::auto_ptr<cNoteEntity> NoteEntity(new cNoteEntity(0, 0, 0, a_World));
- if (!NoteEntity->LoadFromJson(*itr))
- {
- LOGWARNING("ERROR READING NOTE BLOCK FROM JSON!");
- }
- else
- {
- a_BlockEntities.push_back(NoteEntity.release());
- }
- } // for itr - AllNotes[]
-
- // Load jukeboxes:
- Json::Value AllJukeboxes = a_Value.get("Jukeboxes", Json::nullValue);
- for (Json::Value::iterator itr = AllJukeboxes.begin(); itr != AllJukeboxes.end(); ++itr)
- {
- std::auto_ptr<cJukeboxEntity> JukeboxEntity(new cJukeboxEntity(0, 0, 0, a_World));
- if (!JukeboxEntity->LoadFromJson(*itr))
- {
- LOGWARNING("ERROR READING JUKEBOX FROM JSON!");
- }
- else
- {
- a_BlockEntities.push_back(JukeboxEntity.release());
- }
- } // for itr - AllJukeboxes[]
-
- // Load command blocks:
- Json::Value AllCommandBlocks = a_Value.get("CommandBlocks", Json::nullValue);
- for (Json::Value::iterator itr = AllCommandBlocks.begin(); itr != AllCommandBlocks.end(); ++itr)
- {
- std::auto_ptr<cCommandBlockEntity> CommandBlockEntity(new cCommandBlockEntity(0, 0, 0, a_World));
- if (!CommandBlockEntity->LoadFromJson(*itr))
- {
- LOGWARNING("ERROR READING COMMAND BLOCK FROM JSON!");
- }
- else
- {
- a_BlockEntities.push_back(CommandBlockEntity.release());
- }
- } // for itr - AllCommandBlocks[]
-
- // Load mob heads:
- Json::Value AllMobHeads = a_Value.get("MobHeads", Json::nullValue);
- for (Json::Value::iterator itr = AllMobHeads.begin(); itr != AllMobHeads.end(); ++itr)
- {
- std::auto_ptr<cMobHeadEntity> MobHeadEntity(new cMobHeadEntity(0, 0, 0, a_World));
- if (!MobHeadEntity->LoadFromJson(*itr))
- {
- LOGWARNING("ERROR READING MOB HEAD FROM JSON!");
- }
- else
- {
- a_BlockEntities.push_back(MobHeadEntity.release());
- }
- } // for itr - AllMobHeads[]
-}
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cWSSCompact::cPAKFile
-
-#define READ(Var) \
- if (f.Read(&Var, sizeof(Var)) != sizeof(Var)) \
- { \
- LOGERROR("ERROR READING %s FROM FILE %s (line %d); file offset %d", #Var, m_FileName.c_str(), __LINE__, f.Tell()); \
- return; \
- }
-
-cWSSCompact::cPAKFile::cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ, int a_CompressionFactor) :
- m_FileName(a_FileName),
- m_CompressionFactor(a_CompressionFactor),
- m_LayerX(a_LayerX),
- m_LayerZ(a_LayerZ),
- m_NumDirty(0),
- m_ChunkVersion( CHUNK_VERSION), // Init with latest version
- m_PakVersion( PAK_VERSION)
-{
- cFile f;
- if (!f.Open(m_FileName, cFile::fmRead))
- {
- return;
- }
-
- // Read headers:
- READ(m_PakVersion);
- if (m_PakVersion != 1)
- {
- LOGERROR("File \"%s\" is in an unknown pak format (%d)", m_FileName.c_str(), m_PakVersion);
- return;
- }
-
- READ(m_ChunkVersion);
- switch (m_ChunkVersion)
- {
- case 1:
- {
- m_ChunkSize.Set(16, 128, 16);
- break;
- }
- case 2:
- case 3:
- {
- m_ChunkSize.Set(16, 256, 16);
- break;
- }
- default:
- {
- LOGERROR("File \"%s\" is in an unknown chunk format (%d)", m_FileName.c_str(), m_ChunkVersion);
- return;
- }
- };
-
- short NumChunks = 0;
- READ(NumChunks);
-
- // Read chunk headers:
- for (int i = 0; i < NumChunks; i++)
- {
- sChunkHeader * Header = new sChunkHeader;
-
- // Here we do not use the READ macro, as it does not free the resources
- // allocated with new in case of error.
- if (f.Read(Header, sizeof(*Header)) != sizeof(*Header))
- {
- LOGERROR("ERROR READING %s FROM FILE %s (line %d); file offset %d", "Header", m_FileName.c_str(), __LINE__, f.Tell());
- delete Header;
- Header = NULL;
- return;
- }
- m_ChunkHeaders.push_back(Header);
- } // for i - chunk headers
-
- // Read chunk data:
- if (f.ReadRestOfFile(m_DataContents) == -1)
- {
- LOGERROR("Cannot read file \"%s\" contents", m_FileName.c_str());
- return;
- }
-
- if (m_ChunkVersion == 1) // Convert chunks to version 2
- {
- UpdateChunk1To2();
- }
-#if AXIS_ORDER == AXIS_ORDER_XZY
- if (m_ChunkVersion == 2) // Convert chunks to version 3
- {
- UpdateChunk2To3();
- }
-#endif
-}
-
-
-
-
-
-cWSSCompact::cPAKFile::~cPAKFile()
-{
- if (m_NumDirty > 0)
- {
- SynchronizeFile();
- }
- for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr)
- {
- delete *itr;
- }
-}
-
-
-
-
-
-bool cWSSCompact::cPAKFile::GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data)
-{
- int ChunkX = a_Chunk.m_ChunkX;
- int ChunkZ = a_Chunk.m_ChunkZ;
- sChunkHeader * Header = NULL;
- int Offset = 0;
- for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr)
- {
- if (((*itr)->m_ChunkX == ChunkX) && ((*itr)->m_ChunkZ == ChunkZ))
- {
- Header = *itr;
- break;
- }
- Offset += (*itr)->m_CompressedSize;
- }
- if ((Header == NULL) || (Offset + Header->m_CompressedSize > (int)m_DataContents.size()))
- {
- // Chunk not found / data invalid
- return false;
- }
-
- a_UncompressedSize = Header->m_UncompressedSize;
- a_Data.assign(m_DataContents, Offset, Header->m_CompressedSize);
- return true;
-}
-
-
-
-
-
-bool cWSSCompact::cPAKFile::SaveChunk(const cChunkCoords & a_Chunk, cWorld * a_World)
-{
- if (!SaveChunkToData(a_Chunk, a_World))
- {
- return false;
- }
- if (m_NumDirty > MAX_DIRTY_CHUNKS)
- {
- SynchronizeFile();
- }
- return true;
-}
-
-
-
-
-
-void cWSSCompact::cPAKFile::UpdateChunk1To2()
-{
- int Offset = 0;
- AString NewDataContents;
- int ChunksConverted = 0;
- for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr)
- {
- sChunkHeader * Header = *itr;
-
- if (ChunksConverted % 32 == 0)
- {
- LOGINFO("Updating \"%s\" version 1 to version 2: " SIZE_T_FMT " %%", m_FileName.c_str(), (ChunksConverted * 100) / m_ChunkHeaders.size());
- }
- ChunksConverted++;
-
- AString Data;
- int UncompressedSize = Header->m_UncompressedSize;
- Data.assign(m_DataContents, Offset, Header->m_CompressedSize);
- Offset += Header->m_CompressedSize;
-
- // Crude data integrity check:
- int ExpectedSize = (16*128*16)*2 + (16*128*16)/2; // For version 1
- if (UncompressedSize < ExpectedSize)
- {
- LOGWARNING("Chunk [%d, %d] has too short decompressed data (%d bytes out of %d needed), erasing",
- Header->m_ChunkX, Header->m_ChunkZ,
- UncompressedSize, ExpectedSize
- );
- Offset += Header->m_CompressedSize;
- continue;
- }
-
- // Decompress the data:
- AString UncompressedData;
- {
- int errorcode = UncompressString(Data.data(), Data.size(), UncompressedData, (size_t)UncompressedSize);
- if (errorcode != Z_OK)
- {
- LOGERROR("Error %d decompressing data for chunk [%d, %d]",
- errorcode,
- Header->m_ChunkX, Header->m_ChunkZ
- );
- Offset += Header->m_CompressedSize;
- continue;
- }
- }
-
- if (UncompressedSize != (int)UncompressedData.size())
- {
- LOGWARNING("Uncompressed data size differs (exp %d bytes, got " SIZE_T_FMT ") for chunk [%d, %d]",
- UncompressedSize, UncompressedData.size(),
- Header->m_ChunkX, Header->m_ChunkZ
- );
- Offset += Header->m_CompressedSize;
- continue;
- }
-
-
- // Old version is 128 blocks high with YZX axis order
- char ConvertedData[cChunkDef::BlockDataSize];
- int Index = 0;
- unsigned int InChunkOffset = 0;
- for (int x = 0; x < 16; ++x) for (int z = 0; z < 16; ++z)
- {
- for (int y = 0; y < 128; ++y)
- {
- ConvertedData[Index++] = UncompressedData[y + z * 128 + x * 128 * 16 + InChunkOffset];
- }
- // Add 128 empty blocks after an old y column
- memset(ConvertedData + Index, E_BLOCK_AIR, 128);
- Index += 128;
- }
- InChunkOffset += (16 * 128 * 16);
- for (int x = 0; x < 16; ++x) for (int z = 0; z < 16; ++z) // Metadata
- {
- for (int y = 0; y < 64; ++y)
- {
- ConvertedData[Index++] = UncompressedData[y + z * 64 + x * 64 * 16 + InChunkOffset];
- }
- memset(ConvertedData + Index, 0, 64);
- Index += 64;
- }
- InChunkOffset += (16 * 128 * 16) / 2;
- for (int x = 0; x < 16; ++x) for (int z = 0; z < 16; ++z) // Block light
- {
- for (int y = 0; y < 64; ++y)
- {
- ConvertedData[Index++] = UncompressedData[y + z * 64 + x * 64 * 16 + InChunkOffset];
- }
- memset(ConvertedData + Index, 0, 64);
- Index += 64;
- }
- InChunkOffset += (16*128*16)/2;
- for (int x = 0; x < 16; ++x) for (int z = 0; z < 16; ++z) // Sky light
- {
- for (int y = 0; y < 64; ++y)
- {
- ConvertedData[Index++] = UncompressedData[y + z * 64 + x * 64 * 16 + InChunkOffset];
- }
- memset(ConvertedData + Index, 0, 64);
- Index += 64;
- }
- InChunkOffset += (16 * 128 * 16) / 2;
-
- AString Converted(ConvertedData, ARRAYCOUNT(ConvertedData));
-
- // Add JSON data afterwards
- if (UncompressedData.size() > InChunkOffset)
- {
- Converted.append( UncompressedData.begin() + InChunkOffset, UncompressedData.end());
- }
-
- // Re-compress data
- AString CompressedData;
- {
- int errorcode = CompressString(Converted.data(), Converted.size(), CompressedData, m_CompressionFactor);
- if (errorcode != Z_OK)
- {
- LOGERROR("Error %d compressing data for chunk [%d, %d]",
- errorcode,
- Header->m_ChunkX, Header->m_ChunkZ
- );
- continue;
- }
- }
-
- // Save into file's cache
- Header->m_UncompressedSize = (int)Converted.size();
- Header->m_CompressedSize = (int)CompressedData.size();
- NewDataContents.append(CompressedData);
- }
-
- // Done converting
- m_DataContents = NewDataContents;
- m_ChunkVersion = 2;
- SynchronizeFile();
-
- LOGINFO("Updated \"%s\" version 1 to version 2", m_FileName.c_str());
-}
-
-
-
-
-
-void cWSSCompact::cPAKFile::UpdateChunk2To3()
-{
- int Offset = 0;
- AString NewDataContents;
- int ChunksConverted = 0;
- for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr)
- {
- sChunkHeader * Header = *itr;
-
- if (ChunksConverted % 32 == 0)
- {
- LOGINFO("Updating \"%s\" version 2 to version 3: " SIZE_T_FMT " %%", m_FileName.c_str(), (ChunksConverted * 100) / m_ChunkHeaders.size());
- }
- ChunksConverted++;
-
- AString Data;
- int UncompressedSize = Header->m_UncompressedSize;
- Data.assign(m_DataContents, Offset, Header->m_CompressedSize);
- Offset += Header->m_CompressedSize;
-
- // Crude data integrity check:
- const int ExpectedSize = (16 * 256 * 16) * 2 + (16 * 256 * 16) / 2; // For version 2
- if (UncompressedSize < ExpectedSize)
- {
- LOGWARNING("Chunk [%d, %d] has too short decompressed data (%d bytes out of %d needed), erasing",
- Header->m_ChunkX, Header->m_ChunkZ,
- UncompressedSize, ExpectedSize
- );
- Offset += Header->m_CompressedSize;
- continue;
- }
-
- // Decompress the data:
- AString UncompressedData;
- {
- int errorcode = UncompressString(Data.data(), Data.size(), UncompressedData, (size_t)UncompressedSize);
- if (errorcode != Z_OK)
- {
- LOGERROR("Error %d decompressing data for chunk [%d, %d]",
- errorcode,
- Header->m_ChunkX, Header->m_ChunkZ
- );
- Offset += Header->m_CompressedSize;
- continue;
- }
- }
-
- if (UncompressedSize != (int)UncompressedData.size())
- {
- LOGWARNING("Uncompressed data size differs (exp %d bytes, got " SIZE_T_FMT ") for chunk [%d, %d]",
- UncompressedSize, UncompressedData.size(),
- Header->m_ChunkX, Header->m_ChunkZ
- );
- Offset += Header->m_CompressedSize;
- continue;
- }
-
- char ConvertedData[ExpectedSize];
- memset(ConvertedData, 0, ExpectedSize);
-
- // Cannot use cChunk::MakeIndex because it might change again?????????
- // For compatibility, use what we know is current
- #define MAKE_3_INDEX( x, y, z) ( x + (z * 16) + (y * 16 * 16))
-
- unsigned int InChunkOffset = 0;
- for (int x = 0; x < 16; ++x) for (int z = 0; z < 16; ++z) for (int y = 0; y < 256; ++y) // YZX Loop order is important, in 1.1 Y was first then Z then X
- {
- ConvertedData[ MAKE_3_INDEX(x, y, z) ] = UncompressedData[InChunkOffset];
- ++InChunkOffset;
- } // for y, z, x
-
-
- unsigned int index2 = 0;
- for (int x = 0; x < 16; ++x) for (int z = 0; z < 16; ++z) for (int y = 0; y < 256; ++y)
- {
- ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4)) & 0x0f) << ((x&1)*4);
- ++index2;
- }
- InChunkOffset += index2 / 2;
- index2 = 0;
-
- for (int x = 0; x < 16; ++x) for (int z = 0; z < 16; ++z) for (int y = 0; y < 256; ++y)
- {
- ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4)) & 0x0f) << ((x&1)*4);
- ++index2;
- }
- InChunkOffset += index2 / 2;
- index2 = 0;
-
- for (int x = 0; x < 16; ++x) for (int z = 0; z < 16; ++z) for (int y = 0; y < 256; ++y)
- {
- ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4)) & 0x0f) << ((x&1)*4);
- ++index2;
- }
- InChunkOffset += index2 / 2;
-
- AString Converted(ConvertedData, ExpectedSize);
-
- // Add JSON data afterwards
- if (UncompressedData.size() > InChunkOffset)
- {
- Converted.append( UncompressedData.begin() + InChunkOffset, UncompressedData.end());
- }
-
- // Re-compress data
- AString CompressedData;
- {
- int errorcode = CompressString(Converted.data(), Converted.size(), CompressedData, m_CompressionFactor);
- if (errorcode != Z_OK)
- {
- LOGERROR("Error %d compressing data for chunk [%d, %d]",
- errorcode,
- Header->m_ChunkX, Header->m_ChunkZ
- );
- continue;
- }
- }
-
- // Save into file's cache
- Header->m_UncompressedSize = (int)Converted.size();
- Header->m_CompressedSize = (int)CompressedData.size();
- NewDataContents.append(CompressedData);
- }
-
- // Done converting
- m_DataContents = NewDataContents;
- m_ChunkVersion = 3;
- SynchronizeFile();
-
- LOGINFO("Updated \"%s\" version 2 to version 3", m_FileName.c_str());
-}
-
-
-
-
-
-bool cWSSCompact::LoadChunkFromData(const cChunkCoords & a_Chunk, int a_UncompressedSize, const AString & a_Data, cWorld * a_World)
-{
- // Crude data integrity check:
- if (a_UncompressedSize < cChunkDef::BlockDataSize)
- {
- LOGWARNING("Chunk [%d, %d] has too short decompressed data (%d bytes out of %d needed), erasing",
- a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ,
- a_UncompressedSize, cChunkDef::BlockDataSize
- );
- EraseChunkData(a_Chunk);
- return false;
- }
-
- // Decompress the data:
- AString UncompressedData;
- int errorcode = UncompressString(a_Data.data(), a_Data.size(), UncompressedData, (size_t)a_UncompressedSize);
- if (errorcode != Z_OK)
- {
- LOGERROR("Error %d decompressing data for chunk [%d, %d]",
- errorcode,
- a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ
- );
- return false;
- }
-
- if (a_UncompressedSize != (int)UncompressedData.size())
- {
- LOGWARNING("Uncompressed data size differs (exp %d bytes, got " SIZE_T_FMT ") for chunk [%d, %d]",
- a_UncompressedSize, UncompressedData.size(),
- a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ
- );
- return false;
- }
-
- cEntityList Entities;
- cBlockEntityList BlockEntities;
- bool IsLightValid = false;
-
- if (a_UncompressedSize > cChunkDef::BlockDataSize)
- {
- Json::Value root; // will contain the root value after parsing.
- Json::Reader reader;
- if (!reader.parse( UncompressedData.data() + cChunkDef::BlockDataSize, root, false))
- {
- LOGERROR("Failed to parse trailing JSON in chunk [%d, %d]!",
- a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ
- );
- }
- else
- {
- LoadEntitiesFromJson(root, Entities, BlockEntities, a_World);
- IsLightValid = root.get("IsLightValid", false).asBool();
- }
- }
-
- BLOCKTYPE * BlockData = (BLOCKTYPE *)UncompressedData.data();
- NIBBLETYPE * MetaData = (NIBBLETYPE *)(BlockData + MetaOffset);
- NIBBLETYPE * BlockLight = (NIBBLETYPE *)(BlockData + LightOffset);
- NIBBLETYPE * SkyLight = (NIBBLETYPE *)(BlockData + SkyLightOffset);
-
- a_World->QueueSetChunkData(cSetChunkDataPtr(new cSetChunkData(
- a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ,
- BlockData, MetaData,
- IsLightValid ? BlockLight : NULL,
- IsLightValid ? SkyLight : NULL,
- NULL, NULL,
- Entities, BlockEntities,
- false
- )));
-
- return true;
-}
-
-
-
-
-
-bool cWSSCompact::cPAKFile::EraseChunkData(const cChunkCoords & a_Chunk)
-{
- int ChunkX = a_Chunk.m_ChunkX;
- int ChunkZ = a_Chunk.m_ChunkZ;
- int Offset = 0;
- for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr)
- {
- if (((*itr)->m_ChunkX == ChunkX) && ((*itr)->m_ChunkZ == ChunkZ))
- {
- m_DataContents.erase(Offset, (*itr)->m_CompressedSize);
- delete *itr;
- itr = m_ChunkHeaders.erase(itr);
- return true;
- }
- Offset += (*itr)->m_CompressedSize;
- }
-
- return false;
-}
-
-
-
-
-
-bool cWSSCompact::cPAKFile::SaveChunkToData(const cChunkCoords & a_Chunk, cWorld * a_World)
-{
- // Serialize the chunk:
- cJsonChunkSerializer Serializer;
- if (!a_World->GetChunkData(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, Serializer))
- {
- // Chunk not valid
- LOG("cWSSCompact: Trying to save chunk [%d, %d] that has no data, ignoring request.", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
- return false;
- }
-
- AString Data;
- Data.assign((const char *)Serializer.GetBlockData(), cChunkDef::BlockDataSize);
- if (Serializer.HasJsonData())
- {
- AString JsonData;
- Json::StyledWriter writer;
- JsonData = writer.write(Serializer.GetRoot());
- Data.append(JsonData);
- }
-
- // Compress the data:
- AString CompressedData;
- int errorcode = CompressString(Data.data(), Data.size(), CompressedData, m_CompressionFactor);
- if (errorcode != Z_OK)
- {
- LOGERROR("Error %i compressing data for chunk [%d, %d]", errorcode, a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
- return false;
- }
-
- // Erase any existing data for the chunk:
- EraseChunkData(a_Chunk);
-
- // Save the header:
- sChunkHeader * Header = new sChunkHeader;
- if (Header == NULL)
- {
- LOGWARNING("Cannot create a new chunk header to save chunk [%d, %d]", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
- return false;
- }
- Header->m_CompressedSize = (int)CompressedData.size();
- Header->m_ChunkX = a_Chunk.m_ChunkX;
- Header->m_ChunkZ = a_Chunk.m_ChunkZ;
- Header->m_UncompressedSize = (int)Data.size();
- m_ChunkHeaders.push_back(Header);
-
- m_DataContents.append(CompressedData.data(), CompressedData.size());
-
- m_NumDirty++;
- return true;
-}
-
-
-
-
-
-#define WRITE(Var) \
- if (f.Write(&Var, sizeof(Var)) != sizeof(Var)) \
- { \
- LOGERROR("cWSSCompact: ERROR writing %s to file \"%s\" (line %d); file offset %d", #Var, m_FileName.c_str(), __LINE__, f.Tell()); \
- return; \
- }
-
-void cWSSCompact::cPAKFile::SynchronizeFile(void)
-{
- cFile f;
- if (!f.Open(m_FileName, cFile::fmWrite))
- {
- LOGERROR("Cannot open PAK file \"%s\" for writing", m_FileName.c_str());
- return;
- }
-
- WRITE(m_PakVersion);
- WRITE(m_ChunkVersion);
- short NumChunks = (short)m_ChunkHeaders.size();
- WRITE(NumChunks);
- for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr)
- {
- WRITE(**itr);
- }
- if (f.Write(m_DataContents.data(), m_DataContents.size()) != (int)m_DataContents.size())
- {
- LOGERROR("cWSSCompact: ERROR writing chunk contents to file \"%s\" (line %d); file offset %d", m_FileName.c_str(), __LINE__, f.Tell());
- return;
- }
- m_NumDirty = 0;
-}
-
-
-
-
diff --git a/src/WorldStorage/WSSCompact.h b/src/WorldStorage/WSSCompact.h
deleted file mode 100644
index 83e9cb49f..000000000
--- a/src/WorldStorage/WSSCompact.h
+++ /dev/null
@@ -1,157 +0,0 @@
-
-// WSSCompact.h
-
-// Interfaces to the cWSSCompact class representing the "Compact" storage schema (PAK-files)
-
-
-
-
-
-#pragma once
-#ifndef WSSCOMPACT_H_INCLUDED
-#define WSSCOMPACT_H_INCLUDED
-
-#include "WorldStorage.h"
-#include "../Vector3.h"
-#include "json/json.h"
-#include "ChunkDataCallback.h"
-
-
-
-
-
-/// Helper class for serializing a chunk into Json
-class cJsonChunkSerializer :
- public cChunkDataArrayCollector
-{
-public:
-
- cJsonChunkSerializer(void);
-
- Json::Value & GetRoot (void) {return m_Root; }
- BLOCKTYPE * GetBlockData(void) {return (BLOCKTYPE *)m_BlockData; }
- bool HasJsonData (void) const {return m_HasJsonData; }
-
-protected:
-
- // NOTE: block data is serialized into inherited cChunkDataCollector's m_BlockData[] array
-
- // Entities and BlockEntities are serialized to Json
- Json::Value m_Root;
- bool m_HasJsonData;
-
- // cChunkDataCollector overrides:
- virtual void Entity (cEntity * a_Entity) override;
- virtual void BlockEntity (cBlockEntity * a_Entity) override;
- virtual void LightIsValid (bool a_IsLightValid) override;
-} ;
-
-
-
-
-
-class cWSSCompact :
- public cWSSchema
-{
-public:
- cWSSCompact(cWorld * a_World, int a_CompressionFactor) : cWSSchema(a_World), m_CompressionFactor(a_CompressionFactor) {}
- virtual ~cWSSCompact();
-
-protected:
-
- enum
- {
- // Offsets to individual components in the joined blockdata array
- MetaOffset = cChunkDef::NumBlocks,
- LightOffset = MetaOffset + cChunkDef::NumBlocks / 2,
- SkyLightOffset = LightOffset + cChunkDef::NumBlocks / 2,
- } ;
-
- struct sChunkHeader;
- typedef std::vector<sChunkHeader *> sChunkHeaders;
-
- /// Implements a cache for a single PAK file; implements lazy-write in order to be able to write multiple chunks fast
- class cPAKFile
- {
- public:
-
- cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ, int a_CompressionFactor);
- ~cPAKFile();
-
- bool GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data);
- bool SetChunkData(const cChunkCoords & a_Chunk, int a_UncompressedSize, const AString & a_Data);
- bool EraseChunkData(const cChunkCoords & a_Chunk);
-
- bool SaveChunk(const cChunkCoords & a_Chunk, cWorld * a_World);
-
- int GetLayerX(void) const {return m_LayerX; }
- int GetLayerZ(void) const {return m_LayerZ; }
-
- static const int PAK_VERSION = 1;
-#if AXIS_ORDER == AXIS_ORDER_XZY
- static const int CHUNK_VERSION = 3;
-#elif AXIS_ORDER == AXIS_ORDER_YZX
- static const int CHUNK_VERSION = 2;
-#endif
- protected:
-
- AString m_FileName;
- int m_CompressionFactor;
- int m_LayerX;
- int m_LayerZ;
-
- sChunkHeaders m_ChunkHeaders;
- AString m_DataContents; // Data contents of the file, cached
-
- int m_NumDirty; // Number of chunks that were written into m_DataContents but not into the file
-
- Vector3i m_ChunkSize; // Is related to m_ChunkVersion
- char m_ChunkVersion;
- char m_PakVersion;
-
- bool SaveChunkToData(const cChunkCoords & a_Chunk, cWorld * a_World); // Saves the chunk to m_DataContents, updates headers and m_NumDirty
- void SynchronizeFile(void); // Writes m_DataContents along with the headers to file, resets m_NumDirty
-
- void UpdateChunk1To2(void); // Height from 128 to 256
- void UpdateChunk2To3(void); // Axis order from YZX to XZY
- } ;
-
- typedef std::list<cPAKFile *> cPAKFiles;
-
- cCriticalSection m_CS;
- cPAKFiles m_PAKFiles; // A MRU cache of PAK files
-
- int m_CompressionFactor;
-
- /// Loads the correct PAK file either from cache or from disk, manages the m_PAKFiles cache
- cPAKFile * LoadPAKFile(const cChunkCoords & a_Chunk);
-
- /// Gets chunk data from the correct file; locks CS as needed
- bool GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data);
-
- /// Sets chunk data to the correct file; locks CS as needed
- bool SetChunkData(const cChunkCoords & a_Chunk, int a_UncompressedSize, const AString & a_Data);
-
- /// Erases chunk data from the correct file; locks CS as needed
- bool EraseChunkData(const cChunkCoords & a_Chunk);
-
- /// Loads the chunk from the data (no locking needed)
- bool LoadChunkFromData(const cChunkCoords & a_Chunk, int a_UncompressedSize, const AString & a_Data, cWorld * a_World);
-
- void LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities, cWorld * a_World);
-
- // cWSSchema overrides:
- virtual bool LoadChunk(const cChunkCoords & a_Chunk) override;
- virtual bool SaveChunk(const cChunkCoords & a_Chunk) override;
- virtual const AString GetName(void) const override {return "compact"; }
-} ;
-
-
-
-
-
-#endif // WSSCOMPACT_H_INCLUDED
-
-
-
-
diff --git a/src/WorldStorage/WorldStorage.cpp b/src/WorldStorage/WorldStorage.cpp
index 179cf9393..c611bfd90 100644
--- a/src/WorldStorage/WorldStorage.cpp
+++ b/src/WorldStorage/WorldStorage.cpp
@@ -7,7 +7,6 @@
#include "Globals.h"
#include "WorldStorage.h"
-#include "WSSCompact.h"
#include "WSSAnvil.h"
#include "../World.h"
#include "../Generating/ChunkGenerator.h"
@@ -187,7 +186,6 @@ void cWorldStorage::InitSchemas(int a_StorageCompressionFactor)
{
// The first schema added is considered the default
m_Schemas.push_back(new cWSSAnvil (m_World, a_StorageCompressionFactor));
- m_Schemas.push_back(new cWSSCompact (m_World, a_StorageCompressionFactor));
m_Schemas.push_back(new cWSSForgetful(m_World));
// Add new schemas here