1
0

Merge branch 'master' into chunksparsing/structs

Conflicts:
	src/Chunk.cpp
	src/Chunk.h
This commit is contained in:
Tycho 2014-04-27 06:35:27 -07:00
commit 57b8ee9163
188 changed files with 11188 additions and 4931 deletions

2
MCServer/.gitignore vendored
View File

@ -28,3 +28,5 @@ motd.txt
*.deuser
*.dmp
*.xml
mcserver_api.lua

View File

@ -551,7 +551,22 @@ end
{{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.
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:
<table>
<tr><th>String</th><th>Style</th></tr>
<tr><td>b</td><td>Bold text</td></tr>
<tr><td>i</td><td>Italic text</td></tr>
<tr><td>u</td><td>Underlined text</td></tr>
<tr><td>s</td><td>Strikethrough text</td></tr>
<tr><td>o</td><td>Obfuscated text</td></tr>
<tr><td>@X</td><td>color X (X is 0 - 9 or a - f, same as dye meta</td></tr>
</table>
The following picture, taken from MineCraft Wiki, illustrates the color codes:</p>
<img src="http://hydra-media.cursecdn.com/minecraft.gamepedia.com/4/4c/Colors.png?version=34a0f56789a95326e1f7d82047b12232" />
]],
Functions =
{
@ -565,6 +580,7 @@ end
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" },
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." },
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." },
@ -683,7 +699,7 @@ end</pre>
GetLevel = { Params = "EnchantmentNumID", Return = "number", Notes = "Returns the level of the specified enchantment stored in this object; 0 if not stored" },
IsEmpty = { Params = "", Return = "bool", Notes = "Returns true if the object stores no enchantments" },
SetLevel = { Params = "EnchantmentNumID, Level", Return = "", Notes = "Sets the level for the specified enchantment, adding it if not stored before or removing it if level < = 0" },
StringToEnchantmentID = { Params = "EnchantmentTextID", Return = "number", Notes = "(static) Returns the enchantment numerical ID, -1 if not understood. Case insensitive" },
StringToEnchantmentID = { Params = "EnchantmentTextID", Return = "number", Notes = "(static) Returns the enchantment numerical ID, -1 if not understood. Case insensitive. Also understands plain numbers." },
ToString = { Params = "", Return = "string", Notes = "Returns the string description of all the enchantments stored in this object, in numerical-ID form" },
},
Constants =
@ -777,14 +793,14 @@ end</pre>
GetMass = { Params = "", Return = "number", Notes = "Returns the mass of the entity. Currently unused." },
GetMaxHealth = { Params = "", Return = "number", Notes = "Returns the maximum number of hitpoints this entity is allowed to have." },
GetParentClass = { Params = "", Return = "string", Notes = "Returns the name of the direct parent class for this entity" },
GetPitch = { Params = "", Return = "number", Notes = "Returns the pitch (nose-down rotation) of the entity" },
GetPitch = { Params = "", Return = "number", Notes = "Returns the pitch (nose-down rotation) of the entity. Measured in degrees, normal values range from -90 to +90. +90 means looking down, 0 means looking straight ahead, -90 means looking up." },
GetPosition = { Params = "", Return = "{{Vector3d}}", Notes = "Returns the entity's pivot position as a 3D vector" },
GetPosX = { Params = "", Return = "number", Notes = "Returns the X-coord of the entity's pivot" },
GetPosY = { Params = "", Return = "number", Notes = "Returns the Y-coord of the entity's pivot" },
GetPosZ = { Params = "", Return = "number", Notes = "Returns the Z-coord of the entity's pivot" },
GetRawDamageAgainst = { Params = "ReceiverEntity", Return = "number", Notes = "Returns the raw damage that this entity's equipment would cause when attacking the ReceiverEntity. This includes this entity's weapon {{cEnchantments|enchantments}}, but excludes the receiver's armor or potion effects. See {{TakeDamageInfo}} for more information on attack damage." },
GetRoll = { Params = "", Return = "number", Notes = "Returns the roll (sideways rotation) of the entity. Currently unused." },
GetRot = { Params = "", Return = "{{Vector3f}}", Notes = "Returns the entire rotation vector (Yaw, Pitch, Roll)" },
GetRot = { Params = "", Return = "{{Vector3f}}", Notes = "(OBSOLETE) Returns the entire rotation vector (Yaw, Pitch, Roll)" },
GetSpeed = { Params = "", Return = "{{Vector3d}}", Notes = "Returns the complete speed vector of the entity" },
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" },
@ -792,7 +808,7 @@ end</pre>
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." },
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." },
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}}." },
@ -2200,7 +2216,7 @@ end
CastThunderbolt = { Params = "X, Y, Z", Return = "", Notes = "Creates a thunderbolt at the specified coords" },
ChangeWeather = { Params = "", Return = "", Notes = "Forces the weather to change in the next game tick. Weather is changed according to the normal rules: wSunny <-> wRain <-> wStorm" },
ChunkStay = { Params = "ChunkCoordTable, OnChunkAvailable, OnAllChunksAvailable", Return = "", Notes = "Queues the specified chunks to be loaded or generated and calls the specified callbacks once they are loaded. ChunkCoordTable is an arra-table of chunk coords, each coord being a table of 2 numbers: { {Chunk1x, Chunk1z}, {Chunk2x, Chunk2z}, ...}. When any of those chunks are made available (including being available at the start of this call), the OnChunkAvailable() callback is called. When all the chunks are available, the OnAllChunksAvailable() callback is called. The function signatures are: <pre class=\"prettyprint lang-lua\">function OnChunkAvailable(ChunkX, ChunkZ)\nfunction OnAllChunksAvailable()</pre> All return values from the callbacks are ignored." },
CreateProjectile = { Params = "X, Y, Z, {{cProjectileEntity|ProjectileKind}}, {{cEntity|Creator}}, [{{Vector3d|Speed}}]", Return = "", Notes = "Creates a new projectile of the specified kind at the specified coords. The projectile's creator is set to Creator (may be nil). Optional speed indicates the initial speed for the projectile." },
CreateProjectile = { Params = "X, Y, Z, {{cProjectileEntity|ProjectileKind}}, {{cEntity|Creator}}, {{cItem|Originating Item}}, [{{Vector3d|Speed}}]", Return = "", Notes = "Creates a new projectile of the specified kind at the specified coords. The projectile's creator is set to Creator (may be nil). The item that created the projectile entity, commonly the {{cPlayer|player}}'s currently equipped item, is used at present for fireworks to correctly set their entity metadata. It is not used for any other projectile. Optional speed indicates the initial speed for the projectile." },
DigBlock = { Params = "X, Y, Z", Return = "", Notes = "Replaces the specified block with air, without dropping the usual pickups for the block. Wakes up the simulators for the block and its neighbors." },
DoExplosionAt = { Params = "Force, X, Y, Z, CanCauseFire, Source, SourceData", Return = "", Notes = "Creates an explosion of the specified relative force in the specified position. If CanCauseFire is set, the explosion will set blocks on fire, too. The Source parameter specifies the source of the explosion, one of the esXXX constants. The SourceData parameter is specific to each source type, usually it provides more info about the source." },
DoWithBlockEntityAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a block entity at the specified coords, calls the CallbackFunction with the {{cBlockEntity}} parameter representing the block entity. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBlockEntity|BlockEntity}}, [CallbackData])</pre> The function returns false if there is no block entity, or if there is, it returns the bool value that the callback has returned. Use {{tolua}}.cast() to cast the Callback's BlockEntity parameter to the correct {{cBlockEntity}} descendant." },
@ -2913,9 +2929,10 @@ end
{
-- No sorting is provided for these, they will be output in the same order as defined here
{ FileName = "Writing-a-MCServer-plugin.html", Title = "Writing a MCServer plugin" },
{ FileName = "SettingUpDecoda.html", Title = "Setting up the Decoda Lua IDE" },
{ FileName = "SettingUpZeroBrane.html", Title = "Setting up the ZeroBrane Studio Lua IDE" },
{ FileName = "WebWorldThreads.html", Title = "Webserver vs World threads" },
{ FileName = "SettingUpDecoda.html", Title = "Setting up the Decoda Lua IDE" },
{ FileName = "SettingUpZeroBrane.html", Title = "Setting up the ZeroBrane Studio Lua IDE" },
{ FileName = "UsingChunkStays.html", Title = "Using ChunkStays" },
{ FileName = "WebWorldThreads.html", Title = "Webserver vs World threads" },
}
} ;

View File

@ -0,0 +1,167 @@
<!DOCTYPE html>
<html>
<head>
<title>MCServer - Using ChunkStays</title>
<link rel="stylesheet" type="text/css" href="main.css" />
<link rel="stylesheet" type="text/css" href="prettify.css" />
<script src="prettify.js"></script>
<script src="lang-lua.js"></script>
<meta charset="UTF-8">
</head>
<body>
<div id="content">
<h1>Using ChunkStays</h1>
<p>
A plugin may need to manipulate data in arbitrary chunks, and it needs a way to make the server
guarantee that the chunks are available in memory.</p>
<h2>The problem</h2>
<p>
Usually when plugins want to manipulate larger areas of world data, they need to make sure that the
server has the appropriate chunks loaded in the memory. When the data being manipulated can be further
away from the connected players, or the data is being manipulated from a console handler, there is a
real chance that the chunks are not loaded.</p>
<p>
This gets even more important when using the <a href="cBlockArea.html">cBlockArea</a> class for reading
and writing. Those functions will fail when any of the required chunks aren't valid. This means that
either the block area has incomplete data (Read() failed) or incomplete data has been written to the
world (Write() failed). Recovery from this is near impossible - you can't simply read or write again
later, because the world may have changed in the meantime.</p>
<h2>The solution</h2>
<p>
The naive solution would be to monitor chunk loads and unloads, and postpone the operations until all
the chunks are available. This would be quite ineffective and also very soon it would become very
difficult to maintain, if there were multiple code paths requiring this handling.</p>
<p>
An alternate approach has been implemented, accessible through a single (somewhat hidden) function
call: <a href="cWorld.html">cWorld:ChunkStay()</a>. All that this call basically does is, it tells the
server "Load these chunks for me, and call this callback function once you have them all." And the
server does exactly that - it remembers the callback and asks the world loader / generator to provide
the chunks. Once the chunks become available, it calls the callback function for the plugin.</p>
<p>
There are a few gotcha-s, though. If the code that was requesting the read or write had access to some
of the volatile objects, such as <a href="cPlayer.html">cPlayer</a> or
<a href="cEntity.html">cEntity</a> objects, those cannot be accessed by the callback anymore, because
they may have become invalid in the meantime - the player may have disconnected, the entity may have
despawned. So the callback must use the longer way to access such objects, such as calling
<a href="cWorld.html">cWorld:DoWithEntityByID()</a> or
<a href="cWorld.html">cWorld:DoWithPlayer()</a>.</p>
<h2>The example</h2>
<p>
As a simple example, consider a theoretical plugin that allows a player to save the immediate
surroundings of the spawn into a schematic file. The player issues a command to initiate the save, and
the plugin reads a 50 x 50 x 50 block area around the spawn into a cBlockArea and saves it on the disk
as "<PlayerName>_spawn.schematic". When it's done with the saving, it wants to send a message to the
player to let them know the command has succeeded.</p>
<p>
The first attempt shows the naive approach. It simply reads the block area and saves it, then sends the
message. I'll repeat once more, this code is <b>the wrong way</b> to do it!</p>
<pre class="prettyprint lang-lua">
function HandleCommandSaveSpawn(a_Split, a_Player)
-- Get the coords for the spawn:
local SpawnX = a_Player:GetWorld():GetSpawnX()
local SpawnY = a_Player:GetWorld():GetSpawnY()
local SpawnZ = a_Player:GetWorld():GetSpawnZ()
local Bounds = cCuboid(SpawnX - 25, SpawnY - 25, SpawnZ - 25, SpawnX + 25, SpawnY + 25, SpawnZ + 25)
Bounds:ClampY(0, 255)
-- Read the area around spawn into a cBlockArea, save to file:
local Area = cBlockArea()
local FileName = a_Player:GetName() .. "_spawn.schematic"
Area:Read(a_Player:GetWorld(), Bounds, cBlockArea.baTypes + cBlockArea.baMetas)
Area:SaveToSchematicFile(FileName)
-- Notify the player:
a_Player:SendMessage(cCompositeChat("The spawn has been saved", mtInfo))
return true
end
</pre>
<p>
Now if the player goes exploring far and uses the command to save their spawn, the chunks aren't
loaded, so the BlockArea reading fails, the BlockArea contains bad data. Note that the plugin fails to
do any error checking and if the area isn't read from the world, it happily saves the incomplete data
and says "hey, everything's right", althought it has just trashed any previous backup of the spawn
schematic with nonsense data.</p>
<hr/>
<p>
The following script uses the ChunkStay method to alleviate chunk-related problems. This is <b>the
right way</b> of doing it:</p>
<pre class="prettyprint lang-lua">
function HandleCommandSaveSpawn(a_Split, a_Player)
-- Get the coords for the spawn:
local SpawnX = a_Player:GetWorld():GetSpawnX()
local SpawnY = a_Player:GetWorld():GetSpawnY()
local SpawnZ = a_Player:GetWorld():GetSpawnZ()
local Bounds = cCuboid(SpawnX - 25, SpawnY - 25, SpawnZ - 25, SpawnX + 25, SpawnY + 25, SpawnZ + 25)
Bounds:ClampY(0, 255)
-- Get a list of chunks that we need loaded:
local MinChunkX = math.floor((SpawnX - 25) / 16)
local MaxChunkX = math.ceil ((SpawnX + 25) / 16)
local MinChunkZ = math.floor((SpawnZ - 25) / 16)
local MaxChunkZ = math.ceil ((SpawnZ + 25) / 16)
local Chunks = {}
for x = MinChunkX, MaxChunkX do
for z = MinChunkZ, MaxChunkZ do
table.insert(Chunks, {x, z})
end
end -- for x
-- Store the player's name and world to use in the callback, because the a_Player object may no longer be valid:
local PlayerName = a_Player:GetName()
local World = a_Player:GetWorld()
-- This is the callback that is executed once all the chunks are loaded:
local OnAllChunksAvailable = function()
-- Read the area around spawn into a cBlockArea, save to file:
local Area = cBlockArea()
local FileName = PlayerName .. "_spawn.schematic"
if (Area:Read(World, Bounds, cBlockArea.baTypes + cBlockArea.baMetas)) then
Area:SaveToSchematicFile(FileName)
Msg = cCompositeChat("The spawn has been saved", mtInfo)
else
Msg = cCompositeChat("Cannot save the spawn", mtFailure)
end
-- Notify the player:
-- Note that we cannot use a_Player here, because it may no longer be valid (if the player disconnected before the command completes)
World:DoWithPlayer(PlayerName,
function (a_CBPlayer)
a_CBPlayer:SendMessage(Msg)
end
)
end
-- Ask the server to load our chunks and notify us once it's done:
World:ChunkStay(Chunks, nil, OnAllChunksAvailable)
-- Note that code here may get executed before the callback is called!
-- The ChunkStay says "once you have the chunks", not "wait until you have the chunks"
-- So you can't notify the player here, because the saving needn't have occurred yet.
return true
end
</pre>
<p>
Note that this code does its error checking of the Area:Read() function, and it will not overwrite the
previous file unless it actually has the correct data. If you're wondering how the reading could fail
when we've got the chunks loaded, there's still the issue of free RAM - if the memory for the area
cannot be allocated, it cannot be read even with all the chunks present. So we still do need that
check.</p>
<h2>The conclusion</h2>
<p>
Although it makes the code a little bit longer and is a bit more difficult to grasp at first, the
ChunkStay is a useful technique to add to your repertoire. It is to be used whenever you need access to
chunks that may potentially be inaccessible, and you really need the data.</p>
<p>Possibly the biggest hurdle in using the ChunkStay is the fact that it does its work in the
background, thus invalidating all cPlayer and cEntity objects your function may hold, so you need to
re-acquire them from their IDs and names. This is the penalty for using multi-threaded code.</p>
<script>
prettyPrint();
</script>
</div>
</body>
</html>

View File

@ -39,31 +39,31 @@
<h2>Example</h2>
The Core has the facility to kick players using the web interface. It used the following code for the kicking (inside the webadmin handler):
<pre class="prettyprint lang-lua">
local KickPlayerName = Request.Params["players-kick"]
local FoundPlayerCallback = function(Player)
if (Player:GetName() == KickPlayerName) then
Player:GetClientHandle():Kick("You were kicked from the game!")
end
<pre class="prettyprint lang-lua">
local KickPlayerName = Request.Params["players-kick"]
local FoundPlayerCallback = function(Player)
if (Player:GetName() == KickPlayerName) then
Player:GetClientHandle():Kick("You were kicked from the game!")
end
end
cRoot:Get():FindAndDoWithPlayer(KickPlayerName, FoundPlayerCallback)
</pre>
The cRoot:FindAndDoWithPlayer() is unsafe and could have caused a deadlock. The new solution is queue a task; but since we don't know in which world the player is, we need to queue the task to all worlds:
<pre class="prettyprint lang-lua">
cRoot:Get():ForEachWorld( -- For each world...
function(World)
World:QueueTask( -- ... queue a task...
function(a_World)
a_World:DoWithPlayer(KickPlayerName, -- ... to walk the playerlist...
function (a_Player)
a_Player:GetClientHandle():Kick("You were kicked from the game!") -- ... and kick the player
end
cRoot:Get():FindAndDoWithPlayer(KickPlayerName, FoundPlayerCallback)
</pre>
The cRoot:FindAndDoWithPlayer() is unsafe and could have caused a deadlock. The new solution is queue a task; but since we don't know in which world the player is, we need to queue the task to all worlds:
<pre class="prettyprint lang-lua">
cRoot:Get():ForEachWorld( -- For each world...
function(World)
World:QueueTask( -- ... queue a task...
function(a_World)
a_World:DoWithPlayer(KickPlayerName, -- ... to walk the playerlist...
function (a_Player)
a_Player:GetClientHandle():Kick("You were kicked from the game!") -- ... and kick the player
end
)
end
)
end
)
</pre>
)
end
)
end
)
</pre>
<script>
prettyPrint();
</script>

View File

@ -16,22 +16,22 @@ local function ListSubcommands(a_Player, a_Subcommands, a_CmdString)
end
-- Enum all the subcommands:
local Verbs = {};
local Verbs = {}
for cmd, info in pairs(a_Subcommands) do
if (a_Player:HasPermission(info.Permission or "")) then
table.insert(Verbs, " " .. a_CmdString .. " " .. cmd);
if ((a_Player == nil) or (a_Player:HasPermission(info.Permission or ""))) then
table.insert(Verbs, a_CmdString .. " " .. cmd)
end
end
table.sort(Verbs);
table.sort(Verbs)
-- Send the list:
if (a_Player == nil) then
for idx, verb in ipairs(Verbs) do
LOGINFO(verb);
LOGINFO(" " .. verb)
end
else
for idx, verb in ipairs(Verbs) do
a_Player:SendMessage(verb);
a_Player:SendMessage(cCompositeChat(" ", mtInfo):AddSuggestCommandPart(verb, verb))
end
end
end

View File

@ -47,14 +47,15 @@ AttackDamage=4.0
SightDistance=25.0
MaxHealth=40
[Zombiepigman]
[ZombiePigman]
AttackRange=2.0
AttackRate=1
AttackDamage=7.0
SightDistance=25.0
MaxHealth=20
IsFireproof=1
[Cavespider]
[CaveSpider]
AttackRange=2.0
AttackRate=1
AttackDamage=2.0
@ -74,6 +75,7 @@ AttackRate=1
AttackDamage=0.0
SightDistance=50.0
MaxHealth=10
IsFireproof=1
[Silverfish]
AttackRange=2.0
@ -115,6 +117,7 @@ AttackRate=1
AttackDamage=6.0
SightDistance=25.0
MaxHealth=20
IsFireproof=1
[Villager]
AttackRange=2.0
@ -122,6 +125,7 @@ AttackRate=1
AttackDamage=0.0
SightDistance=25.0
MaxHealth=20
IsFireproof=0
[Witch]
AttackRange=2.0
@ -145,12 +149,13 @@ AttackDamage=0.0
SightDistance=25.0
MaxHealth=10
[Magmacube]
[MagmaCube]
AttackRange=2.0
AttackRate=1
AttackDamage=6.0
SightDistance=25.0
MaxHealth=16
IsFireproof=1
[Horse]
AttackRange=2.0

View File

@ -30,7 +30,7 @@ if "a%ftpsite%" == "a" (
cd MCServer
copy /Y settings_apidump.ini settings.ini
echo stop | MCServer
echo api | MCServer
cd ..

View File

@ -45,6 +45,8 @@ macro(set_flags)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11")
add_flags_cxx("-stdlib=libc++")
add_flags_lnk("-stdlib=libc++")
else()
add_flags_cxx("-pthread")
endif()

View File

@ -387,6 +387,8 @@ bool cConnection::RelayFromServer(void)
return CLIENTSEND(Buffer, res);
}
}
ASSERT(!"Unhandled server state while relaying from server");
return false;
}
@ -423,6 +425,8 @@ bool cConnection::RelayFromClient(void)
return SERVERSEND(Buffer, res);
}
}
ASSERT(!"Unhandled server state while relaying from client");
return false;
}
@ -438,11 +442,11 @@ double cConnection::GetRelativeTime(void)
bool cConnection::SendData(SOCKET a_Socket, const char * a_Data, int a_Size, const char * a_Peer)
bool cConnection::SendData(SOCKET a_Socket, const char * a_Data, size_t a_Size, const char * a_Peer)
{
DataLog(a_Data, a_Size, "Sending data to %s, %d bytes", a_Peer, a_Size);
DataLog(a_Data, a_Size, "Sending data to %s, %u bytes", a_Peer, (unsigned)a_Size);
int res = send(a_Socket, a_Data, a_Size, 0);
int res = send(a_Socket, a_Data, (int)a_Size, 0);
if (res <= 0)
{
Log("%s closed the socket: %d, %d; aborting connection", a_Peer, res, SocketError);
@ -2193,11 +2197,39 @@ bool cConnection::HandleServerSpawnMob(void)
struct sSpawnData
{
AString m_Name;
AString m_Value;
AString m_Signature;
sSpawnData(const AString & a_Name, const AString & a_Value, const AString & a_Signature) :
m_Name(a_Name),
m_Value(a_Value),
m_Signature(a_Signature)
{
}
};
typedef std::vector<sSpawnData> sSpawnDatas;
bool cConnection::HandleServerSpawnNamedEntity(void)
{
HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, EntityID);
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, EntityUUID);
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, EntityName);
HANDLE_SERVER_PACKET_READ(ReadVarInt, UInt32, DataCount);
sSpawnDatas Data;
for (UInt32 i = 0; i < DataCount; i++)
{
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Name)
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Value)
HANDLE_SERVER_PACKET_READ(ReadVarUTF8String, AString, Signature)
Data.push_back(sSpawnData(Name, Value, Signature));
}
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX);
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY);
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ);
@ -2215,6 +2247,13 @@ bool cConnection::HandleServerSpawnNamedEntity(void)
Log(" EntityID = %u (0x%x)", EntityID, EntityID);
Log(" UUID = \"%s\"", EntityUUID.c_str());
Log(" Name = \"%s\"", EntityName.c_str());
Log(" NumData = %u", DataCount);
for (sSpawnDatas::const_iterator itr = Data.begin(), end = Data.end(); itr != end; ++itr)
{
Log(" Name = \"%s\", Value = \"%s\", Signature = \"%s\"",
itr->m_Name.c_str(), itr->m_Value.c_str(), itr->m_Signature.c_str()
);
} // for itr - Data[]
Log(" Pos = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str());
Log(" Rotation = <yaw %d, pitch %d>", Yaw, Pitch);
Log(" CurrentItem = %d", CurrentItem);

View File

@ -103,7 +103,7 @@ protected:
double GetRelativeTime(void);
/// Sends data to the specified socket. If sending fails, prints a fail message using a_Peer and returns false.
bool SendData(SOCKET a_Socket, const char * a_Data, int a_Size, const char * a_Peer);
bool SendData(SOCKET a_Socket, const char * a_Data, size_t a_Size, const char * a_Peer);
/// Sends data to the specified socket. If sending fails, prints a fail message using a_Peer and returns false.
bool SendData(SOCKET a_Socket, cByteBuffer & a_Data, const char * a_Peer);

View File

@ -4,12 +4,11 @@ project (expat)
file(GLOB SOURCE
"*.c"
"*.h"
)
# add headers to MSVC project files:
if (WIN32)
file(GLOB HEADERS "*.h")
set(SOURCE ${SOURCE} ${HEADERS})
# Set files to go to a "Sources" folder in MSVC project files:
if (MSVC)
source_group("Sources" FILES ${SOURCE})
endif()

View File

@ -1,7 +1,11 @@
cmake_minimum_required (VERSION 2.6)
project (iniFile)
include_directories ("${PROJECT_SOURCE_DIR}/../../src/")
add_library(iniFile iniFile)
file(GLOB SOURCE
"*.h"
"*.cpp"
)
add_library(iniFile ${SOURCE})

View File

@ -154,7 +154,7 @@ bool cIniFile::ReadFile(const AString & a_FileName, bool a_AllowExampleRedirect)
case ';':
case '#':
{
if (names.size() == 0)
if (names.empty())
{
AddHeaderComment(line.substr(pLeft + 1));
}
@ -168,8 +168,9 @@ bool cIniFile::ReadFile(const AString & a_FileName, bool a_AllowExampleRedirect)
} // while (getline())
f.close();
if (names.size() == 0)
if (keys.empty() && names.empty() && comments.empty())
{
// File be empty or unreadable, equivalent to nonexistant
return false;
}

View File

@ -7,6 +7,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../")
file(GLOB SOURCE
"*.c"
"*.h"
)
add_library(luaexpat ${SOURCE})

View File

@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../../src/")
file(GLOB SOURCE
"*.cpp"
"*.h"
)
add_library(md5 ${SOURCE})

View File

@ -26,7 +26,11 @@ if(NOT XXD_EXECUTABLE STREQUAL "XXD_EXECUTABLE-NOTFOUND")
COMMAND ${XXD_EXECUTABLE} -i lua/declaration.lua | sed 's/unsigned char/static const unsigned char/g' >declaration_lua.h
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/src/bin/
DEPENDS ${PROJECT_SOURCE_DIR}/src/bin/lua/declaration.lua)
set_property(SOURCE src/bin/toluabind.c APPEND PROPERTY OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/src/bin/enumerate_lua.h ${PROJECT_SOURCE_DIR}/src/bin/basic_lua.h ${PROJECT_SOURCE_DIR}/src/bin/function_lua.h ${PROJECT_SOURCE_DIR}/src/bin/declaration_lua.h)
add_custom_command(OUTPUT ${PROJECT_SOURCE_DIR}/src/bin/container_lua.h
COMMAND ${XXD_EXECUTABLE} -i lua/container.lua | sed 's/unsigned char/static const unsigned char/g' >container_lua.h
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/src/bin/
DEPENDS ${PROJECT_SOURCE_DIR}/src/bin/lua/container.lua)
set_property(SOURCE src/bin/toluabind.c APPEND PROPERTY OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/src/bin/enumerate_lua.h ${PROJECT_SOURCE_DIR}/src/bin/basic_lua.h ${PROJECT_SOURCE_DIR}/src/bin/function_lua.h ${PROJECT_SOURCE_DIR}/src/bin/declaration_lua.h ${PROJECT_SOURCE_DIR}/src/bin/container_lua.h)
else()
message("xxd not found, changes to tolua scripts will be ignored")
endif()

File diff suppressed because it is too large Load Diff

View File

@ -990,14 +990,15 @@ static const unsigned char lua_function_lua[] = {
0x5f, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x28, 0x74,
0x29, 0x0a, 0x20, 0x73, 0x65, 0x74, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x61,
0x62, 0x6c, 0x65, 0x28, 0x74, 0x2c, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x46,
0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x0a, 0x0a, 0x20, 0x69,
0x66, 0x20, 0x74, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x7e, 0x3d,
0x20, 0x27, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x27, 0x20, 0x61, 0x6e, 0x64,
0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x0a, 0x20, 0x69, 0x66,
0x20, 0x74, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x7e, 0x3d, 0x20,
0x27, 0x27, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x65, 0x72,
0x72, 0x6f, 0x72, 0x28, 0x22, 0x23, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69,
0x64, 0x20, 0x27, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x27, 0x20, 0x73, 0x70,
0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22,
0x27, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x27, 0x20, 0x61, 0x6e, 0x64, 0x20,
0x74, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x7e, 0x3d, 0x20, 0x27,
0x27, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x65, 0x72, 0x72,
0x6f, 0x72, 0x28, 0x22, 0x23, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64,
0x20, 0x27, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x27, 0x20, 0x73, 0x70, 0x65,
0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20,
0x22, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74,
0x29, 0x0a, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x20, 0x61, 0x70, 0x70,
0x65, 0x6e, 0x64, 0x28, 0x74, 0x29, 0x0a, 0x20, 0x69, 0x66, 0x20, 0x74,
0x3a, 0x69, 0x6e, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x28, 0x29, 0x20, 0x74,
@ -1072,139 +1073,139 @@ static const unsigned char lua_function_lua[] = {
0x0a, 0x20, 0x2d, 0x2d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x74, 0x20,
0x3d, 0x20, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61,
0x6d, 0x73, 0x28, 0x73, 0x74, 0x72, 0x73, 0x75, 0x62, 0x28, 0x61, 0x2c,
0x32, 0x2c, 0x2d, 0x32, 0x29, 0x29, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20,
0x6e, 0x6f, 0x74, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x5b, 0x27, 0x57,
0x27, 0x5d, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e,
0x67, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x28, 0x61, 0x2c, 0x20, 0x22, 0x25,
0x2e, 0x25, 0x2e, 0x25, 0x2e, 0x25, 0x73, 0x2a, 0x25, 0x29, 0x22, 0x29,
0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x0a, 0x09, 0x09, 0x77, 0x61, 0x72,
0x6e, 0x69, 0x6e, 0x67, 0x28, 0x22, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x76, 0x61, 0x72,
0x69, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65,
0x6e, 0x74, 0x73, 0x20, 0x28, 0x60, 0x2e, 0x2e, 0x2e, 0x27, 0x29, 0x20,
0x61, 0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x75, 0x70, 0x70,
0x6f, 0x72, 0x74, 0x65, 0x64, 0x2e, 0x20, 0x49, 0x67, 0x6e, 0x6f, 0x72,
0x69, 0x6e, 0x67, 0x20, 0x22, 0x2e, 0x2e, 0x64, 0x2e, 0x2e, 0x61, 0x2e,
0x2e, 0x63, 0x29, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e,
0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x0a,
0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x69, 0x3d, 0x31, 0x0a, 0x20,
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6c, 0x20, 0x3d, 0x20, 0x7b, 0x6e,
0x3d, 0x30, 0x7d, 0x0a, 0x0a, 0x20, 0x09, 0x61, 0x20, 0x3d, 0x20, 0x73,
0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75, 0x62, 0x28, 0x61,
0x2c, 0x20, 0x22, 0x25, 0x73, 0x2a, 0x28, 0x5b, 0x25, 0x28, 0x25, 0x29,
0x5d, 0x29, 0x25, 0x73, 0x2a, 0x22, 0x2c, 0x20, 0x22, 0x25, 0x31, 0x22,
0x29, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x74, 0x2c, 0x73,
0x74, 0x72, 0x69, 0x70, 0x2c, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x3d, 0x20,
0x73, 0x74, 0x72, 0x69, 0x70, 0x5f, 0x70, 0x61, 0x72, 0x73, 0x28, 0x73,
0x74, 0x72, 0x73, 0x75, 0x62, 0x28, 0x61, 0x2c, 0x32, 0x2c, 0x2d, 0x32,
0x29, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x73, 0x74, 0x72, 0x69,
0x70, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x09, 0x09, 0x2d, 0x2d, 0x6c,
0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6e, 0x73, 0x20, 0x3d, 0x20, 0x73, 0x74,
0x72, 0x69, 0x6e, 0x67, 0x2e, 0x73, 0x75, 0x62, 0x28, 0x73, 0x74, 0x72,
0x73, 0x75, 0x62, 0x28, 0x61, 0x2c, 0x31, 0x2c, 0x2d, 0x32, 0x29, 0x2c,
0x20, 0x31, 0x2c, 0x20, 0x2d, 0x28, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x2e, 0x6c, 0x65, 0x6e, 0x28, 0x6c, 0x61, 0x73, 0x74, 0x29, 0x2b, 0x31,
0x29, 0x29, 0x0a, 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6e,
0x73, 0x20, 0x3d, 0x20, 0x6a, 0x6f, 0x69, 0x6e, 0x28, 0x74, 0x2c, 0x20,
0x22, 0x2c, 0x22, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x6c, 0x61, 0x73, 0x74,
0x2d, 0x31, 0x29, 0x0a, 0x0a, 0x09, 0x09, 0x6e, 0x73, 0x20, 0x3d, 0x20,
0x22, 0x28, 0x22, 0x2e, 0x2e, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e,
0x67, 0x73, 0x75, 0x62, 0x28, 0x6e, 0x73, 0x2c, 0x20, 0x22, 0x25, 0x73,
0x2a, 0x2c, 0x25, 0x73, 0x2a, 0x24, 0x22, 0x2c, 0x20, 0x22, 0x22, 0x29,
0x2e, 0x2e, 0x27, 0x29, 0x27, 0x0a, 0x09, 0x09, 0x2d, 0x2d, 0x6e, 0x73,
0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x70, 0x5f, 0x64, 0x65, 0x66,
0x61, 0x75, 0x6c, 0x74, 0x73, 0x28, 0x6e, 0x73, 0x29, 0x0a, 0x0a, 0x09,
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x20, 0x3d, 0x20, 0x46,
0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x64, 0x2c, 0x20, 0x6e,
0x73, 0x2c, 0x20, 0x63, 0x29, 0x0a, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20,
0x69, 0x3d, 0x31, 0x2c, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x64, 0x6f, 0x0a,
0x09, 0x09, 0x09, 0x74, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74,
0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75, 0x62, 0x28, 0x74, 0x5b,
0x69, 0x5d, 0x2c, 0x20, 0x22, 0x3d, 0x2e, 0x2a, 0x24, 0x22, 0x2c, 0x20,
0x22, 0x22, 0x29, 0x0a, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x65,
0x6e, 0x64, 0x0a, 0x0a, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x74,
0x5b, 0x69, 0x5d, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x6c, 0x2e, 0x6e,
0x20, 0x3d, 0x20, 0x6c, 0x2e, 0x6e, 0x2b, 0x31, 0x0a, 0x20, 0x20, 0x6c,
0x5b, 0x6c, 0x2e, 0x6e, 0x5d, 0x20, 0x3d, 0x20, 0x44, 0x65, 0x63, 0x6c,
0x61, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x74, 0x5b, 0x69, 0x5d,
0x2c, 0x27, 0x76, 0x61, 0x72, 0x27, 0x2c, 0x74, 0x72, 0x75, 0x65, 0x29,
0x0a, 0x20, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x69, 0x2b, 0x31, 0x0a, 0x20,
0x65, 0x6e, 0x64, 0x0a, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66,
0x20, 0x3d, 0x20, 0x44, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x28, 0x64, 0x2c, 0x27, 0x66, 0x75, 0x6e, 0x63, 0x27, 0x29,
0x0a, 0x20, 0x66, 0x2e, 0x61, 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x6c,
0x0a, 0x20, 0x66, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x3d, 0x20,
0x63, 0x0a, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x46,
0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x29, 0x0a, 0x65,
0x6e, 0x64, 0x0a, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x20, 0x6a, 0x6f, 0x69, 0x6e, 0x28, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x70,
0x2c, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x2c, 0x20, 0x6c, 0x61, 0x73,
0x74, 0x29, 0x0a, 0x0a, 0x09, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x3d,
0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x31, 0x0a,
0x09, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x6c, 0x61, 0x73, 0x74,
0x20, 0x6f, 0x72, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x67, 0x65,
0x74, 0x6e, 0x28, 0x74, 0x29, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
0x20, 0x6c, 0x73, 0x65, 0x70, 0x20, 0x3d, 0x20, 0x22, 0x22, 0x0a, 0x09,
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x74, 0x20, 0x3d, 0x20,
0x22, 0x22, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6c, 0x6f,
0x6f, 0x70, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x0a, 0x09,
0x66, 0x6f, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x66, 0x69, 0x72, 0x73,
0x74, 0x2c, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x64, 0x6f, 0x0a, 0x0a, 0x09,
0x09, 0x72, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x74, 0x2e, 0x2e,
0x6c, 0x73, 0x65, 0x70, 0x2e, 0x2e, 0x74, 0x5b, 0x69, 0x5d, 0x0a, 0x09,
0x09, 0x6c, 0x73, 0x65, 0x70, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x70, 0x0a,
0x09, 0x09, 0x6c, 0x6f, 0x6f, 0x70, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75,
0x65, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x6e,
0x6f, 0x74, 0x20, 0x6c, 0x6f, 0x6f, 0x70, 0x20, 0x74, 0x68, 0x65, 0x6e,
0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x22, 0x22,
0x0a, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75,
0x72, 0x6e, 0x20, 0x72, 0x65, 0x74, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a,
0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74, 0x72,
0x69, 0x70, 0x5f, 0x70, 0x61, 0x72, 0x73, 0x28, 0x73, 0x29, 0x0a, 0x0a,
0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x74, 0x20, 0x3d, 0x20, 0x73,
0x70, 0x6c, 0x69, 0x74, 0x5f, 0x63, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e,
0x73, 0x28, 0x73, 0x2c, 0x20, 0x27, 0x2c, 0x27, 0x29, 0x0a, 0x09, 0x6c,
0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x74, 0x72, 0x69, 0x70, 0x20, 0x3d,
0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61,
0x6c, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x0a, 0x0a, 0x09, 0x66, 0x6f, 0x72,
0x20, 0x69, 0x3d, 0x74, 0x2e, 0x6e, 0x2c, 0x31, 0x2c, 0x2d, 0x31, 0x20,
0x64, 0x6f, 0x0a, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74,
0x20, 0x73, 0x74, 0x72, 0x69, 0x70, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x70,
0x61, 0x72, 0x61, 0x6d, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x28,
0x74, 0x5b, 0x69, 0x5d, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x09,
0x09, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x69, 0x0a, 0x09,
0x09, 0x09, 0x73, 0x74, 0x72, 0x69, 0x70, 0x20, 0x3d, 0x20, 0x74, 0x72,
0x75, 0x65, 0x0a, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x09, 0x2d,
0x2d, 0x69, 0x66, 0x20, 0x73, 0x74, 0x72, 0x69, 0x70, 0x20, 0x74, 0x68,
0x65, 0x6e, 0x0a, 0x09, 0x09, 0x2d, 0x2d, 0x09, 0x74, 0x5b, 0x69, 0x5d,
0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73,
0x75, 0x62, 0x28, 0x74, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x22, 0x3d, 0x2e,
0x2a, 0x24, 0x22, 0x2c, 0x20, 0x22, 0x22, 0x29, 0x0a, 0x09, 0x09, 0x2d,
0x2d, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x09,
0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x2c, 0x73, 0x74, 0x72,
0x69, 0x70, 0x2c, 0x6c, 0x61, 0x73, 0x74, 0x0a, 0x0a, 0x65, 0x6e, 0x64,
0x0a, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73,
0x74, 0x72, 0x69, 0x70, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74,
0x73, 0x28, 0x73, 0x29, 0x0a, 0x0a, 0x09, 0x73, 0x20, 0x3d, 0x20, 0x73,
0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75, 0x62, 0x28, 0x73,
0x2c, 0x20, 0x22, 0x5e, 0x25, 0x28, 0x22, 0x2c, 0x20, 0x22, 0x22, 0x29,
0x0a, 0x09, 0x73, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x2e, 0x67, 0x73, 0x75, 0x62, 0x28, 0x73, 0x2c, 0x20, 0x22, 0x25, 0x29,
0x24, 0x22, 0x2c, 0x20, 0x22, 0x22, 0x29, 0x0a, 0x0a, 0x09, 0x6c, 0x6f,
0x63, 0x61, 0x6c, 0x20, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x70, 0x6c, 0x69,
0x74, 0x5f, 0x63, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x28, 0x73,
0x2c, 0x20, 0x22, 0x2c, 0x22, 0x29, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61,
0x6c, 0x20, 0x73, 0x65, 0x70, 0x2c, 0x20, 0x72, 0x65, 0x74, 0x20, 0x3d,
0x20, 0x22, 0x22, 0x2c, 0x22, 0x22, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20,
0x69, 0x3d, 0x31, 0x2c, 0x74, 0x2e, 0x6e, 0x20, 0x64, 0x6f, 0x0a, 0x09,
0x09, 0x74, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69,
0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75, 0x62, 0x28, 0x74, 0x5b, 0x69, 0x5d,
0x2c, 0x20, 0x22, 0x3d, 0x2e, 0x2a, 0x24, 0x22, 0x2c, 0x20, 0x22, 0x22,
0x29, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65,
0x74, 0x2e, 0x2e, 0x73, 0x65, 0x70, 0x2e, 0x2e, 0x74, 0x5b, 0x69, 0x5d,
0x0a, 0x09, 0x09, 0x73, 0x65, 0x70, 0x20, 0x3d, 0x20, 0x22, 0x2c, 0x22,
0x0a, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75,
0x72, 0x6e, 0x20, 0x22, 0x28, 0x22, 0x2e, 0x2e, 0x72, 0x65, 0x74, 0x2e,
0x2e, 0x22, 0x29, 0x22, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x0a
0x32, 0x2c, 0x2d, 0x32, 0x29, 0x29, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x6e,
0x6f, 0x74, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x5b, 0x27, 0x57, 0x27,
0x5d, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x2e, 0x66, 0x69, 0x6e, 0x64, 0x28, 0x61, 0x2c, 0x20, 0x22, 0x25, 0x2e,
0x25, 0x2e, 0x25, 0x2e, 0x25, 0x73, 0x2a, 0x25, 0x29, 0x22, 0x29, 0x20,
0x74, 0x68, 0x65, 0x6e, 0x0a, 0x0a, 0x09, 0x09, 0x77, 0x61, 0x72, 0x6e,
0x69, 0x6e, 0x67, 0x28, 0x22, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x76, 0x61, 0x72, 0x69,
0x61, 0x62, 0x6c, 0x65, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e,
0x74, 0x73, 0x20, 0x28, 0x60, 0x2e, 0x2e, 0x2e, 0x27, 0x29, 0x20, 0x61,
0x72, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6f,
0x72, 0x74, 0x65, 0x64, 0x2e, 0x20, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x69,
0x6e, 0x67, 0x20, 0x22, 0x2e, 0x2e, 0x64, 0x2e, 0x2e, 0x61, 0x2e, 0x2e,
0x63, 0x29, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20,
0x6e, 0x69, 0x6c, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x0a, 0x20,
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x69, 0x3d, 0x31, 0x0a, 0x20, 0x6c,
0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6c, 0x20, 0x3d, 0x20, 0x7b, 0x6e, 0x3d,
0x30, 0x7d, 0x0a, 0x0a, 0x20, 0x09, 0x61, 0x20, 0x3d, 0x20, 0x73, 0x74,
0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75, 0x62, 0x28, 0x61, 0x2c,
0x20, 0x22, 0x25, 0x73, 0x2a, 0x28, 0x5b, 0x25, 0x28, 0x25, 0x29, 0x5d,
0x29, 0x25, 0x73, 0x2a, 0x22, 0x2c, 0x20, 0x22, 0x25, 0x31, 0x22, 0x29,
0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x74, 0x2c, 0x73, 0x74,
0x72, 0x69, 0x70, 0x2c, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x73,
0x74, 0x72, 0x69, 0x70, 0x5f, 0x70, 0x61, 0x72, 0x73, 0x28, 0x73, 0x74,
0x72, 0x73, 0x75, 0x62, 0x28, 0x61, 0x2c, 0x32, 0x2c, 0x2d, 0x32, 0x29,
0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x73, 0x74, 0x72, 0x69, 0x70,
0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x09, 0x09, 0x2d, 0x2d, 0x6c, 0x6f,
0x63, 0x61, 0x6c, 0x20, 0x6e, 0x73, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72,
0x69, 0x6e, 0x67, 0x2e, 0x73, 0x75, 0x62, 0x28, 0x73, 0x74, 0x72, 0x73,
0x75, 0x62, 0x28, 0x61, 0x2c, 0x31, 0x2c, 0x2d, 0x32, 0x29, 0x2c, 0x20,
0x31, 0x2c, 0x20, 0x2d, 0x28, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e,
0x6c, 0x65, 0x6e, 0x28, 0x6c, 0x61, 0x73, 0x74, 0x29, 0x2b, 0x31, 0x29,
0x29, 0x0a, 0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6e, 0x73,
0x20, 0x3d, 0x20, 0x6a, 0x6f, 0x69, 0x6e, 0x28, 0x74, 0x2c, 0x20, 0x22,
0x2c, 0x22, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x2d,
0x31, 0x29, 0x0a, 0x0a, 0x09, 0x09, 0x6e, 0x73, 0x20, 0x3d, 0x20, 0x22,
0x28, 0x22, 0x2e, 0x2e, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67,
0x73, 0x75, 0x62, 0x28, 0x6e, 0x73, 0x2c, 0x20, 0x22, 0x25, 0x73, 0x2a,
0x2c, 0x25, 0x73, 0x2a, 0x24, 0x22, 0x2c, 0x20, 0x22, 0x22, 0x29, 0x2e,
0x2e, 0x27, 0x29, 0x27, 0x0a, 0x09, 0x09, 0x2d, 0x2d, 0x6e, 0x73, 0x20,
0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x70, 0x5f, 0x64, 0x65, 0x66, 0x61,
0x75, 0x6c, 0x74, 0x73, 0x28, 0x6e, 0x73, 0x29, 0x0a, 0x0a, 0x09, 0x09,
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x20, 0x3d, 0x20, 0x46, 0x75,
0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x64, 0x2c, 0x20, 0x6e, 0x73,
0x2c, 0x20, 0x63, 0x29, 0x0a, 0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x69,
0x3d, 0x31, 0x2c, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x64, 0x6f, 0x0a, 0x09,
0x09, 0x09, 0x74, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72,
0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75, 0x62, 0x28, 0x74, 0x5b, 0x69,
0x5d, 0x2c, 0x20, 0x22, 0x3d, 0x2e, 0x2a, 0x24, 0x22, 0x2c, 0x20, 0x22,
0x22, 0x29, 0x0a, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x65, 0x6e,
0x64, 0x0a, 0x0a, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x74, 0x5b,
0x69, 0x5d, 0x20, 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x6c, 0x2e, 0x6e, 0x20,
0x3d, 0x20, 0x6c, 0x2e, 0x6e, 0x2b, 0x31, 0x0a, 0x20, 0x20, 0x6c, 0x5b,
0x6c, 0x2e, 0x6e, 0x5d, 0x20, 0x3d, 0x20, 0x44, 0x65, 0x63, 0x6c, 0x61,
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x74, 0x5b, 0x69, 0x5d, 0x2c,
0x27, 0x76, 0x61, 0x72, 0x27, 0x2c, 0x74, 0x72, 0x75, 0x65, 0x29, 0x0a,
0x20, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x69, 0x2b, 0x31, 0x0a, 0x20, 0x65,
0x6e, 0x64, 0x0a, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x20,
0x3d, 0x20, 0x44, 0x65, 0x63, 0x6c, 0x61, 0x72, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x28, 0x64, 0x2c, 0x27, 0x66, 0x75, 0x6e, 0x63, 0x27, 0x29, 0x0a,
0x20, 0x66, 0x2e, 0x61, 0x72, 0x67, 0x73, 0x20, 0x3d, 0x20, 0x6c, 0x0a,
0x20, 0x66, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x63,
0x0a, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x5f, 0x46, 0x75,
0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x66, 0x29, 0x0a, 0x65, 0x6e,
0x64, 0x0a, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20,
0x6a, 0x6f, 0x69, 0x6e, 0x28, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x70, 0x2c,
0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x2c, 0x20, 0x6c, 0x61, 0x73, 0x74,
0x29, 0x0a, 0x0a, 0x09, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x3d, 0x20,
0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x31, 0x0a, 0x09,
0x6c, 0x61, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20,
0x6f, 0x72, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x67, 0x65, 0x74,
0x6e, 0x28, 0x74, 0x29, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20,
0x6c, 0x73, 0x65, 0x70, 0x20, 0x3d, 0x20, 0x22, 0x22, 0x0a, 0x09, 0x6c,
0x6f, 0x63, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x22,
0x22, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6c, 0x6f, 0x6f,
0x70, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x66,
0x6f, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74,
0x2c, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x64, 0x6f, 0x0a, 0x0a, 0x09, 0x09,
0x72, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x74, 0x2e, 0x2e, 0x6c,
0x73, 0x65, 0x70, 0x2e, 0x2e, 0x74, 0x5b, 0x69, 0x5d, 0x0a, 0x09, 0x09,
0x6c, 0x73, 0x65, 0x70, 0x20, 0x3d, 0x20, 0x73, 0x65, 0x70, 0x0a, 0x09,
0x09, 0x6c, 0x6f, 0x6f, 0x70, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65,
0x0a, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x6e, 0x6f,
0x74, 0x20, 0x6c, 0x6f, 0x6f, 0x70, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x22, 0x22, 0x0a,
0x09, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72,
0x6e, 0x20, 0x72, 0x65, 0x74, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x66,
0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74, 0x72, 0x69,
0x70, 0x5f, 0x70, 0x61, 0x72, 0x73, 0x28, 0x73, 0x29, 0x0a, 0x0a, 0x09,
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x70,
0x6c, 0x69, 0x74, 0x5f, 0x63, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73,
0x28, 0x73, 0x2c, 0x20, 0x27, 0x2c, 0x27, 0x29, 0x0a, 0x09, 0x6c, 0x6f,
0x63, 0x61, 0x6c, 0x20, 0x73, 0x74, 0x72, 0x69, 0x70, 0x20, 0x3d, 0x20,
0x66, 0x61, 0x6c, 0x73, 0x65, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
0x20, 0x6c, 0x61, 0x73, 0x74, 0x0a, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20,
0x69, 0x3d, 0x74, 0x2e, 0x6e, 0x2c, 0x31, 0x2c, 0x2d, 0x31, 0x20, 0x64,
0x6f, 0x0a, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20,
0x73, 0x74, 0x72, 0x69, 0x70, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x70, 0x61,
0x72, 0x61, 0x6d, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x28, 0x74,
0x5b, 0x69, 0x5d, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x09, 0x09,
0x09, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x69, 0x0a, 0x09, 0x09,
0x09, 0x73, 0x74, 0x72, 0x69, 0x70, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75,
0x65, 0x0a, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x09, 0x09, 0x2d, 0x2d,
0x69, 0x66, 0x20, 0x73, 0x74, 0x72, 0x69, 0x70, 0x20, 0x74, 0x68, 0x65,
0x6e, 0x0a, 0x09, 0x09, 0x2d, 0x2d, 0x09, 0x74, 0x5b, 0x69, 0x5d, 0x20,
0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75,
0x62, 0x28, 0x74, 0x5b, 0x69, 0x5d, 0x2c, 0x20, 0x22, 0x3d, 0x2e, 0x2a,
0x24, 0x22, 0x2c, 0x20, 0x22, 0x22, 0x29, 0x0a, 0x09, 0x09, 0x2d, 0x2d,
0x65, 0x6e, 0x64, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x09, 0x72,
0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x2c, 0x73, 0x74, 0x72, 0x69,
0x70, 0x2c, 0x6c, 0x61, 0x73, 0x74, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x0a,
0x0a, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x74,
0x72, 0x69, 0x70, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73,
0x28, 0x73, 0x29, 0x0a, 0x0a, 0x09, 0x73, 0x20, 0x3d, 0x20, 0x73, 0x74,
0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75, 0x62, 0x28, 0x73, 0x2c,
0x20, 0x22, 0x5e, 0x25, 0x28, 0x22, 0x2c, 0x20, 0x22, 0x22, 0x29, 0x0a,
0x09, 0x73, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e,
0x67, 0x73, 0x75, 0x62, 0x28, 0x73, 0x2c, 0x20, 0x22, 0x25, 0x29, 0x24,
0x22, 0x2c, 0x20, 0x22, 0x22, 0x29, 0x0a, 0x0a, 0x09, 0x6c, 0x6f, 0x63,
0x61, 0x6c, 0x20, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x70, 0x6c, 0x69, 0x74,
0x5f, 0x63, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x28, 0x73, 0x2c,
0x20, 0x22, 0x2c, 0x22, 0x29, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
0x20, 0x73, 0x65, 0x70, 0x2c, 0x20, 0x72, 0x65, 0x74, 0x20, 0x3d, 0x20,
0x22, 0x22, 0x2c, 0x22, 0x22, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x69,
0x3d, 0x31, 0x2c, 0x74, 0x2e, 0x6e, 0x20, 0x64, 0x6f, 0x0a, 0x09, 0x09,
0x74, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e,
0x67, 0x2e, 0x67, 0x73, 0x75, 0x62, 0x28, 0x74, 0x5b, 0x69, 0x5d, 0x2c,
0x20, 0x22, 0x3d, 0x2e, 0x2a, 0x24, 0x22, 0x2c, 0x20, 0x22, 0x22, 0x29,
0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x74,
0x2e, 0x2e, 0x73, 0x65, 0x70, 0x2e, 0x2e, 0x74, 0x5b, 0x69, 0x5d, 0x0a,
0x09, 0x09, 0x73, 0x65, 0x70, 0x20, 0x3d, 0x20, 0x22, 0x2c, 0x22, 0x0a,
0x09, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72,
0x6e, 0x20, 0x22, 0x28, 0x22, 0x2e, 0x2e, 0x72, 0x65, 0x74, 0x2e, 0x2e,
0x22, 0x29, 0x22, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x0a
};
unsigned int lua_function_lua_len = 14483;
unsigned int lua_function_lua_len = 14494;

View File

@ -58,7 +58,7 @@ function classContainer:hasvar ()
while self[i] do
if self[i]:isvariable() then
return 1
end
end
i = i+1
end
return 0
@ -568,7 +568,7 @@ function classContainer:doparse (s)
-- Enumerate(name,body)
-- return strsub(s,e+1)
-- end
-- end
-- end
do
local b,e,body,name = strfind(s,"^%s*typedef%s+enum[^{]*(%b{})%s*([%w_][^%s]*)%s*;%s*")
@ -606,14 +606,14 @@ function classContainer:doparse (s)
-- try function
do
--local b,e,decl,arg,const = strfind(s,"^%s*([~_%w][_@%w%s%*&:<>]*[_%w])%s*(%b())%s*(c?o?n?s?t?)%s*=?%s*0?%s*;%s*")
local b,e,decl,arg,const,virt = strfind(s,"^%s*([^%(\n]+)%s*(%b())%s*(c?o?n?s?t?)%s*(=?%s*0?)%s*;%s*")
local b,e,decl,arg,const,virt = strfind(s,"^%s*([^%(\n]+)%s*(%b())%s*(c?o?n?s?t?)v?e?r?r?i?d?e?%s*o?v?e?r?r?i?d?e?%s*(=?%s*0?)%s*;%s*")
if not b then
-- try function with template
b,e,decl,arg,const = strfind(s,"^%s*([~_%w][_@%w%s%*&:<>]*[_%w]%b<>)%s*(%b())%s*(c?o?n?s?t?)%s*=?%s*0?%s*;%s*")
b,e,decl,arg,const = strfind(s,"^%s*([~_%w][_@%w%s%*&:<>]*[_%w]%b<>)%s*(%b())%s*(c?o?n?s?t?)v?e?r?r?i?d?e?%s*o?v?e?r?r?i?d?e?%s*=?%s*0?%s*;%s*")
end
if not b then
-- try a single letter function name
b,e,decl,arg,const = strfind(s,"^%s*([_%w])%s*(%b())%s*(c?o?n?s?t?)%s*;%s*")
b,e,decl,arg,const = strfind(s,"^%s*([_%w])%s*(%b())%s*(c?o?n?s?t?)v?e?r?r?i?d?e?%s*o?v?e?r?r?i?d?e?%s*;%s*")
end
if not b then
-- try function pointer
@ -629,6 +629,9 @@ function classContainer:doparse (s)
end
end
_curr_code = strsub(s,b,e)
if const == 'o' then
const = ''
end
Function(decl,arg,const)
return strsub(s,e+1)
end
@ -636,14 +639,17 @@ function classContainer:doparse (s)
-- try inline function
do
local b,e,decl,arg,const = strfind(s,"^%s*([^%(\n]+)%s*(%b())%s*(c?o?n?s?t?)[^;{]*%b{}%s*;?%s*")
local b,e,decl,arg,const = strfind(s,"^%s*([^%(\n]+)%s*(%b())%s*(c?o?n?s?t?)v?e?r?r?i?d?e?%s*o?v?e?r?r?i?d?e?[^;{]*%b{}%s*;?%s*")
--local b,e,decl,arg,const = strfind(s,"^%s*([~_%w][_@%w%s%*&:<>]*[_%w>])%s*(%b())%s*(c?o?n?s?t?)[^;]*%b{}%s*;?%s*")
if not b then
-- try a single letter function name
b,e,decl,arg,const = strfind(s,"^%s*([_%w])%s*(%b())%s*(c?o?n?s?t?).-%b{}%s*;?%s*")
b,e,decl,arg,const = strfind(s,"^%s*([_%w])%s*(%b())%s*(c?o?n?s?t?)v?e?r?r?i?d?e?%s*o?v?e?r?r?i?d?e?.-%b{}%s*;?%s*")
end
if b then
_curr_code = strsub(s,b,e)
if const == 'o' then
const = ''
end
Function(decl,arg,const)
return strsub(s,e+1)
end

View File

@ -458,9 +458,8 @@ end
-- Internal constructor
function _Function (t)
setmetatable(t,classFunction)
if t.const ~= 'const' and t.const ~= '' then
error("#invalid 'const' specification")
error("#invalid 'const' specification: " .. t.const)
end
append(t)
@ -489,7 +488,6 @@ end
function Function (d,a,c)
--local t = split(strsub(a,2,-2),',') -- eliminate braces
--local t = split_params(strsub(a,2,-2))
if not flags['W'] and string.find(a, "%.%.%.%s*%)") then
warning("Functions with variable arguments (`...') are not supported. Ignoring "..d..a..c)

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../../src/")
file(GLOB SOURCE
"*.c"
"*.h"
)
if(NOT TARGET zlib)

View File

@ -1,267 +0,0 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Authenticator.h"
#include "OSSupport/BlockingTCPLink.h"
#include "Root.h"
#include "Server.h"
#include "inifile/iniFile.h"
#include <sstream>
#define DEFAULT_AUTH_SERVER "session.minecraft.net"
#define DEFAULT_AUTH_ADDRESS "/game/checkserver.jsp?user=%USERNAME%&serverId=%SERVERID%"
#define MAX_REDIRECTS 10
cAuthenticator::cAuthenticator(void) :
super("cAuthenticator"),
m_Server(DEFAULT_AUTH_SERVER),
m_Address(DEFAULT_AUTH_ADDRESS),
m_ShouldAuthenticate(true)
{
}
cAuthenticator::~cAuthenticator()
{
Stop();
}
void cAuthenticator::ReadINI(cIniFile & IniFile)
{
m_Server = IniFile.GetValueSet("Authentication", "Server", DEFAULT_AUTH_SERVER);
m_Address = IniFile.GetValueSet("Authentication", "Address", DEFAULT_AUTH_ADDRESS);
m_ShouldAuthenticate = IniFile.GetValueSetB("Authentication", "Authenticate", true);
}
void cAuthenticator::Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash)
{
if (!m_ShouldAuthenticate)
{
cRoot::Get()->AuthenticateUser(a_ClientID);
return;
}
cCSLock Lock(m_CS);
m_Queue.push_back(cUser(a_ClientID, a_UserName, a_ServerHash));
m_QueueNonempty.Set();
}
void cAuthenticator::Start(cIniFile & IniFile)
{
ReadINI(IniFile);
m_ShouldTerminate = false;
super::Start();
}
void cAuthenticator::Stop(void)
{
m_ShouldTerminate = true;
m_QueueNonempty.Set();
Wait();
}
void cAuthenticator::Execute(void)
{
for (;;)
{
cCSLock Lock(m_CS);
while (!m_ShouldTerminate && (m_Queue.size() == 0))
{
cCSUnlock Unlock(Lock);
m_QueueNonempty.Wait();
}
if (m_ShouldTerminate)
{
return;
}
ASSERT(!m_Queue.empty());
int ClientID = m_Queue.front().m_ClientID;
AString UserName = m_Queue.front().m_Name;
AString ActualAddress = m_Address;
ReplaceString(ActualAddress, "%USERNAME%", UserName);
ReplaceString(ActualAddress, "%SERVERID%", m_Queue.front().m_ServerID);
m_Queue.pop_front();
Lock.Unlock();
if (!AuthFromAddress(m_Server, ActualAddress, UserName))
{
cRoot::Get()->KickUser(ClientID, "Failed to authenticate account!");
}
else
{
cRoot::Get()->AuthenticateUser(ClientID);
}
} // for (-ever)
}
bool cAuthenticator::AuthFromAddress(const AString & a_Server, const AString & a_Address, const AString & a_UserName, int a_Level /* = 1 */)
{
// Returns true if the user authenticated okay, false on error; iLevel is the recursion deptht (bails out if too deep)
cBlockingTCPLink Link;
if (!Link.Connect(a_Server.c_str(), 80))
{
LOGWARNING("%s: cannot connect to auth server \"%s\", kicking user \"%s\"",
__FUNCTION__, a_Server.c_str(), a_UserName.c_str()
);
return false;
}
Link.SendMessage( AString( "GET " + a_Address + " HTTP/1.1\r\n" ).c_str());
Link.SendMessage( AString( "User-Agent: MCServer\r\n" ).c_str());
Link.SendMessage( AString( "Host: " + a_Server + "\r\n" ).c_str());
//Link.SendMessage( AString( "Host: session.minecraft.net\r\n" ).c_str());
Link.SendMessage( AString( "Accept: */*\r\n" ).c_str());
Link.SendMessage( AString( "Connection: close\r\n" ).c_str()); //Close so we don´t have to mess with the Content-Length :)
Link.SendMessage( AString( "\r\n" ).c_str());
AString DataRecvd;
Link.ReceiveData(DataRecvd);
Link.CloseSocket();
std::stringstream ss(DataRecvd);
// Parse the data received:
std::string temp;
ss >> temp;
bool bRedirect = false;
bool bOK = false;
if ((temp.compare("HTTP/1.1") == 0) || (temp.compare("HTTP/1.0") == 0))
{
int code;
ss >> code;
if (code == 302)
{
// redirect blabla
LOGD("%s: Need to redirect, current level %d!", __FUNCTION__, a_Level);
if (a_Level > MAX_REDIRECTS)
{
LOGERROR("cAuthenticator: received too many levels of redirection from auth server \"%s\" for user \"%s\", bailing out and kicking the user", a_Server.c_str(), a_UserName.c_str());
return false;
}
bRedirect = true;
}
else if (code == 200)
{
LOGD("cAuthenticator: Received status 200 OK! :D");
bOK = true;
}
}
else
{
LOGERROR("cAuthenticator: cannot parse auth reply from server \"%s\" for user \"%s\", kicking the user.", a_Server.c_str(), a_UserName.c_str());
return false;
}
if( bRedirect )
{
AString Location;
// Search for "Location:"
bool bFoundLocation = false;
while( !bFoundLocation && ss.good() )
{
char c = 0;
while( c != '\n' )
{
ss.get( c );
}
AString Name;
ss >> Name;
if (Name.compare("Location:") == 0)
{
bFoundLocation = true;
ss >> Location;
}
}
if (!bFoundLocation)
{
LOGERROR("cAuthenticator: received invalid redirection from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str());
return false;
}
Location = Location.substr(strlen("http://"), std::string::npos); // Strip http://
std::string Server = Location.substr( 0, Location.find( "/" ) ); // Only leave server address
Location = Location.substr( Server.length(), std::string::npos);
return AuthFromAddress(Server, Location, a_UserName, a_Level + 1);
}
if (!bOK)
{
LOGERROR("cAuthenticator: received an error from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str());
return false;
}
// Header says OK, so receive the rest.
// Go past header, double \n means end of headers
char c = 0;
while (ss.good())
{
while (c != '\n')
{
ss.get(c);
}
ss.get(c);
if( c == '\n' || c == '\r' || ss.peek() == '\r' || ss.peek() == '\n' )
break;
}
if (!ss.good())
{
LOGERROR("cAuthenticator: error while parsing response body from auth server \"%s\" for user \"%s\", kicking user.", a_Server.c_str(), a_UserName.c_str());
return false;
}
std::string Result;
ss >> Result;
LOGD("cAuthenticator: Authentication result was %s", Result.c_str());
if (Result.compare("YES") == 0) //Works well
{
LOGINFO("Authentication result \"YES\", player authentication success!");
return true;
}
LOGINFO("Authentication result was \"%s\", player authentication failure!", Result.c_str());
return false;
}

Binary file not shown.

View File

@ -0,0 +1,116 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "BeaconEntity.h"
#include "../BlockArea.h"
cBeaconEntity::cBeaconEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
super(E_BLOCK_BEACON, a_BlockX, a_BlockY, a_BlockZ, a_World)
{
}
int cBeaconEntity::GetPyramidLevel(void)
{
cBlockArea Area;
int MinY = GetPosY() - 4;
if (MinY < 0)
{
MinY = 0;
}
int MaxY = GetPosY() - 1;
if (MaxY < 0)
{
MaxY = 0;
}
Area.Read(
m_World,
GetPosX() - 4, GetPosX() + 4,
MinY, MaxY,
GetPosZ() - 4, GetPosZ() + 4,
cBlockArea::baTypes
);
int Layer = 1;
int MiddleXZ = 4;
for (int Y = Area.GetSizeY() - 1; Y > 0; Y--)
{
for (int X = MiddleXZ - Layer; X <= (MiddleXZ + Layer); X++)
{
for (int Z = MiddleXZ - Layer; Z <= (MiddleXZ + Layer); Z++)
{
if (!IsMineralBlock(Area.GetRelBlockType(X, Y, Z)))
{
return Layer - 1;
}
}
}
Layer++;
}
return Layer - 1;
}
bool cBeaconEntity::IsMineralBlock(BLOCKTYPE a_BlockType)
{
switch(a_BlockType)
{
case E_BLOCK_DIAMOND_BLOCK:
case E_BLOCK_GOLD_BLOCK:
case E_BLOCK_IRON_BLOCK:
case E_BLOCK_EMERALD_BLOCK:
{
return true;
}
}
return false;
}
bool cBeaconEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
return false;
}
void cBeaconEntity::SaveToJson(Json::Value& a_Value)
{
}
void cBeaconEntity::SendTo(cClientHandle & a_Client)
{
}
void cBeaconEntity::UsedBy(cPlayer * a_Player)
{
}

View File

@ -0,0 +1,44 @@
#pragma once
#include "BlockEntity.h"
namespace Json
{
class Value;
}
class cBeaconEntity :
public cBlockEntity
{
typedef cBlockEntity super;
public:
/** The initial constructor */
cBeaconEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
/** Returns the amount of layers the pyramid below the beacon has. */
int GetPyramidLevel(void);
/** Returns true if the block is a diamond block, a golden block, an iron block or an emerald block. */
static bool IsMineralBlock(BLOCKTYPE a_BlockType);
// cBlockEntity overrides:
virtual void SaveToJson(Json::Value& a_Value ) override;
virtual void SendTo(cClientHandle & a_Client) override;
virtual void UsedBy(cPlayer * a_Player) override;
virtual bool Tick(float a_Dt, cChunk & /* a_Chunk */) override;
} ;

View File

@ -4,6 +4,7 @@
// Implements the cBlockEntity class that is the common ancestor for all block entities
#include "Globals.h"
#include "BeaconEntity.h"
#include "BlockEntity.h"
#include "ChestEntity.h"
#include "CommandBlockEntity.h"
@ -26,6 +27,7 @@ cBlockEntity * cBlockEntity::CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE
{
switch (a_BlockType)
{
case E_BLOCK_BEACON: return new cBeaconEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_CHEST: return new cChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_COMMAND_BLOCK: return new cCommandBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_World);
case E_BLOCK_DISPENSER: return new cDispenserEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);

View File

@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../")
file(GLOB SOURCE
"*.cpp"
"*.h"
)
add_library(BlockEntities ${SOURCE})

View File

@ -128,10 +128,11 @@ void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
if (DispChunk->GetBlock(DispX, DispY, DispZ) == E_BLOCK_AIR)
{
DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_FIRE, 0);
m_Contents.SetSlot(a_SlotNum, m_Contents.GetSlot(a_SlotNum).m_ItemType, m_Contents.GetSlot(a_SlotNum).m_ItemCount, m_Contents.GetSlot(a_SlotNum).m_ItemDamage + 1);
// If the durability has run out destroy the item.
if (m_Contents.GetSlot(a_SlotNum).m_ItemDamage > 64)
{
bool ItemBroke = m_Contents.DamageItem(a_SlotNum, 1);
if (ItemBroke)
{
m_Contents.ChangeSlotCount(a_SlotNum, -1);
}
}

View File

@ -413,19 +413,20 @@ bool cFurnaceEntity::CanCookInputToOutput(void) const
return false;
}
if (m_Contents.GetSlot(fsOutput).IsEmpty())
const cItem & Slot = m_Contents.GetSlot(fsOutput);
if (Slot.IsEmpty())
{
// The output is empty, can cook
return true;
}
if (!m_Contents.GetSlot(fsOutput).IsEqual(*m_CurrentRecipe->Out))
if (!Slot.IsEqual(*m_CurrentRecipe->Out))
{
// The output slot is blocked with something that cannot be stacked with the recipe's output
return false;
}
if (m_Contents.GetSlot(fsOutput).IsFullStack())
if (Slot.IsFullStack())
{
// Cannot add any more items to the output slot
return false;

View File

@ -234,24 +234,27 @@ bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick)
bool TrySuckPickupIn(cPickup * a_Pickup)
{
cItem & Item = a_Pickup->GetItem();
for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
{
if (m_Contents.IsSlotEmpty(i))
{
m_bFoundPickupsAbove = true;
m_Contents.SetSlot(i, a_Pickup->GetItem());
m_Contents.SetSlot(i, Item);
a_Pickup->Destroy(); // Kill pickup
return true;
}
else if (m_Contents.GetSlot(i).IsEqual(a_Pickup->GetItem()) && !m_Contents.GetSlot(i).IsFullStack())
else if (m_Contents.GetSlot(i).IsEqual(Item) && !m_Contents.GetSlot(i).IsFullStack())
{
m_bFoundPickupsAbove = true;
int PreviousCount = m_Contents.GetSlot(i).m_ItemCount;
a_Pickup->GetItem().m_ItemCount -= m_Contents.ChangeSlotCount(i, a_Pickup->GetItem().m_ItemCount) - PreviousCount; // Set count to however many items were added
if (a_Pickup->GetItem().IsEmpty())
Item.m_ItemCount -= m_Contents.ChangeSlotCount(i, Item.m_ItemCount) - PreviousCount; // Set count to however many items were added
if (Item.IsEmpty())
{
a_Pickup->Destroy(); // Kill pickup if all items were added
}

View File

@ -503,6 +503,10 @@ enum
E_META_PLANKS_CONIFER = 1,
E_META_PLANKS_BIRCH = 2,
E_META_PLANKS_JUNGLE = 3,
// E_BLOCK_(XXX_WEIGHTED)_PRESSURE_PLATE metas:
E_META_PRESSURE_PLATE_RAISED = 0,
E_META_PRESSURE_PLATE_DEPRESSED = 1,
// E_BLOCK_QUARTZ_BLOCK metas:
E_META_QUARTZ_NORMAL = 0,

131
src/Blocks/BlockBigFlower.h Normal file
View File

@ -0,0 +1,131 @@
#pragma once
#include "BlockHandler.h"
class cBlockBigFlowerHandler :
public cBlockHandler
{
public:
typedef cBlockHandler super;
cBlockBigFlowerHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType)
{
}
virtual void DropBlock(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_BlockPluginInterface, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ) override
{
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
if (Meta & 0x8)
{
super::DropBlock(a_ChunkInterface, a_WorldInterface, a_BlockPluginInterface, a_Digger, a_BlockX, a_BlockY - 1, a_BlockZ);
}
else
{
super::DropBlock(a_ChunkInterface, a_WorldInterface, a_BlockPluginInterface, a_Digger, a_BlockX, a_BlockY, a_BlockZ);
}
}
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{
NIBBLETYPE Meta = a_BlockMeta & 0x7;
if ((Meta == 2) || (Meta == 3))
{
return;
}
a_Pickups.push_back(cItem(E_BLOCK_BIG_FLOWER, 1, Meta));
}
virtual void OnDestroyedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override
{
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
if (Meta & 0x8)
{
Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ);
}
NIBBLETYPE FlowerMeta = Meta & 0x7;
if (!a_Player->IsGameModeCreative())
{
if (((FlowerMeta == 2) || (FlowerMeta == 3)) && (a_Player->GetEquippedItem().m_ItemType == E_ITEM_SHEARS))
{
MTRand r1;
if (r1.randInt(10) == 5)
{
cItems Pickups;
if (FlowerMeta == 2)
{
Pickups.Add(E_BLOCK_TALL_GRASS, 2, 1);
}
else if (FlowerMeta == 3)
{
Pickups.Add(E_BLOCK_TALL_GRASS, 2, 2);
}
a_WorldInterface.SpawnItemPickups(Pickups, a_BlockX, a_BlockY, a_BlockZ);
}
a_Player->UseEquippedItem();
}
}
}
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
{
return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR) && (a_RelY < cChunkDef::Height) && ((a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ) == E_BLOCK_AIR) || (a_Chunk.GetBlock(a_RelX, a_RelY + 1, a_RelZ) == E_BLOCK_BIG_FLOWER)));
}
virtual void OnPlacedByPlayer(
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,
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
) override
{
int Meta = (((int)floor(a_Player->GetYaw() * 4.0 / 360.0 + 0.5) & 0x3) + 2) % 4;
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, m_BlockType, 0x8 | Meta);
}
virtual void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override
{
NIBBLETYPE OldMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
if (OldMeta & 0x8)
{
// Was upper part of flower
if (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) == m_BlockType)
{
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
}
}
else
{
// Was lower part
if (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ) == m_BlockType)
{
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_AIR, 0);
}
}
}
virtual const char * GetStepSound(void) override
{
return "step.grass";
}
} ;

View File

@ -56,6 +56,7 @@ public:
(Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST)
)
{
// FIXME: This is unreachable, as the condition is the same as the above one
a_BlockMeta = (yaw < 0) ? 4 : 5;
return true;
}

View File

@ -0,0 +1,37 @@
#pragma once
#include "BlockHandler.h"
#include "../UI/Window.h"
#include "../Entities/Player.h"
class cBlockEnchantmentTableHandler :
public cBlockHandler
{
public:
cBlockEnchantmentTableHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType)
{
}
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
{
cWindow * Window = new cEnchantingWindow(a_BlockX, a_BlockY, a_BlockZ);
a_Player->OpenWindow(Window);
}
virtual bool IsUseable(void) override
{
return true;
}
};

View File

@ -8,6 +8,7 @@
#include "../Chunk.h"
#include "BlockAnvil.h"
#include "BlockBed.h"
#include "BlockBigFlower.h"
#include "BlockBrewingStand.h"
#include "BlockButton.h"
#include "BlockCactus.h"
@ -24,6 +25,7 @@
#include "BlockDirt.h"
#include "BlockDoor.h"
#include "BlockDropSpenser.h"
#include "BlockEnchantmentTable.h"
#include "BlockEnderchest.h"
#include "BlockEntity.h"
#include "BlockFarmland.h"
@ -90,6 +92,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType)
case E_BLOCK_ACTIVATOR_RAIL: return new cBlockRailHandler (a_BlockType);
case E_BLOCK_ANVIL: return new cBlockAnvilHandler (a_BlockType);
case E_BLOCK_BED: return new cBlockBedHandler (a_BlockType);
case E_BLOCK_BIG_FLOWER: return new cBlockBigFlowerHandler (a_BlockType);
case E_BLOCK_BIRCH_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType);
case E_BLOCK_BREWING_STAND: return new cBlockBrewingStandHandler (a_BlockType);
case E_BLOCK_BRICK_STAIRS: return new cBlockStairsHandler (a_BlockType);
@ -117,6 +120,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType)
case E_BLOCK_DOUBLE_WOODEN_SLAB: return new cBlockDoubleSlabHandler (a_BlockType);
case E_BLOCK_DROPPER: return new cBlockDropSpenserHandler (a_BlockType);
case E_BLOCK_EMERALD_ORE: return new cBlockOreHandler (a_BlockType);
case E_BLOCK_ENCHANTMENT_TABLE: return new cBlockEnchantmentTableHandler(a_BlockType);
case E_BLOCK_ENDER_CHEST: return new cBlockEnderchestHandler (a_BlockType);
case E_BLOCK_FARMLAND: return new cBlockFarmlandHandler ( );
case E_BLOCK_FENCE_GATE: return new cBlockFenceGateHandler (a_BlockType);

View File

@ -16,6 +16,7 @@
{ \
case E_BLOCK_LEAVES: a_Area.SetBlockType(x, y, z, (BLOCKTYPE)(E_BLOCK_SPONGE + i + 1)); break; \
case E_BLOCK_LOG: return true; \
case E_BLOCK_NEW_LEAVES: a_Area.SetBlockType(x, y, z, (BLOCKTYPE)(E_BLOCK_SPONGE + i + 1)); break; \
case E_BLOCK_NEW_LOG: return true; \
}

View File

@ -141,7 +141,7 @@ public:
NIBBLETYPE Meta = 0;
char RailsCnt = 0;
bool Neighbors[8]; // 0 - EAST, 1 - WEST, 2 - NORTH, 3 - SOUTH, 4 - EAST UP, 5 - WEST UP, 6 - NORTH UP, 7 - SOUTH UP
memset(Neighbors, false, sizeof(Neighbors));
memset(Neighbors, 0, sizeof(Neighbors));
Neighbors[0] = (IsUnstable(a_ChunkInterface, a_BlockX + 1, a_BlockY, a_BlockZ) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_EAST, E_PURE_DOWN));
Neighbors[1] = (IsUnstable(a_ChunkInterface, a_BlockX - 1, a_BlockY, a_BlockZ) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_WEST, E_PURE_DOWN));
Neighbors[2] = (IsUnstable(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ - 1) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_DOWN));

View File

@ -31,7 +31,7 @@ public:
}
static char RotationToMetaData(double a_Rotation)
static NIBBLETYPE RotationToMetaData(double a_Rotation)
{
a_Rotation += 180 + (180 / 16); // So it's not aligned with axis
if (a_Rotation > 360)
@ -45,7 +45,7 @@ public:
}
static char DirectionToMetaData(eBlockFace a_Direction)
static NIBBLETYPE DirectionToMetaData(eBlockFace a_Direction)
{
switch (a_Direction)
{

View File

@ -15,14 +15,14 @@ public:
: cBlockHandler(a_BlockType)
{
}
virtual bool DoesIgnoreBuildCollision(void) override
{
return true;
}
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{
// Drop seeds, sometimes
@ -34,11 +34,25 @@ public:
}
virtual void OnDestroyedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override
{
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
if ((!a_Player->IsGameModeCreative()) && (a_Player->GetEquippedItem().m_ItemType == E_ITEM_SHEARS))
{
cItems Pickups;
Pickups.Add(E_BLOCK_TALL_GRASS, 1, Meta);
a_WorldInterface.SpawnItemPickups(Pickups, a_BlockX, a_BlockY, a_BlockZ);
}
a_Player->UseEquippedItem();
}
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
{
return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR));
}
virtual const char * GetStepSound(void) override
{

View File

@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../")
file(GLOB SOURCE
"*.cpp"
"*.h"
)
add_library(Blocks ${SOURCE})

View File

@ -288,7 +288,7 @@ bool cBoundingBox::CalcLineIntersection(const Vector3d & a_Min, const Vector3d &
Coeff = c;
}
c = a_Line1.LineCoeffToXZPlane(a_Line2, a_Max.y);
if ((c >= 0) && (c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
if ((c >= 0) && (c < Coeff) && IsInside(a_Min, a_Max, a_Line1 + (a_Line2 - a_Line1) * c))
{
Face = (a_Line1.y > a_Line2.y) ? BLOCK_FACE_YP : BLOCK_FACE_YM;
Coeff = c;

View File

@ -143,7 +143,7 @@ protected:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cByteBuffer:
cByteBuffer::cByteBuffer(int a_BufferSize) :
cByteBuffer::cByteBuffer(size_t a_BufferSize) :
m_Buffer(new char[a_BufferSize + 1]),
m_BufferSize(a_BufferSize + 1),
#ifdef _DEBUG

View File

@ -27,7 +27,7 @@ their own synchronization.
class cByteBuffer
{
public:
cByteBuffer(int a_BufferSize);
cByteBuffer(size_t a_BufferSize);
~cByteBuffer();
/// Writes the bytes specified to the ringbuffer. Returns true if successful, false if not

View File

@ -123,6 +123,7 @@ if (NOT MSVC)
file(GLOB SOURCE
"*.cpp"
"*.h"
)
list(REMOVE_ITEM SOURCE "${PROJECT_SOURCE_DIR}/StackWalker.cpp" "${PROJECT_SOURCE_DIR}/LeakFinder.cpp")

View File

@ -268,7 +268,7 @@ void cChunk::SetAllData(
const HeightMap * a_HeightMap,
const BiomeMap & a_BiomeMap,
cBlockEntityList & a_BlockEntities
)
)
{
memcpy(m_BiomeMap, a_BiomeMap, sizeof(m_BiomeMap));
@ -448,7 +448,7 @@ void cChunk::CollectMobCensus(cMobCensus& toFill)
{
cMonster& Monster = (cMonster&)(**itr);
currentPosition = Monster.GetPosition();
for (std::list<const Vector3d*>::const_iterator itr2 = playerPositions.begin(); itr2 != playerPositions.end(); itr2 ++)
for (std::list<const Vector3d*>::const_iterator itr2 = playerPositions.begin(); itr2 != playerPositions.end(); ++itr2)
{
toFill.CollectMob(Monster,*this,(currentPosition-**itr2).SqrLength());
}
@ -596,7 +596,7 @@ void cChunk::Tick(float a_Dt)
delete ToDelete;
continue;
}
itr++;
++itr;
} // for itr - m_Entitites[]
// If any entity moved out of the chunk, move it to the neighbor:
@ -746,7 +746,7 @@ void cChunk::BroadcastPendingBlockChanges(void)
void cChunk::CheckBlocks()
{
if (m_ToTickBlocks.size() == 0)
if (m_ToTickBlocks.empty())
{
return;
}
@ -1291,6 +1291,7 @@ void cChunk::CreateBlockEntities(void)
BLOCKTYPE BlockType = GetBlock(x, y, z);
switch (BlockType)
{
case E_BLOCK_BEACON:
case E_BLOCK_CHEST:
case E_BLOCK_COMMAND_BLOCK:
case E_BLOCK_DISPENSER:
@ -1419,6 +1420,7 @@ void cChunk::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType,
// If the new block is a block entity, create the entity object:
switch (a_BlockType)
{
case E_BLOCK_BEACON:
case E_BLOCK_CHEST:
case E_BLOCK_COMMAND_BLOCK:
case E_BLOCK_DISPENSER:
@ -1466,7 +1468,7 @@ void cChunk::QueueTickBlock(int a_RelX, int a_RelY, int a_RelZ)
return;
}
m_ToTickBlocks.push_back(Vector3i(a_RelX, a_RelY, a_RelZ));
m_ToTickBlocks.push_back(MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ));
}
@ -1529,7 +1531,7 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta));
}
m_ChunkBuffer.SetMeta(a_RelX, a_RelY, a_RelZ, a_BlockMeta);
SetNibble(m_BlockMeta, index, a_BlockMeta);
// ONLY recalculate lighting if it's necessary!
if (
@ -1552,7 +1554,7 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
{
for (int y = a_RelY - 1; y > 0; --y)
{
if (GetBlock(a_RelX, y, a_RelZ) != E_BLOCK_AIR)
if (GetBlock(MakeIndexNoCheck(a_RelX, y, a_RelZ)) != E_BLOCK_AIR)
{
m_HeightMap[a_RelX + a_RelZ * Width] = (unsigned char)y;
break;
@ -1569,16 +1571,18 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
void cChunk::SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client)
{
// The coords must be valid, because the upper level already does chunk lookup. No need to check them again.
// There's an debug-time assert in MakeIndexNoCheck anyway
unsigned int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
if (a_Client == NULL)
{
// Queue the block for all clients in the chunk (will be sent in Tick())
m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, GetBlock(a_RelX, a_RelY, a_RelZ), GetMeta(a_RelX, a_RelY, a_RelZ)));
m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, GetBlock(index), GetMeta(index)));
return;
}
Vector3i wp = PositionToWorldPosition(a_RelX, a_RelY, a_RelZ);
a_Client->SendBlockChange(wp.x, wp.y, wp.z, GetBlock(a_RelX, a_RelY, a_RelZ), GetMeta(a_RelX, a_RelY, a_RelZ));
a_Client->SendBlockChange(wp.x, wp.y, wp.z, GetBlock(index), GetMeta(index));
// FS #268 - if a BlockEntity digging is cancelled by a plugin, the entire block entity must be re-sent to the client:
for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), end = m_BlockEntities.end(); itr != end; ++itr)
@ -2456,10 +2460,11 @@ void cChunk::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_
void cChunk::GetBlockInfo(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight)
{
a_BlockType = GetBlock(a_RelX, a_RelY, a_RelZ);
a_Meta = m_ChunkBuffer.GetMeta(a_RelX, a_RelY, a_RelZ);
a_SkyLight = m_ChunkBuffer.GetSkyLight(a_RelX, a_RelY, a_RelZ);
a_BlockLight = m_ChunkBuffer.GetBlockLight(a_RelX, a_RelY, a_RelZ);
int Idx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
a_BlockType = GetBlock(Idx);
a_Meta = cChunkDef::GetNibble(m_BlockMeta, Idx);
a_SkyLight = cChunkDef::GetNibble(m_BlockSkyLight, Idx);
a_BlockLight = cChunkDef::GetNibble(m_BlockLight, Idx);
}
@ -2481,7 +2486,7 @@ cChunk * cChunk::GetNeighborChunk(int a_BlockX, int a_BlockZ)
cChunk * cChunk::GetRelNeighborChunk(int a_RelX, int a_RelZ)
{
// If the relative coords are too far away, use the parent's chunk lookup instead:
if ((a_RelX < 128) || (a_RelX > 128) || (a_RelZ < -128) || (a_RelZ > 128))
if ((a_RelX < -128) || (a_RelX > 128) || (a_RelZ < -128) || (a_RelZ > 128))
{
int BlockX = m_PosX * cChunkDef::Width + a_RelX;
int BlockZ = m_PosZ * cChunkDef::Width + a_RelZ;

View File

@ -386,14 +386,14 @@ private:
struct sSetBlockQueueItem
{
Int64 m_Tick;
int m_RelX, m_RelY, m_RelZ;
BLOCKTYPE m_BlockType;
NIBBLETYPE m_BlockMeta;
Int64 m_Tick;
BLOCKTYPE m_PreviousType;
sSetBlockQueueItem(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Int64 a_Tick, BLOCKTYPE a_PreviousBlockType) :
m_RelX(a_RelX), m_RelY(a_RelY), m_RelZ(a_RelZ), m_BlockType(a_BlockType), m_BlockMeta(a_BlockMeta), m_Tick(a_Tick), m_PreviousType(a_PreviousBlockType)
m_Tick(a_Tick), m_RelX(a_RelX), m_RelY(a_RelY), m_RelZ(a_RelZ), m_BlockType(a_BlockType), m_BlockMeta(a_BlockMeta), m_PreviousType(a_PreviousBlockType)
{
}
} ;

View File

@ -77,13 +77,19 @@ public:
idx = x + Width * z // Need to verify this with the protocol spec, currently unknown!
*/
typedef EMCSBiome BiomeMap[Width * Width];
/// The type used for block type operations and storage, AXIS_ORDER ordering
typedef BLOCKTYPE BlockTypes[NumBlocks];
/// The type used for block data in nibble format, AXIS_ORDER ordering
typedef NIBBLETYPE BlockNibbles[NumBlocks / 2];
/** The storage wrapper used for compressed blockdata residing in RAMz */
typedef std::vector<BLOCKTYPE> COMPRESSED_BLOCKTYPE;
/** The storage wrapper used for compressed nibbledata residing in RAMz */
typedef std::vector<NIBBLETYPE> COMPRESSED_NIBBLETYPE;
/// Converts absolute block coords into relative (chunk + block) coords:
inline static void AbsoluteToRelative(/* in-out */ int & a_X, int & a_Y, int & a_Z, /* out */ int & a_ChunkX, int & a_ChunkZ )
@ -219,24 +225,13 @@ public:
ASSERT((a_Z >= 0) && (a_Z <= Width));
a_BiomeMap[a_X + Width * a_Z] = a_Biome;
}
static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, int a_BlockIdx)
static NIBBLETYPE GetNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, int a_BlockIdx, bool a_IsSkyLightNibble = false)
{
if ((a_BlockIdx > -1) && (a_BlockIdx < NumBlocks))
{
return (a_Buffer[a_BlockIdx / 2] >> ((a_BlockIdx & 1) * 4)) & 0x0f;
}
ASSERT(!"cChunkDef::GetNibble(): index out of chunk range!");
return 0;
}
static NIBBLETYPE GetNibble(const std::vector<NIBBLETYPE> & a_Buffer, int a_BlockIdx, bool a_IsSkyLightNibble = false)
{
if ((a_BlockIdx > -1) && (a_BlockIdx < NumBlocks))
{
if (a_Buffer.empty() || ((size_t)(a_BlockIdx / 2) > a_Buffer.size() - 1))
if ((size_t)(a_BlockIdx / 2) >= a_Buffer.size())
{
return (a_IsSkyLightNibble ? 0xff : 0);
}
@ -245,29 +240,29 @@ public:
ASSERT(!"cChunkDef::GetNibble(): index out of chunk range!");
return 0;
}
static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, int x, int y, int z)
static NIBBLETYPE GetNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, int x, int y, int z, bool a_IsSkyLightNibble = false)
{
if ((x < Width) && (x > -1) && (y < Height) && (y > -1) && (z < Width) && (z > -1))
{
int Index = MakeIndexNoCheck(x, y, z);
return (a_Buffer[Index / 2] >> ((Index & 1) * 4)) & 0x0f;
if ((size_t)(Index / 2) >= a_Buffer.size())
{
return (a_IsSkyLightNibble ? 0xff : 0);
}
return ExpandNibble(a_Buffer, Index);
}
ASSERT(!"cChunkDef::GetNibble(): coords out of chunk range!");
return 0;
}
static NIBBLETYPE GetNibble(const std::vector<NIBBLETYPE> & a_Buffer, int x, int y, int z, bool a_IsSkyLightNibble = false)
static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, int x, int y, int z)
{
if ((x < Width) && (x > -1) && (y < Height) && (y > -1) && (z < Width) && (z > -1))
{
int Index = MakeIndexNoCheck(x, y, z);
if (a_Buffer.empty() || ((size_t)(Index / 2) > a_Buffer.size() - 1))
{
return (a_IsSkyLightNibble ? 0xff : 0);
}
return (a_Buffer[(size_t)(Index / 2)] >> ((Index & 1) * 4)) & 0x0f;
}
ASSERT(!"cChunkDef::GetNibble(): coords out of chunk range!");
@ -275,39 +270,22 @@ public:
}
static void SetNibble(NIBBLETYPE * a_Buffer, int a_BlockIdx, NIBBLETYPE a_Nibble)
static void SetNibble(COMPRESSED_NIBBLETYPE & a_Buffer, int a_BlockIdx, NIBBLETYPE a_Nibble)
{
if ((a_BlockIdx < 0) || (a_BlockIdx >= NumBlocks))
{
ASSERT(!"cChunkDef::SetNibble(): index out of range!");
return;
}
a_Buffer[a_BlockIdx / 2] = static_cast<NIBBLETYPE>(
(a_Buffer[a_BlockIdx / 2] & (0xf0 >> ((a_BlockIdx & 1) * 4))) | // The untouched nibble
((a_Nibble & 0x0f) << ((a_BlockIdx & 1) * 4)) // The nibble being set
);
}
static void SetNibble(std::vector<NIBBLETYPE> & a_Buffer, int a_BlockIdx, NIBBLETYPE a_Nibble)
{
if ((a_BlockIdx < 0) || (a_BlockIdx >= NumBlocks))
{
ASSERT(!"cChunkDef::SetNibble(): index out of range!");
return;
}
if (a_Buffer.empty() || ((size_t)(a_BlockIdx / 2) > a_Buffer.size() - 1))
if ((size_t)(a_BlockIdx / 2) >= a_Buffer.size())
{
a_Buffer.resize((size_t)((a_BlockIdx / 2) + 1));
}
a_Buffer[(size_t)(a_BlockIdx / 2)] = static_cast<NIBBLETYPE>(
(a_Buffer[a_BlockIdx / 2] & (0xf0 >> ((a_BlockIdx & 1) * 4))) | // The untouched nibble
((a_Nibble & 0x0f) << ((a_BlockIdx & 1) * 4)) // The nibble being set
);
a_Buffer[(size_t)(a_BlockIdx / 2)] = PackNibble(a_Buffer, a_BlockIdx, a_Nibble);
}
static void SetNibble(NIBBLETYPE * a_Buffer, int x, int y, int z, NIBBLETYPE a_Nibble)
static void SetNibble(COMPRESSED_NIBBLETYPE & a_Buffer, int x, int y, int z, NIBBLETYPE a_Nibble)
{
if (
(x >= Width) || (x < 0) ||
@ -320,48 +298,32 @@ public:
}
int Index = MakeIndexNoCheck(x, y, z);
a_Buffer[Index / 2] = static_cast<NIBBLETYPE>(
(a_Buffer[Index / 2] & (0xf0 >> ((Index & 1) * 4))) | // The untouched nibble
((a_Nibble & 0x0f) << ((Index & 1) * 4)) // The nibble being set
if ((size_t)(Index / 2) >= a_Buffer.size())
{
a_Buffer.resize((size_t)((Index / 2) + 1));
}
a_Buffer[(size_t)(Index / 2)] = PackNibble(a_Buffer, Index, a_Nibble);
}
private:
inline static NIBBLETYPE PackNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, int a_Index, NIBBLETYPE a_Nibble)
{
return static_cast<NIBBLETYPE>(
(a_Buffer[a_Index / 2] & (0xf0 >> ((a_Index & 1) * 4))) | // The untouched nibble
((a_Nibble & 0x0f) << ((a_Index & 1) * 4)) // The nibble being set
);
}
static void SetNibble(std::vector<NIBBLETYPE> & a_Buffer, int x, int y, int z, NIBBLETYPE a_Nibble)
inline static NIBBLETYPE ExpandNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, int a_Index)
{
if (
(x >= Width) || (x < 0) ||
(y >= Height) || (y < 0) ||
(z >= Width) || (z < 0)
)
{
ASSERT(!"cChunkDef::SetNibble(): index out of range!");
return;
}
int Index = MakeIndexNoCheck(x, y, z);
if (a_Buffer.empty() || ((size_t)(Index / 2) > a_Buffer.size() - 1))
{
a_Buffer.resize((size_t)((Index / 2) + 1));
}
a_Buffer[(size_t)(Index / 2)] = static_cast<NIBBLETYPE>(
(a_Buffer[Index / 2] & (0xf0 >> ((Index & 1) * 4))) | // The untouched nibble
((a_Nibble & 0x0f) << ((Index & 1) * 4)) // The nibble being set
);
return (a_Buffer[a_Index / 2] >> ((a_Index & 1) * 4)) & 0x0f;
}
inline static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos)
{
return GetNibble(a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z );
}
inline static void SetNibble(NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos, NIBBLETYPE a_Value)
{
SetNibble( a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, a_Value );
}
} ;

View File

@ -913,19 +913,21 @@ void cChunkMap::SetChunkData(
}
// Notify relevant ChunkStays:
for (cChunkStays::iterator itr = m_ChunkStays.begin(); itr != m_ChunkStays.end(); )
cChunkStays ToBeDisabled;
for (cChunkStays::iterator itr = m_ChunkStays.begin(), end = m_ChunkStays.end(); itr != end; ++itr)
{
if ((*itr)->ChunkAvailable(a_ChunkX, a_ChunkZ))
{
cChunkStays::iterator cur = itr;
++itr;
m_ChunkStays.erase(cur);
}
else
{
++itr;
// The chunkstay wants to be disabled, add it to a list of to-be-disabled chunkstays for later processing:
ToBeDisabled.push_back(*itr);
}
} // for itr - m_ChunkStays[]
// Disable (and possibly remove) the chunkstays that chose to get disabled:
for (cChunkStays::iterator itr = ToBeDisabled.begin(), end = ToBeDisabled.end(); itr != end; ++itr)
{
(*itr)->Disable();
}
}
// Notify plugins of the chunk becoming available
@ -1650,7 +1652,7 @@ void cChunkMap::AddEntity(cEntity * a_Entity)
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ());
if ((Chunk == NULL) && !Chunk->IsValid())
if ((Chunk == NULL) || !Chunk->IsValid())
{
LOGWARNING("Entity at %p (%s, ID %d) spawning in a non-existent chunk, the entity is lost.",
a_Entity, a_Entity->GetClass(), a_Entity->GetUniqueID()
@ -1685,7 +1687,7 @@ void cChunkMap::RemoveEntity(cEntity * a_Entity)
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ());
if ((Chunk == NULL) && !Chunk->IsValid())
if ((Chunk == NULL) || !Chunk->IsValid())
{
return;
}
@ -1717,7 +1719,7 @@ bool cChunkMap::ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
if ((Chunk == NULL) && !Chunk->IsValid())
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
}
@ -1967,7 +1969,7 @@ bool cChunkMap::ForEachBlockEntityInChunk(int a_ChunkX, int a_ChunkZ, cBlockEnti
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
if ((Chunk == NULL) && !Chunk->IsValid())
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
}
@ -1982,7 +1984,7 @@ bool cChunkMap::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback &
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
if ((Chunk == NULL) && !Chunk->IsValid())
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
}
@ -1997,7 +1999,7 @@ bool cChunkMap::ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCa
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
if ((Chunk == NULL) && !Chunk->IsValid())
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
}
@ -2012,7 +2014,7 @@ bool cChunkMap::ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallba
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
if ((Chunk == NULL) && !Chunk->IsValid())
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
}
@ -2027,7 +2029,7 @@ bool cChunkMap::ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpens
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
if ((Chunk == NULL) && !Chunk->IsValid())
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
}
@ -2042,7 +2044,7 @@ bool cChunkMap::ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallba
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
if ((Chunk == NULL) && !Chunk->IsValid())
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
}
@ -2060,7 +2062,7 @@ bool cChunkMap::DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cB
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
if ((Chunk == NULL) && !Chunk->IsValid())
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
}
@ -2078,7 +2080,7 @@ bool cChunkMap::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCa
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
if ((Chunk == NULL) && !Chunk->IsValid())
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
}
@ -2096,7 +2098,7 @@ bool cChunkMap::DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDis
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
if ((Chunk == NULL) && !Chunk->IsValid())
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
}
@ -2114,7 +2116,7 @@ bool cChunkMap::DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropp
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
if ((Chunk == NULL) && !Chunk->IsValid())
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
}
@ -2132,7 +2134,7 @@ bool cChunkMap::DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cD
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
if ((Chunk == NULL) && !Chunk->IsValid())
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
}
@ -2150,7 +2152,7 @@ bool cChunkMap::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurna
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
if ((Chunk == NULL) && !Chunk->IsValid())
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
}
@ -2167,7 +2169,7 @@ bool cChunkMap::DoWithNoteBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cNot
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
if ((Chunk == NULL) && !Chunk->IsValid())
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
}
@ -2184,7 +2186,7 @@ bool cChunkMap::DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, c
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
if ((Chunk == NULL) && !Chunk->IsValid())
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
}
@ -2202,7 +2204,7 @@ bool cChunkMap::DoWithMobHeadAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHe
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
if ((Chunk == NULL) && !Chunk->IsValid())
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
}
@ -2220,7 +2222,7 @@ bool cChunkMap::DoWithFlowerPotAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFlo
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
if ((Chunk == NULL) && !Chunk->IsValid())
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
}
@ -2238,7 +2240,7 @@ bool cChunkMap::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString &
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
if ((Chunk == NULL) && !Chunk->IsValid())
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
}
@ -2970,7 +2972,12 @@ void cChunkMap::AddChunkStay(cChunkStay & a_ChunkStay)
Chunk->Stay(true);
if (Chunk->IsValid())
{
a_ChunkStay.ChunkAvailable(itr->m_ChunkX, itr->m_ChunkZ);
if (a_ChunkStay.ChunkAvailable(itr->m_ChunkX, itr->m_ChunkZ))
{
// The chunkstay wants to be deactivated, disable it and bail out:
a_ChunkStay.Disable();
return;
}
}
} // for itr - WantedChunks[]
}
@ -3013,6 +3020,7 @@ void cChunkMap::DelChunkStay(cChunkStay & a_ChunkStay)
}
Chunk->Stay(false);
} // for itr - Chunks[]
a_ChunkStay.OnDisabled();
}

View File

@ -31,10 +31,7 @@ cChunkStay::~cChunkStay()
void cChunkStay::Clear(void)
{
if (m_ChunkMap != NULL)
{
Disable();
}
ASSERT(m_ChunkMap == NULL);
m_Chunks.clear();
}
@ -97,8 +94,9 @@ void cChunkStay::Disable(void)
{
ASSERT(m_ChunkMap != NULL);
m_ChunkMap->DelChunkStay(*this);
cChunkMap * ChunkMap = m_ChunkMap;
m_ChunkMap = NULL;
ChunkMap->DelChunkStay(*this);
}

View File

@ -36,8 +36,12 @@ class cChunkStay
{
public:
cChunkStay(void);
/** Deletes the object. Note that this calls Clear(), which means that the ChunkStay needs to be disabled. */
virtual ~cChunkStay();
/** Clears all the chunks that have been added.
To be used only while the ChunkStay object is not enabled. */
void Clear(void);
/** Adds a chunk to be locked from unloading.

View File

@ -24,13 +24,15 @@
#include "Root.h"
#include "Authenticator.h"
#include "Protocol/Authenticator.h"
#include "MersenneTwister.h"
#include "Protocol/ProtocolRecognizer.h"
#include "CompositeChat.h"
#include "Items/ItemSword.h"
#include "md5/md5.h"
/** Maximum number of explosions to send this tick, server will start dropping if exceeded */
@ -175,6 +177,39 @@ void cClientHandle::Destroy(void)
void cClientHandle::GenerateOfflineUUID(void)
{
m_UUID = GenerateOfflineUUID(m_Username);
}
AString cClientHandle::GenerateOfflineUUID(const AString & a_Username)
{
// Proper format for a version 3 UUID is:
// xxxxxxxx-xxxx-3xxx-yxxx-xxxxxxxxxxxx where x is any hexadecimal digit and y is one of 8, 9, A, or B
// Generate an md5 checksum, and use it as base for the ID:
MD5 Checksum(a_Username);
AString UUID = Checksum.hexdigest();
UUID[12] = '3'; // Version 3 UUID
UUID[16] = '8'; // Variant 1 UUID
// Now the digest doesn't have the UUID slashes, but the client requires them, so add them into the appropriate positions:
UUID.insert(8, "-");
UUID.insert(13, "-");
UUID.insert(18, "-");
UUID.insert(23, "-");
return UUID;
}
void cClientHandle::Kick(const AString & a_Reason)
{
if (m_State >= csAuthenticating) // Don't log pings
@ -188,7 +223,7 @@ void cClientHandle::Kick(const AString & a_Reason)
void cClientHandle::Authenticate(void)
void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID)
{
if (m_State != csAuthenticating)
{
@ -197,6 +232,12 @@ void cClientHandle::Authenticate(void)
ASSERT( m_Player == NULL );
m_Username = a_Name;
m_UUID = a_UUID;
// Send login success (if the protocol supports it):
m_Protocol->SendLoginSuccess();
// Spawn player (only serversided, so data is loaded)
m_Player = new cPlayer(this, GetUsername());
@ -412,14 +453,16 @@ void cClientHandle::HandlePing(void)
{
// Somebody tries to retrieve information about the server
AString Reply;
const cServer & Server = *cRoot::Get()->GetServer();
Printf(Reply, "%s%s%i%s%i",
cRoot::Get()->GetServer()->GetDescription().c_str(),
Server.GetDescription().c_str(),
cChatColor::Delimiter.c_str(),
cRoot::Get()->GetServer()->GetNumPlayers(),
Server.GetNumPlayers(),
cChatColor::Delimiter.c_str(),
cRoot::Get()->GetServer()->GetMaxPlayers()
Server.GetMaxPlayers()
);
Kick(Reply.c_str());
Kick(Reply);
}
@ -994,7 +1037,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
{
HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler);
}
else if (ItemHandler->IsFood())
else if (ItemHandler->IsFood() && !m_Player->IsGameModeCreative())
{
if (m_Player->IsSatiated())
{
@ -1175,8 +1218,8 @@ void cClientHandle::HandleChat(const AString & a_Message)
Color = AString("@") + Color[2];
}
else
{
Color.empty();
{
Color.clear();
}
Msg.AddTextPart(AString("<") + m_Player->GetName() + "> ", Color);
Msg.ParseText(a_Message);
@ -1532,7 +1575,7 @@ void cClientHandle::HandleTabCompletion(const AString & a_Text)
void cClientHandle::SendData(const char * a_Data, int a_Size)
void cClientHandle::SendData(const char * a_Data, size_t a_Size)
{
if (m_HasSentDC)
{
@ -1547,7 +1590,7 @@ void cClientHandle::SendData(const char * a_Data, int a_Size)
if (m_OutgoingDataOverflow.empty())
{
// No queued overflow data; if this packet fits into the ringbuffer, put it in, otherwise put it in the overflow buffer:
int CanFit = m_OutgoingData.GetFreeSpace();
size_t CanFit = m_OutgoingData.GetFreeSpace();
if (CanFit > a_Size)
{
CanFit = a_Size;
@ -1677,13 +1720,16 @@ void cClientHandle::Tick(float a_Dt)
}
// Send a ping packet:
cTimer t1;
if ((m_LastPingTime + cClientHandle::PING_TIME_MS <= t1.GetNowTime()))
if (m_State == csPlaying)
{
m_PingID++;
m_PingStartTime = t1.GetNowTime();
m_Protocol->SendKeepAlive(m_PingID);
m_LastPingTime = m_PingStartTime;
cTimer t1;
if ((m_LastPingTime + cClientHandle::PING_TIME_MS <= t1.GetNowTime()))
{
m_PingID++;
m_PingStartTime = t1.GetNowTime();
m_Protocol->SendKeepAlive(m_PingID);
m_LastPingTime = m_PingStartTime;
}
}
// Handle block break animation:
@ -2101,7 +2147,7 @@ void cClientHandle::SendExplosion(double a_BlockX, double a_BlockY, double a_Blo
}
// Update the statistics:
m_NumExplosionsThisTick += 1;
m_NumExplosionsThisTick++;
m_Protocol->SendExplosion(a_BlockX, a_BlockY, a_BlockZ, a_Radius, a_BlocksAffected, a_PlayerMotion);
}
@ -2633,7 +2679,7 @@ void cClientHandle::PacketError(unsigned char a_PacketType)
void cClientHandle::DataReceived(const char * a_Data, int a_Size)
void cClientHandle::DataReceived(const char * a_Data, size_t a_Size)
{
// Data is received from the client, store it in the buffer to be processed by the Tick thread:
m_TimeSinceLastPacket = 0;
@ -2685,4 +2731,27 @@ void cClientHandle::SocketClosed(void)
void cClientHandle::HandleEnchantItem(Byte & WindowID, Byte & Enchantment)
{
cEnchantingWindow * Window = (cEnchantingWindow*)m_Player->GetWindow();
cItem Item = *Window->m_SlotArea->GetSlot(0, *m_Player);
int BaseEnchantmentLevel = Window->GetPropertyValue(Enchantment);
if (Item.EnchantByXPLevels(BaseEnchantmentLevel))
{
if (m_Player->IsGameModeCreative() || m_Player->DeltaExperience(-m_Player->XpForLevel(BaseEnchantmentLevel)) >= 0)
{
Window->m_SlotArea->SetSlot(0, *m_Player, Item);
Window->SendSlot(*m_Player, Window->m_SlotArea, 0);
Window->BroadcastWholeWindow();
Window->SetProperty(0, 0, *m_Player);
Window->SetProperty(1, 0, *m_Player);
Window->SetProperty(2, 0, *m_Player);
}
}
}

View File

@ -18,6 +18,8 @@
#include "ByteBuffer.h"
#include "Scoreboard.h"
#include "Map.h"
#include "Enchantments.h"
#include "UI/SlotArea.h"
@ -62,8 +64,22 @@ public:
cPlayer* GetPlayer() { return m_Player; } // tolua_export
const AString & GetUUID(void) const { return m_UUID; } // tolua_export
void SetUUID(const AString & a_UUID) { m_UUID = a_UUID; }
/** Generates an UUID based on the username stored for this client, and stores it in the m_UUID member.
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.
Internally calls the GenerateOfflineUUID static function. */
void GenerateOfflineUUID(void);
/** 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. */
static AString GenerateOfflineUUID(const AString & a_Username); // tolua_export
void Kick(const AString & a_Reason); // tolua_export
void Authenticate(void); // Called by cAuthenticator when the user passes authentication
void Authenticate(const AString & a_Name, const AString & a_UUID); // Called by cAuthenticator when the user passes authentication
void StreamChunks(void);
@ -225,11 +241,14 @@ public:
*/
bool HandleLogin(int a_ProtocolVersion, const AString & a_Username);
void SendData(const char * a_Data, int a_Size);
void SendData(const char * a_Data, size_t a_Size);
/** Called when the player moves into a different world; queues sreaming the new chunks */
void MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket);
/** Called when the player will enchant a Item */
void HandleEnchantItem(Byte & WindowID, Byte & Enchantment);
private:
/** Handles the block placing packet when it is a real block placement (not block-using, item-using or eating) */
@ -326,6 +345,7 @@ private:
static int s_ClientCount;
int m_UniqueID;
AString m_UUID;
/** Set to true when the chunk where the player is is sent to the client. Used for spawning the player */
bool m_HasSentPlayerChunk;
@ -362,7 +382,7 @@ private:
void HandleCommandBlockMessage(const char * a_Data, unsigned int a_Length);
// cSocketThreads::cCallback overrides:
virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client
virtual void DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client
virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client
virtual void SocketClosed (void) override; // The socket has been closed for any reason
}; // tolua_export

View File

@ -661,14 +661,16 @@ cCraftingRecipes::cRecipe * cCraftingRecipes::MatchRecipe(const cItem * a_Crafti
ASSERT(itrS->x + a_OffsetX < a_GridWidth);
ASSERT(itrS->y + a_OffsetY < a_GridHeight);
int GridID = (itrS->x + a_OffsetX) + a_GridStride * (itrS->y + a_OffsetY);
const cItem & Item = itrS->m_Item;
if (
(itrS->x >= a_GridWidth) ||
(itrS->y >= a_GridHeight) ||
(itrS->m_Item.m_ItemType != a_CraftingGrid[GridID].m_ItemType) || // same item type?
(itrS->m_Item.m_ItemCount > a_CraftingGrid[GridID].m_ItemCount) || // not enough items
(Item.m_ItemType != a_CraftingGrid[GridID].m_ItemType) || // same item type?
(Item.m_ItemCount > a_CraftingGrid[GridID].m_ItemCount) || // not enough items
(
(itrS->m_Item.m_ItemDamage > 0) && // should compare damage values?
(itrS->m_Item.m_ItemDamage != a_CraftingGrid[GridID].m_ItemDamage)
(Item.m_ItemDamage > 0) && // should compare damage values?
(Item.m_ItemDamage != a_CraftingGrid[GridID].m_ItemDamage)
)
)
{
@ -824,7 +826,7 @@ void cCraftingRecipes::HandleFireworks(const cItem * a_CraftingGrid, cCraftingRe
case E_ITEM_DYE:
{
int GridID = (itr->x + a_OffsetX) + a_GridStride * (itr->y + a_OffsetY);
DyeColours.push_back(cFireworkItem::GetVanillaColourCodeFromDye(a_CraftingGrid[GridID].m_ItemDamage));
DyeColours.push_back(cFireworkItem::GetVanillaColourCodeFromDye((NIBBLETYPE)(a_CraftingGrid[GridID].m_ItemDamage & 0x0f)));
break;
}
case E_ITEM_GUNPOWDER: break;

View File

@ -206,7 +206,7 @@ int cRSAPrivateKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte
ASSERT(!"Invalid a_DecryptedMaxLength!");
return -1;
}
if (a_EncryptedMaxLength < m_Rsa.len)
if (a_PlainLength < m_Rsa.len)
{
LOGD("%s: Invalid a_PlainLength: got %u, exp at least %u",
__FUNCTION__, (unsigned)a_PlainLength, (unsigned)(m_Rsa.len)

View File

@ -112,18 +112,21 @@ void cDeadlockDetect::CheckWorldAge(const AString & a_WorldName, Int64 a_Age)
ASSERT(!"Unknown world in cDeadlockDetect");
return;
}
if (itr->second.m_Age == a_Age)
cDeadlockDetect::sWorldAge & WorldAge = itr->second;
if (WorldAge.m_Age == a_Age)
{
itr->second.m_NumCyclesSame += 1;
if (itr->second.m_NumCyclesSame > (1000 * m_IntervalSec) / CYCLE_MILLISECONDS)
WorldAge.m_NumCyclesSame += 1;
if (WorldAge.m_NumCyclesSame > (1000 * m_IntervalSec) / CYCLE_MILLISECONDS)
{
DeadlockDetected();
}
}
else
{
itr->second.m_Age = a_Age;
itr->second.m_NumCyclesSame = 0;
WorldAge.m_Age = a_Age;
WorldAge.m_NumCyclesSame = 0;
}
}

View File

@ -493,15 +493,17 @@ inline void EulerToVector(double a_Pan, double a_Pitch, double & a_X, double & a
inline void VectorToEuler(double a_X, double a_Y, double a_Z, double & a_Pan, double & a_Pitch)
{
if (fabs(a_X) < std::numeric_limits<double>::epsilon())
{
a_Pan = atan2(a_Z, a_X) * 180 / PI - 90;
}
else
double r = sqrt((a_X * a_X) + (a_Z * a_Z));
if (r < std::numeric_limits<double>::epsilon())
{
a_Pan = 0;
}
a_Pitch = atan2(a_Y, sqrt((a_X * a_X) + (a_Z * a_Z))) * 180 / PI;
else
{
a_Pan = atan2(a_Z, a_X) * 180 / PI - 90;
}
a_Pitch = atan2(a_Y, r) * 180 / PI;
}

View File

@ -5,6 +5,7 @@
#include "Globals.h"
#include "Enchantments.h"
#include "WorldStorage/FastNBT.h"
#include "FastRandom.h"
@ -28,6 +29,18 @@ cEnchantments::cEnchantments(const AString & a_StringSpec)
void cEnchantments::Add(const cEnchantments & a_Other)
{
for (cEnchantments::cMap::const_iterator itr = a_Other.m_Enchantments.begin(), end = a_Other.m_Enchantments.end(); itr != end; ++itr)
{
SetLevel(itr->first, itr->second);
} // for itr - a_Other.m_Enchantments[]
}
void cEnchantments::AddFromString(const AString & a_StringSpec)
{
// Add enchantments in the stringspec; if a specified enchantment already exists, overwrites it
@ -49,21 +62,17 @@ void cEnchantments::AddFromString(const AString & a_StringSpec)
LOG("%s: Malformed enchantment decl: \"%s\", skipping.", __FUNCTION__, itr->c_str());
continue;
}
int id = atoi(Split[0].c_str());
if ((id == 0) && (Split[0] != "0"))
int id = StringToEnchantmentID(Split[0]);
if (id < 0)
{
id = StringToEnchantmentID(Split[0]);
LOG("%s: Failed to parse enchantment \"%s\", skipping.", __FUNCTION__, Split[0].c_str());
continue;
}
int lvl = atoi(Split[1].c_str());
if (
((id <= 0) && (Split[0] != "0")) ||
((lvl == 0) && (Split[1] != "0"))
)
if ((lvl == 0) && (Split[1] != "0"))
{
// Numbers failed to parse
LOG("%s: Failed to parse enchantment declaration for numbers: \"%s\" and \"%s\", skipping.",
__FUNCTION__, Split[0].c_str(), Split[1].c_str()
);
// Level failed to parse
LOG("%s: Failed to parse enchantment level \"%s\", skipping.", __FUNCTION__, Split[1].c_str());
continue;
}
SetLevel(id, lvl);
@ -150,7 +159,7 @@ bool cEnchantments::IsEmpty(void) const
int cEnchantments::StringToEnchantmentID(const AString & a_EnchantmentName)
{
struct
static const struct
{
int m_Value;
const char * m_Name;
@ -181,6 +190,15 @@ int cEnchantments::StringToEnchantmentID(const AString & a_EnchantmentName)
{ enchLuckOfTheSea, "LuckOfTheSea"},
{ enchLure, "Lure"},
} ;
// First try to parse as a number:
int id = atoi(a_EnchantmentName.c_str());
if ((id != 0) || (a_EnchantmentName == "0"))
{
return id;
}
// It wasn't a number, do a lookup:
for (size_t i = 0; i < ARRAYCOUNT(EnchantmentNames); i++)
{
if (NoCaseCompare(EnchantmentNames[i].m_Name, a_EnchantmentName) == 0)
@ -213,8 +231,782 @@ bool cEnchantments::operator !=(const cEnchantments & a_Other) const
void cEnchantments::AddItemEnchantmentWeights(cWeightedEnchantments & a_Enchantments, short a_ItemType, int a_EnchantmentLevel)
{
if (ItemCategory::IsSword(a_ItemType))
{
// Sharpness
if ((a_EnchantmentLevel >= 34) && (a_EnchantmentLevel <= 54))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchSharpness, 4);
}
else if ((a_EnchantmentLevel >= 23) && (a_EnchantmentLevel <= 43))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchSharpness, 3);
}
else if ((a_EnchantmentLevel >= 12) && (a_EnchantmentLevel <= 32))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchSharpness, 2);
}
else if ((a_EnchantmentLevel >= 1) && (a_EnchantmentLevel <= 21))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchSharpness, 1);
}
// Smite
if ((a_EnchantmentLevel >= 29) && (a_EnchantmentLevel <= 49))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchSmite, 4);
}
else if ((a_EnchantmentLevel >= 21) && (a_EnchantmentLevel <= 41))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchSmite, 3);
}
else if ((a_EnchantmentLevel >= 13) && (a_EnchantmentLevel <= 33))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchSmite, 2);
}
else if ((a_EnchantmentLevel >= 5) && (a_EnchantmentLevel <= 25))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchSmite, 1);
}
// Bane of Arthropods
if ((a_EnchantmentLevel >= 29) && (a_EnchantmentLevel <= 49))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchBaneOfArthropods, 4);
}
else if ((a_EnchantmentLevel >= 21) && (a_EnchantmentLevel <= 41))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchBaneOfArthropods, 3);
}
else if ((a_EnchantmentLevel >= 13) && (a_EnchantmentLevel <= 33))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchBaneOfArthropods, 2);
}
else if ((a_EnchantmentLevel >= 5) && (a_EnchantmentLevel <= 25))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchBaneOfArthropods, 1);
}
// Knockback
if ((a_EnchantmentLevel >= 25) && (a_EnchantmentLevel <= 75))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchKnockback, 2);
}
else if ((a_EnchantmentLevel >= 5) && (a_EnchantmentLevel <= 55))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchKnockback, 1);
}
// Fire Aspect
if ((a_EnchantmentLevel >= 30) && (a_EnchantmentLevel <= 80))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchFireAspect, 2);
}
else if ((a_EnchantmentLevel >= 10) && (a_EnchantmentLevel <= 60))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchFireAspect, 1);
}
// Looting
if ((a_EnchantmentLevel >= 33) && (a_EnchantmentLevel <= 83))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchLooting, 3);
}
else if ((a_EnchantmentLevel >= 24) && (a_EnchantmentLevel <= 74))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchLooting, 2);
}
else if ((a_EnchantmentLevel >= 15) && (a_EnchantmentLevel <= 65))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchLooting, 1);
}
}
else if (ItemCategory::IsTool(a_ItemType))
{
// Efficiency
if ((a_EnchantmentLevel >= 31) && (a_EnchantmentLevel <= 81))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchEfficiency, 4);
}
else if ((a_EnchantmentLevel >= 21) && (a_EnchantmentLevel <= 71))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchEfficiency, 3);
}
else if ((a_EnchantmentLevel >= 11) && (a_EnchantmentLevel <= 61))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchEfficiency, 2);
}
else if ((a_EnchantmentLevel >= 1) && (a_EnchantmentLevel <= 51))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchEfficiency, 1);
}
// Silk Touch
if ((a_EnchantmentLevel >= 15) && (a_EnchantmentLevel <= 65))
{
AddEnchantmentWeightToVector(a_Enchantments, 1, enchSilkTouch, 1);
}
// Fortune
if ((a_EnchantmentLevel >= 33) && (a_EnchantmentLevel <= 83))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchFortune, 3);
}
else if ((a_EnchantmentLevel >= 24) && (a_EnchantmentLevel <= 74))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchFortune, 2);
}
else if ((a_EnchantmentLevel >= 15) && (a_EnchantmentLevel <= 65))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchFortune, 1);
}
}
else if (ItemCategory::IsArmor(a_ItemType))
{
// Protection
if ((a_EnchantmentLevel >= 34) && (a_EnchantmentLevel <= 54))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchProtection, 4);
}
else if ((a_EnchantmentLevel >= 23) && (a_EnchantmentLevel <= 43))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchProtection, 3);
}
else if ((a_EnchantmentLevel >= 12) && (a_EnchantmentLevel <= 32))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchProtection, 2);
}
else if ((a_EnchantmentLevel >= 1) && (a_EnchantmentLevel <= 21))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchProtection, 1);
}
// Fire Protection
if ((a_EnchantmentLevel >= 34) && (a_EnchantmentLevel <= 46))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchFireProtection, 4);
}
else if ((a_EnchantmentLevel >= 26) && (a_EnchantmentLevel <= 38))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchFireProtection, 3);
}
else if ((a_EnchantmentLevel >= 18) && (a_EnchantmentLevel <= 30))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchFireProtection, 2);
}
else if ((a_EnchantmentLevel >= 10) && (a_EnchantmentLevel <= 22))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchFireProtection, 1);
}
// Blast Protection
if ((a_EnchantmentLevel >= 29) && (a_EnchantmentLevel <= 41))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchBlastProtection, 4);
}
else if ((a_EnchantmentLevel >= 21) && (a_EnchantmentLevel <= 33))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchBlastProtection, 3);
}
else if ((a_EnchantmentLevel >= 13) && (a_EnchantmentLevel <= 25))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchBlastProtection, 2);
}
else if ((a_EnchantmentLevel >= 5) && (a_EnchantmentLevel <= 17))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchBlastProtection, 1);
}
// Projectile Protection
if ((a_EnchantmentLevel >= 21) && (a_EnchantmentLevel <= 36))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchProjectileProtection, 4);
}
else if ((a_EnchantmentLevel >= 15) && (a_EnchantmentLevel <= 30))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchProjectileProtection, 3);
}
else if ((a_EnchantmentLevel >= 9) && (a_EnchantmentLevel <= 24))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchProjectileProtection, 2);
}
else if ((a_EnchantmentLevel >= 3) && (a_EnchantmentLevel <= 18))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchProjectileProtection, 1);
}
// Thorns
if ((a_EnchantmentLevel >= 50) && (a_EnchantmentLevel <= 100))
{
AddEnchantmentWeightToVector(a_Enchantments, 1, enchThorns, 3);
}
else if ((a_EnchantmentLevel >= 30) && (a_EnchantmentLevel <= 80))
{
AddEnchantmentWeightToVector(a_Enchantments, 1, enchThorns, 2);
}
else if ((a_EnchantmentLevel >= 10) && (a_EnchantmentLevel <= 60))
{
AddEnchantmentWeightToVector(a_Enchantments, 1, enchThorns, 1);
}
if (ItemCategory::IsHelmet(a_ItemType))
{
// Respiration
if ((a_EnchantmentLevel >= 30) && (a_EnchantmentLevel <= 60))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchRespiration, 3);
}
else if ((a_EnchantmentLevel >= 20) && (a_EnchantmentLevel <= 50))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchRespiration, 2);
}
else if ((a_EnchantmentLevel >= 10) && (a_EnchantmentLevel <= 40))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchRespiration, 1);
}
// Aqua Affinity
if ((a_EnchantmentLevel >= 1) && (a_EnchantmentLevel <= 41))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchAquaAffinity, 1);
}
}
else if (ItemCategory::IsBoots(a_ItemType))
{
// Feather Fall
if ((a_EnchantmentLevel >= 23) && (a_EnchantmentLevel <= 33))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchFeatherFalling, 4);
}
else if ((a_EnchantmentLevel >= 17) && (a_EnchantmentLevel <= 27))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchFeatherFalling, 3);
}
else if ((a_EnchantmentLevel >= 11) && (a_EnchantmentLevel <= 21))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchFeatherFalling, 2);
}
else if ((a_EnchantmentLevel >= 5) && (a_EnchantmentLevel <= 15))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchFeatherFalling, 1);
}
}
}
else if (a_ItemType == E_ITEM_BOW)
{
// Power
if ((a_EnchantmentLevel >= 31) && (a_EnchantmentLevel <= 46))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchPower, 4);
}
else if ((a_EnchantmentLevel >= 21) && (a_EnchantmentLevel <= 36))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchPower, 3);
}
else if ((a_EnchantmentLevel >= 11) && (a_EnchantmentLevel <= 26))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchPower, 2);
}
else if ((a_EnchantmentLevel >= 1) && (a_EnchantmentLevel <= 16))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchPower, 1);
}
// Punch
if ((a_EnchantmentLevel >= 32) && (a_EnchantmentLevel <= 57))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchPunch, 2);
}
else if ((a_EnchantmentLevel >= 12) && (a_EnchantmentLevel <= 37))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchPunch, 1);
}
// Flame and Infinity
if ((a_EnchantmentLevel >= 20) && (a_EnchantmentLevel <= 50))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchFlame, 1);
AddEnchantmentWeightToVector(a_Enchantments, 1, enchInfinity, 1);
}
}
else if (a_ItemType == E_ITEM_FISHING_ROD)
{
// Luck of the Sea and Lure
if ((a_EnchantmentLevel >= 33) && (a_EnchantmentLevel <= 83))
{
AddEnchantmentWeightToVector(a_Enchantments, 1, enchLuckOfTheSea, 3);
AddEnchantmentWeightToVector(a_Enchantments, 1, enchLure, 3);
}
else if ((a_EnchantmentLevel >= 24) && (a_EnchantmentLevel <= 74))
{
AddEnchantmentWeightToVector(a_Enchantments, 1, enchLuckOfTheSea, 2);
AddEnchantmentWeightToVector(a_Enchantments, 1, enchLure, 2);
}
else if ((a_EnchantmentLevel >= 15) && (a_EnchantmentLevel <= 65))
{
AddEnchantmentWeightToVector(a_Enchantments, 1, enchLuckOfTheSea, 1);
AddEnchantmentWeightToVector(a_Enchantments, 1, enchLure, 1);
}
}
else if (a_ItemType == E_ITEM_BOOK)
{
// All Enchantments
// Sharpness
if ((a_EnchantmentLevel >= 34) && (a_EnchantmentLevel <= 54))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchSharpness, 4);
}
else if ((a_EnchantmentLevel >= 23) && (a_EnchantmentLevel <= 43))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchSharpness, 3);
}
else if ((a_EnchantmentLevel >= 12) && (a_EnchantmentLevel <= 32))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchSharpness, 2);
}
else if ((a_EnchantmentLevel >= 1) && (a_EnchantmentLevel <= 21))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchSharpness, 1);
}
// Smite
if ((a_EnchantmentLevel >= 29) && (a_EnchantmentLevel <= 49))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchSmite, 4);
}
else if ((a_EnchantmentLevel >= 21) && (a_EnchantmentLevel <= 41))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchSmite, 3);
}
else if ((a_EnchantmentLevel >= 13) && (a_EnchantmentLevel <= 33))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchSmite, 2);
}
else if ((a_EnchantmentLevel >= 5) && (a_EnchantmentLevel <= 25))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchSmite, 1);
}
// Bane of Arthropods
if ((a_EnchantmentLevel >= 29) && (a_EnchantmentLevel <= 49))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchBaneOfArthropods, 4);
}
else if ((a_EnchantmentLevel >= 21) && (a_EnchantmentLevel <= 41))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchBaneOfArthropods, 3);
}
else if ((a_EnchantmentLevel >= 13) && (a_EnchantmentLevel <= 33))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchBaneOfArthropods, 2);
}
else if ((a_EnchantmentLevel >= 5) && (a_EnchantmentLevel <= 25))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchBaneOfArthropods, 1);
}
// Knockback
if ((a_EnchantmentLevel >= 25) && (a_EnchantmentLevel <= 75))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchKnockback, 2);
}
else if ((a_EnchantmentLevel >= 5) && (a_EnchantmentLevel <= 55))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchKnockback, 1);
}
// Fire Aspect
if ((a_EnchantmentLevel >= 30) && (a_EnchantmentLevel <= 80))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchFireAspect, 2);
}
else if ((a_EnchantmentLevel >= 10) && (a_EnchantmentLevel <= 60))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchFireAspect, 1);
}
// Looting
if ((a_EnchantmentLevel >= 33) && (a_EnchantmentLevel <= 83))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchLooting, 3);
}
else if ((a_EnchantmentLevel >= 24) && (a_EnchantmentLevel <= 74))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchLooting, 2);
}
else if ((a_EnchantmentLevel >= 15) && (a_EnchantmentLevel <= 65))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchLooting, 1);
}
// Efficiency
if ((a_EnchantmentLevel >= 31) && (a_EnchantmentLevel <= 81))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchEfficiency, 4);
}
else if ((a_EnchantmentLevel >= 21) && (a_EnchantmentLevel <= 71))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchEfficiency, 3);
}
else if ((a_EnchantmentLevel >= 11) && (a_EnchantmentLevel <= 61))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchEfficiency, 2);
}
else if ((a_EnchantmentLevel >= 1) && (a_EnchantmentLevel <= 51))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchEfficiency, 1);
}
// Silk Touch
if ((a_EnchantmentLevel >= 15) && (a_EnchantmentLevel <= 65))
{
AddEnchantmentWeightToVector(a_Enchantments, 1, enchSilkTouch, 1);
}
// Fortune
if ((a_EnchantmentLevel >= 33) && (a_EnchantmentLevel <= 83))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchFortune, 3);
}
else if ((a_EnchantmentLevel >= 24) && (a_EnchantmentLevel <= 74))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchFortune, 2);
}
else if ((a_EnchantmentLevel >= 15) && (a_EnchantmentLevel <= 65))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchFortune, 1);
}
// Protection
if ((a_EnchantmentLevel >= 34) && (a_EnchantmentLevel <= 54))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchProtection, 4);
}
else if ((a_EnchantmentLevel >= 23) && (a_EnchantmentLevel <= 43))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchProtection, 3);
}
else if ((a_EnchantmentLevel >= 12) && (a_EnchantmentLevel <= 32))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchProtection, 2);
}
else if ((a_EnchantmentLevel >= 1) && (a_EnchantmentLevel <= 21))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchProtection, 1);
}
// Fire Protection
if ((a_EnchantmentLevel >= 34) && (a_EnchantmentLevel <= 46))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchFireProtection, 4);
}
else if ((a_EnchantmentLevel >= 26) && (a_EnchantmentLevel <= 38))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchFireProtection, 3);
}
else if ((a_EnchantmentLevel >= 18) && (a_EnchantmentLevel <= 30))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchFireProtection, 2);
}
else if ((a_EnchantmentLevel >= 10) && (a_EnchantmentLevel <= 22))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchFireProtection, 1);
}
// Blast Protection
if ((a_EnchantmentLevel >= 29) && (a_EnchantmentLevel <= 41))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchBlastProtection, 4);
}
else if ((a_EnchantmentLevel >= 21) && (a_EnchantmentLevel <= 33))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchBlastProtection, 3);
}
else if ((a_EnchantmentLevel >= 13) && (a_EnchantmentLevel <= 25))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchBlastProtection, 2);
}
else if ((a_EnchantmentLevel >= 5) && (a_EnchantmentLevel <= 17))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchBlastProtection, 1);
}
// Projectile Protection
if ((a_EnchantmentLevel >= 21) && (a_EnchantmentLevel <= 36))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchProjectileProtection, 4);
}
else if ((a_EnchantmentLevel >= 15) && (a_EnchantmentLevel <= 30))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchProjectileProtection, 3);
}
else if ((a_EnchantmentLevel >= 9) && (a_EnchantmentLevel <= 24))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchProjectileProtection, 2);
}
else if ((a_EnchantmentLevel >= 3) && (a_EnchantmentLevel <= 18))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchProjectileProtection, 1);
}
// Thorns
if ((a_EnchantmentLevel >= 50) && (a_EnchantmentLevel <= 100))
{
AddEnchantmentWeightToVector(a_Enchantments, 1, enchThorns, 3);
}
else if ((a_EnchantmentLevel >= 30) && (a_EnchantmentLevel <= 80))
{
AddEnchantmentWeightToVector(a_Enchantments, 1, enchThorns, 2);
}
else if ((a_EnchantmentLevel >= 10) && (a_EnchantmentLevel <= 60))
{
AddEnchantmentWeightToVector(a_Enchantments, 1, enchThorns, 1);
}
// Respiration
if ((a_EnchantmentLevel >= 30) && (a_EnchantmentLevel <= 60))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchRespiration, 3);
}
else if ((a_EnchantmentLevel >= 20) && (a_EnchantmentLevel <= 50))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchRespiration, 2);
}
else if ((a_EnchantmentLevel >= 10) && (a_EnchantmentLevel <= 40))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchRespiration, 1);
}
// Aqua Affinity
if ((a_EnchantmentLevel >= 1) && (a_EnchantmentLevel <= 41))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchAquaAffinity, 1);
}
// Feather Fall
if ((a_EnchantmentLevel >= 23) && (a_EnchantmentLevel <= 33))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchFeatherFalling, 4);
}
else if ((a_EnchantmentLevel >= 17) && (a_EnchantmentLevel <= 27))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchFeatherFalling, 3);
}
else if ((a_EnchantmentLevel >= 11) && (a_EnchantmentLevel <= 21))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchFeatherFalling, 2);
}
else if ((a_EnchantmentLevel >= 5) && (a_EnchantmentLevel <= 15))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchFeatherFalling, 1);
}
// Power
if ((a_EnchantmentLevel >= 31) && (a_EnchantmentLevel <= 46))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchPower, 4);
}
else if ((a_EnchantmentLevel >= 21) && (a_EnchantmentLevel <= 36))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchPower, 3);
}
else if ((a_EnchantmentLevel >= 11) && (a_EnchantmentLevel <= 26))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchPower, 2);
}
else if ((a_EnchantmentLevel >= 1) && (a_EnchantmentLevel <= 16))
{
AddEnchantmentWeightToVector(a_Enchantments, 10, enchPower, 1);
}
// Punch
if ((a_EnchantmentLevel >= 32) && (a_EnchantmentLevel <= 57))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchPunch, 2);
}
else if ((a_EnchantmentLevel >= 12) && (a_EnchantmentLevel <= 37))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchPunch, 1);
}
// Flame and Infinity
if ((a_EnchantmentLevel >= 20) && (a_EnchantmentLevel <= 50))
{
AddEnchantmentWeightToVector(a_Enchantments, 2, enchFlame, 1);
AddEnchantmentWeightToVector(a_Enchantments, 1, enchInfinity, 1);
}
// Luck of the Sea and Lure
if ((a_EnchantmentLevel >= 33) && (a_EnchantmentLevel <= 83))
{
AddEnchantmentWeightToVector(a_Enchantments, 1, enchLuckOfTheSea, 3);
AddEnchantmentWeightToVector(a_Enchantments, 1, enchLure, 3);
}
else if ((a_EnchantmentLevel >= 24) && (a_EnchantmentLevel <= 74))
{
AddEnchantmentWeightToVector(a_Enchantments, 1, enchLuckOfTheSea, 2);
AddEnchantmentWeightToVector(a_Enchantments, 1, enchLure, 2);
}
else if ((a_EnchantmentLevel >= 15) && (a_EnchantmentLevel <= 65))
{
AddEnchantmentWeightToVector(a_Enchantments, 1, enchLuckOfTheSea, 1);
AddEnchantmentWeightToVector(a_Enchantments, 1, enchLure, 1);
}
}
// Unbreaking
if ((a_EnchantmentLevel >= 21) && (a_EnchantmentLevel <= 71))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchUnbreaking, 3);
}
else if ((a_EnchantmentLevel >= 13) && (a_EnchantmentLevel <= 63))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchUnbreaking, 2);
}
else if ((a_EnchantmentLevel >= 5) && (a_EnchantmentLevel <= 55))
{
AddEnchantmentWeightToVector(a_Enchantments, 5, enchUnbreaking, 1);
}
}
void cEnchantments::AddEnchantmentWeightToVector(cWeightedEnchantments & a_Enchantments, int a_Weight, int a_EnchantmentID, int a_EnchantmentLevel)
{
cWeightedEnchantment weightedenchantment;
weightedenchantment.m_Weight = a_Weight;
cEnchantments enchantment;
enchantment.SetLevel(a_EnchantmentID, a_EnchantmentLevel);
weightedenchantment.m_Enchantments = enchantment;
a_Enchantments.push_back(weightedenchantment);
}
void cEnchantments::RemoveEnchantmentWeightFromVector(cWeightedEnchantments & a_Enchantments, int a_EnchantmentID)
{
for (cWeightedEnchantments::iterator it = a_Enchantments.begin(); it != a_Enchantments.end(); ++it)
{
if ((*it).m_Enchantments.GetLevel(a_EnchantmentID) > 0)
{
a_Enchantments.erase(it);
break;
}
}
}
void cEnchantments::RemoveEnchantmentWeightFromVector(cWeightedEnchantments & a_Enchantments, const cEnchantments & a_Enchantment)
{
for (cWeightedEnchantments::iterator it = a_Enchantments.begin(); it != a_Enchantments.end(); ++it)
{
if ((*it).m_Enchantments == a_Enchantment)
{
a_Enchantments.erase(it);
break;
}
}
}
void cEnchantments::CheckEnchantmentConflictsFromVector(cWeightedEnchantments & a_Enchantments, cEnchantments a_FirstEnchantment)
{
if (a_FirstEnchantment.GetLevel(cEnchantments::enchProtection) > 0)
{
RemoveEnchantmentWeightFromVector(a_Enchantments, cEnchantments::enchFireProtection);
RemoveEnchantmentWeightFromVector(a_Enchantments, cEnchantments::enchBlastProtection);
RemoveEnchantmentWeightFromVector(a_Enchantments, cEnchantments::enchProjectileProtection);
}
else if (a_FirstEnchantment.GetLevel(cEnchantments::enchFireProtection) > 0)
{
RemoveEnchantmentWeightFromVector(a_Enchantments, cEnchantments::enchProtection);
RemoveEnchantmentWeightFromVector(a_Enchantments, cEnchantments::enchBlastProtection);
RemoveEnchantmentWeightFromVector(a_Enchantments, cEnchantments::enchProjectileProtection);
}
else if (a_FirstEnchantment.GetLevel(cEnchantments::enchBlastProtection) > 0)
{
RemoveEnchantmentWeightFromVector(a_Enchantments, cEnchantments::enchProtection);
RemoveEnchantmentWeightFromVector(a_Enchantments, cEnchantments::enchFireProtection);
RemoveEnchantmentWeightFromVector(a_Enchantments, cEnchantments::enchProjectileProtection);
}
else if (a_FirstEnchantment.GetLevel(cEnchantments::enchProjectileProtection) > 0)
{
RemoveEnchantmentWeightFromVector(a_Enchantments, cEnchantments::enchProtection);
RemoveEnchantmentWeightFromVector(a_Enchantments, cEnchantments::enchFireProtection);
RemoveEnchantmentWeightFromVector(a_Enchantments, cEnchantments::enchBlastProtection);
}
else if (a_FirstEnchantment.GetLevel(cEnchantments::enchSharpness) > 0)
{
RemoveEnchantmentWeightFromVector(a_Enchantments, cEnchantments::enchSmite);
RemoveEnchantmentWeightFromVector(a_Enchantments, cEnchantments::enchBaneOfArthropods);
}
else if (a_FirstEnchantment.GetLevel(cEnchantments::enchSmite) > 0)
{
RemoveEnchantmentWeightFromVector(a_Enchantments, cEnchantments::enchSharpness);
RemoveEnchantmentWeightFromVector(a_Enchantments, cEnchantments::enchBaneOfArthropods);
}
else if (a_FirstEnchantment.GetLevel(cEnchantments::enchBaneOfArthropods) > 0)
{
RemoveEnchantmentWeightFromVector(a_Enchantments, cEnchantments::enchSharpness);
RemoveEnchantmentWeightFromVector(a_Enchantments, cEnchantments::enchSmite);
}
else if (a_FirstEnchantment.GetLevel(cEnchantments::enchSilkTouch) > 0)
{
RemoveEnchantmentWeightFromVector(a_Enchantments, cEnchantments::enchFortune);
}
else if (a_FirstEnchantment.GetLevel(cEnchantments::enchFortune) > 0)
{
RemoveEnchantmentWeightFromVector(a_Enchantments, cEnchantments::enchSilkTouch);
}
}
cEnchantments cEnchantments::GetRandomEnchantmentFromVector(cWeightedEnchantments & a_Enchantments)
{
cFastRandom Random;
int AllWeights = 0;
for (cWeightedEnchantments::iterator it = a_Enchantments.begin(); it != a_Enchantments.end(); ++it)
{
AllWeights += (*it).m_Weight;
}
int RandomNumber = Random.GenerateRandomInteger(0, AllWeights - 1);
for (cWeightedEnchantments::iterator it = a_Enchantments.begin(); it != a_Enchantments.end(); ++it)
{
RandomNumber -= (*it).m_Weight;
if (RandomNumber < 0)
{
return (*it).m_Enchantments;
}
}
return cEnchantments();
}

View File

@ -8,6 +8,7 @@
#pragma once
#include "Defines.h"
#include "WorldStorage/EnchantmentSerializer.h"
@ -18,6 +19,11 @@ class cFastNBTWriter;
class cParsedNBT;
// fwd:
struct cWeightedEnchantment;
typedef std::vector<cWeightedEnchantment> cWeightedEnchantments;
@ -28,11 +34,14 @@ mapping each enchantment's id onto its level. ID may be either a number or the e
Level value of 0 means no such enchantment, and it will not be stored in the m_Enchantments.
Serialization will never put zero-level enchantments into the stringspec and will always use numeric IDs.
*/
// tolua_begin
class cEnchantments
{
public:
/// Individual enchantment IDs, corresponding to their NBT IDs ( http://www.minecraftwiki.net/wiki/Data_Values#Enchantment_IDs )
/** Individual enchantment IDs, corresponding to their NBT IDs ( http://www.minecraftwiki.net/wiki/Data_Values#Enchantment_IDs )
*/
enum
{
@ -61,56 +70,87 @@ public:
enchLuckOfTheSea = 61,
enchLure = 62,
} ;
/// Creates an empty enchantments container
/** Creates an empty enchantments container */
cEnchantments(void);
/// Creates an enchantments container filled with enchantments parsed from stringspec
/** Creates an enchantments container filled with enchantments parsed from stringspec */
cEnchantments(const AString & a_StringSpec);
/// Adds enchantments in the stringspec; if a specified enchantment already exists, overwrites it
/** Adds the enchantments contained in a_Other into this object.
Existing enchantments are preserved, unless a_Other specifies a different level, in which case the level is changed. */
void Add(const cEnchantments & a_Other);
/** Adds enchantments in the stringspec; if a specified enchantment already exists, overwrites it */
void AddFromString(const AString & a_StringSpec);
/// Serializes all the enchantments into a string
/** Serializes all the enchantments into a string */
AString ToString(void) const;
/// Returns the level for the specified enchantment; 0 if not stored
/** Returns the level for the specified enchantment; 0 if not stored */
int GetLevel(int a_EnchantmentID) const;
/// Sets the level for the specified enchantment, adding it if not stored before or removing it if level <= 0
/** Sets the level for the specified enchantment, adding it if not stored before or removing it if level <= 0 */
void SetLevel(int a_EnchantmentID, int a_Level);
/// Removes all enchantments
/** Removes all enchantments */
void Clear(void);
/// Returns true if there are no enchantments
/** Returns true if there are no enchantments */
bool IsEmpty(void) const;
/// Converts enchantment name to the numeric representation; returns -1 if enchantment name not found; case insensitive
/** Converts enchantment name or ID (number in string) to the numeric representation; returns -1 if enchantment name not found; case insensitive */
static int StringToEnchantmentID(const AString & a_EnchantmentName);
/// Returns true if a_Other contains exactly the same enchantments and levels
/** Returns true if a_Other contains exactly the same enchantments and levels */
bool operator ==(const cEnchantments & a_Other) const;
// tolua_end
/** Add enchantment weights from item to the vector */
static void AddItemEnchantmentWeights(cWeightedEnchantments & a_Enchantments, short a_ItemType, int a_EnchantmentLevel);
/** Add a enchantment with weight to the vector */
static void AddEnchantmentWeightToVector(cWeightedEnchantments & a_Enchantments, int a_Weight, int a_EnchantmentID, int a_EnchantmentLevel);
/// Returns true if a_Other doesn't contain exactly the same enchantments and levels
/** Remove the entire enchantment (with weight) from the vector */
static void RemoveEnchantmentWeightFromVector(cWeightedEnchantments & a_Enchantments, int a_EnchantmentID);
/** Remove the entire enchantment (with weight) from the vector */
static void RemoveEnchantmentWeightFromVector(cWeightedEnchantments & a_Enchantments, const cEnchantments & a_Enchantment);
/** Check enchantment conflicts from enchantments from the vector */
static void CheckEnchantmentConflictsFromVector(cWeightedEnchantments & a_Enchantments, cEnchantments a_FirstEnchantment);
/** Gets random enchantment from Vector and returns it */
static cEnchantments GetRandomEnchantmentFromVector(cWeightedEnchantments & a_Enchantments);
/** Returns true if a_Other doesn't contain exactly the same enchantments and levels */
bool operator !=(const cEnchantments & a_Other) const;
/// Writes the enchantments into the specified NBT writer; begins with the LIST tag of the specified name ("ench" or "StoredEnchantments")
/** Writes the enchantments into the specified NBT writer; begins with the LIST tag of the specified name ("ench" or "StoredEnchantments") */
friend void EnchantmentSerializer::WriteToNBTCompound(cEnchantments const& a_Enchantments, cFastNBTWriter & a_Writer, const AString & a_ListTagName);
/// Reads the enchantments from the specified NBT list tag (ench or StoredEnchantments)
/** Reads the enchantments from the specified NBT list tag (ench or StoredEnchantments) */
friend void EnchantmentSerializer::ParseFromNBT(cEnchantments& a_Enchantments, const cParsedNBT & a_NBT, int a_EnchListTagIdx);
protected:
/// Maps enchantment ID -> enchantment level
/** Maps enchantment ID -> enchantment level */
typedef std::map<int, int> cMap;
/// Currently stored enchantments
/** Currently stored enchantments */
cMap m_Enchantments;
} ; // tolua_export
// Define the cWeightedEnchantment struct for the Enchanting System to store the EnchantmentWeights:
struct cWeightedEnchantment
{
int m_Weight;
cEnchantments m_Enchantments;
};

View File

@ -1,12 +1,14 @@
#pragma once
#define ntohll(x) ((((UInt64)ntohl((u_long)x)) << 32) + ntohl(x >> 32))
// Changes endianness
inline unsigned long long HostToNetwork8(const void* a_Value )
inline UInt64 HostToNetwork8(const void * a_Value)
{
unsigned long long __HostToNetwork8;
memcpy( &__HostToNetwork8, a_Value, sizeof( __HostToNetwork8 ) );
@ -18,7 +20,7 @@ inline unsigned long long HostToNetwork8(const void* a_Value )
inline unsigned int HostToNetwork4(const void* a_Value )
inline UInt32 HostToNetwork4(const void* a_Value )
{
unsigned int __HostToNetwork4;
memcpy( &__HostToNetwork4, a_Value, sizeof( __HostToNetwork4 ) );
@ -30,11 +32,10 @@ inline unsigned int HostToNetwork4(const void* a_Value )
inline double NetworkToHostDouble8(const void* a_Value )
inline double NetworkToHostDouble8(const void * a_Value)
{
#define ntohll(x) ((((unsigned long long)ntohl((u_long)x)) << 32) + ntohl(x >> 32))
unsigned long long buf = 0;//(*(unsigned long long*)a_Value);
memcpy( &buf, a_Value, 8 );
UInt64 buf = 0;
memcpy(&buf, a_Value, 8);
buf = ntohll(buf);
double x;
memcpy(&x, &buf, sizeof(double));
@ -45,23 +46,25 @@ inline double NetworkToHostDouble8(const void* a_Value )
inline long long NetworkToHostLong8(const void * a_Value )
inline Int64 NetworkToHostLong8(const void * a_Value)
{
unsigned long long buf = *(unsigned long long*)a_Value;
UInt64 buf;
memcpy(&buf, a_Value, 8);
buf = ntohll(buf);
return *reinterpret_cast<long long *>(&buf);
return *reinterpret_cast<Int64 *>(&buf);
}
inline float NetworkToHostFloat4(const void* a_Value )
inline float NetworkToHostFloat4(const void * a_Value)
{
u_long buf = *(u_long*)a_Value;
buf = ntohl( buf );
float x = 0;
memcpy( &x, &buf, sizeof(float) );
UInt32 buf;
float x;
memcpy(&buf, a_Value, 4);
buf = ntohl(buf);
memcpy(&x, &buf, sizeof(float));
return x;
}

View File

@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../")
file(GLOB SOURCE
"*.cpp"
"*.h"
)
add_library(Entities ${SOURCE})

View File

@ -45,6 +45,7 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
, m_IsInitialized(false)
, m_EntityType(a_EntityType)
, m_World(NULL)
, m_IsFireproof(false)
, m_TicksSinceLastBurnDamage(0)
, m_TicksSinceLastLavaDamage(0)
, m_TicksSinceLastFireDamage(0)
@ -325,12 +326,41 @@ void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
m_Health = 0;
}
if (IsMob() || IsPlayer()) // Knockback for only players and mobs
if ((IsMob() || IsPlayer()) && (a_TDI.Attacker != NULL)) // Knockback for only players and mobs
{
AddSpeed(a_TDI.Knockback * 2);
int KnockbackLevel = 0;
if (a_TDI.Attacker->GetEquippedWeapon().m_ItemType == E_ITEM_BOW)
{
KnockbackLevel = a_TDI.Attacker->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchPunch);
}
else
{
KnockbackLevel = a_TDI.Attacker->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchKnockback);
}
Vector3d additionalSpeed(0, 0, 0);
switch (KnockbackLevel)
{
case 1:
{
additionalSpeed.Set(5, .3, 5);
break;
}
case 2:
{
additionalSpeed.Set(8, .3, 8);
break;
}
default:
{
additionalSpeed.Set(2, .3, 2);
break;
}
}
AddSpeed(a_TDI.Knockback * additionalSpeed);
}
m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_HURT);
m_World->BroadcastEntityStatus(*this, esGenericHurt);
if (m_Health <= 0)
{
@ -479,7 +509,7 @@ void cEntity::KilledBy(cEntity * a_Killer)
GetDrops(Drops, a_Killer);
m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ());
m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_DEAD);
m_World->BroadcastEntityStatus(*this, esGenericDead);
}
@ -519,37 +549,36 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
}
else
{
if (a_Chunk.IsValid())
if (!a_Chunk.IsValid())
{
cChunk * NextChunk = a_Chunk.GetNeighborChunk(POSX_TOINT, POSZ_TOINT);
if ((NextChunk == NULL) || !NextChunk->IsValid())
{
return;
}
TickBurning(*NextChunk);
if (GetPosY() < VOID_BOUNDARY)
{
TickInVoid(*NextChunk);
}
else
{
m_TicksSinceLastVoidDamage = 0;
}
if (IsMob() || IsPlayer())
{
// Set swimming state
SetSwimState(*NextChunk);
// Handle drowning
HandleAir();
}
HandlePhysics(a_Dt, *NextChunk);
return;
}
// Position changed -> super::Tick() called
GET_AND_VERIFY_CURRENT_CHUNK(NextChunk, POSX_TOINT, POSZ_TOINT)
TickBurning(*NextChunk);
if (GetPosY() < VOID_BOUNDARY)
{
TickInVoid(*NextChunk);
}
else
{
m_TicksSinceLastVoidDamage = 0;
}
if (IsMob() || IsPlayer())
{
// Set swimming state
SetSwimState(*NextChunk);
// Handle drowning
HandleAir();
}
// None of the above functions change position, we remain in the chunk of NextChunk
HandlePhysics(a_Dt, *NextChunk);
}
}
@ -559,34 +588,30 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
int BlockX = POSX_TOINT;
int BlockY = POSY_TOINT;
int BlockZ = POSZ_TOINT;
// Position changed -> super::HandlePhysics() called
GET_AND_VERIFY_CURRENT_CHUNK(NextChunk, BlockX, BlockZ)
// TODO Add collision detection with entities.
a_Dt /= 1000; // Convert from msec to sec
Vector3d NextPos = Vector3d(GetPosX(),GetPosY(),GetPosZ());
Vector3d NextSpeed = Vector3d(GetSpeedX(),GetSpeedY(),GetSpeedZ());
int BlockX = (int) floor(NextPos.x);
int BlockY = (int) floor(NextPos.y);
int BlockZ = (int) floor(NextPos.z);
Vector3d NextPos = Vector3d(GetPosX(), GetPosY(), GetPosZ());
Vector3d NextSpeed = Vector3d(GetSpeedX(), GetSpeedY(), GetSpeedZ());
if ((BlockY >= cChunkDef::Height) || (BlockY < 0))
{
// Outside of the world
cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ);
// See if we can commit our changes. If not, we will discard them.
if (NextChunk != NULL)
{
SetSpeed(NextSpeed);
NextPos += (NextSpeed * a_Dt);
SetPosition(NextPos);
}
// Outside of the world
AddSpeedY(m_Gravity * a_Dt);
AddPosition(GetSpeed() * a_Dt);
return;
}
int RelBlockX = BlockX - (a_Chunk.GetPosX() * cChunkDef::Width);
int RelBlockZ = BlockZ - (a_Chunk.GetPosZ() * cChunkDef::Width);
BLOCKTYPE BlockIn = a_Chunk.GetBlock( RelBlockX, BlockY, RelBlockZ );
BLOCKTYPE BlockBelow = (BlockY > 0) ? a_Chunk.GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR;
int RelBlockX = BlockX - (NextChunk->GetPosX() * cChunkDef::Width);
int RelBlockZ = BlockZ - (NextChunk->GetPosZ() * cChunkDef::Width);
BLOCKTYPE BlockIn = NextChunk->GetBlock( RelBlockX, BlockY, RelBlockZ );
BLOCKTYPE BlockBelow = (BlockY > 0) ? NextChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR;
if (!cBlockInfo::IsSolid(BlockIn)) // Making sure we are not inside a solid block
{
if (m_bOnGround) // check if it's still on the ground
@ -616,7 +641,7 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
bool IsNoAirSurrounding = true;
for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
{
if (!a_Chunk.UnboundedRelGetBlockType(RelBlockX + gCrossCoords[i].x, BlockY, RelBlockZ + gCrossCoords[i].z, GotBlock))
if (!NextChunk->UnboundedRelGetBlockType(RelBlockX + gCrossCoords[i].x, BlockY, RelBlockZ + gCrossCoords[i].z, GotBlock))
{
// The pickup is too close to an unloaded chunk, bail out of any physics handling
return;
@ -764,20 +789,8 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
}
}
BlockX = (int) floor(NextPos.x);
BlockZ = (int) floor(NextPos.z);
cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ);
// See if we can commit our changes. If not, we will discard them.
if (NextChunk != NULL)
{
if (NextPos.x != GetPosX()) SetPosX(NextPos.x);
if (NextPos.y != GetPosY()) SetPosY(NextPos.y);
if (NextPos.z != GetPosZ()) SetPosZ(NextPos.z);
if (NextSpeed.x != GetSpeedX()) SetSpeedX(NextSpeed.x);
if (NextSpeed.y != GetSpeedY()) SetSpeedY(NextSpeed.y);
if (NextSpeed.z != GetSpeedZ()) SetSpeedZ(NextSpeed.z);
}
SetPosition(NextPos);
SetSpeed(NextSpeed);
}
@ -788,6 +801,14 @@ void cEntity::TickBurning(cChunk & a_Chunk)
{
// Remember the current burning state:
bool HasBeenBurning = (m_TicksLeftBurning > 0);
if (m_World->IsWeatherWet())
{
if (POSY_TOINT > m_World->GetHeight(POSX_TOINT, POSZ_TOINT))
{
m_TicksLeftBurning = 0;
}
}
// Do the burning damage:
if (m_TicksLeftBurning > 0)
@ -795,7 +816,10 @@ void cEntity::TickBurning(cChunk & a_Chunk)
m_TicksSinceLastBurnDamage++;
if (m_TicksSinceLastBurnDamage >= BURN_TICKS_PER_DAMAGE)
{
TakeDamage(dtOnFire, NULL, BURN_DAMAGE, 0);
if (!m_IsFireproof)
{
TakeDamage(dtOnFire, NULL, BURN_DAMAGE, 0);
}
m_TicksSinceLastBurnDamage = 0;
}
m_TicksLeftBurning--;
@ -806,7 +830,7 @@ void cEntity::TickBurning(cChunk & a_Chunk)
int MaxRelX = (int)floor(GetPosX() + m_Width / 2) - a_Chunk.GetPosX() * cChunkDef::Width;
int MinRelZ = (int)floor(GetPosZ() - m_Width / 2) - a_Chunk.GetPosZ() * cChunkDef::Width;
int MaxRelZ = (int)floor(GetPosZ() + m_Width / 2) - a_Chunk.GetPosZ() * cChunkDef::Width;
int MinY = std::max(0, std::min(cChunkDef::Height - 1, (int)floor(GetPosY())));
int MinY = std::max(0, std::min(cChunkDef::Height - 1, POSY_TOINT));
int MaxY = std::max(0, std::min(cChunkDef::Height - 1, (int)ceil (GetPosY() + m_Height)));
bool HasWater = false;
bool HasLava = false;
@ -863,7 +887,10 @@ void cEntity::TickBurning(cChunk & a_Chunk)
m_TicksSinceLastLavaDamage++;
if (m_TicksSinceLastLavaDamage >= LAVA_TICKS_PER_DAMAGE)
{
TakeDamage(dtLavaContact, NULL, LAVA_DAMAGE, 0);
if (!m_IsFireproof)
{
TakeDamage(dtLavaContact, NULL, LAVA_DAMAGE, 0);
}
m_TicksSinceLastLavaDamage = 0;
}
}
@ -881,7 +908,10 @@ void cEntity::TickBurning(cChunk & a_Chunk)
m_TicksSinceLastFireDamage++;
if (m_TicksSinceLastFireDamage >= FIRE_TICKS_PER_DAMAGE)
{
TakeDamage(dtFireContact, NULL, FIRE_DAMAGE, 0);
if (!m_IsFireproof)
{
TakeDamage(dtFireContact, NULL, FIRE_DAMAGE, 0);
}
m_TicksSinceLastFireDamage = 0;
}
}
@ -981,13 +1011,13 @@ void cEntity::HandleAir(void)
}
else
{
m_AirTickTimer -= 1;
m_AirTickTimer--;
}
}
else
{
// Reduce air supply
m_AirLevel -= 1;
m_AirLevel--;
}
}
else
@ -1040,6 +1070,16 @@ void cEntity::SetMaxHealth(int a_MaxHealth)
/// Sets whether the entity is fireproof
void cEntity::SetIsFireproof(bool a_IsFireproof)
{
m_IsFireproof = a_IsFireproof;
}
/// Puts the entity on fire for the specified amount of ticks
void cEntity::StartBurning(int a_TicksLeftBurning)
{
@ -1099,15 +1139,15 @@ void cEntity::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ)
void cEntity::BroadcastMovementUpdate(const cClientHandle * a_Exclude)
{
//We need to keep updating the clients when there is movement or if there was a change in speed and after 2 ticks
if( (m_Speed.SqrLength() > 0.0004f || m_bDirtySpeed) && (m_World->GetWorldAge() - m_TimeLastSpeedPacket >= 2))
// Send velocity packet every two ticks if: speed is not negligible or speed was set (as indicated by the DirtySpeed flag)
if (((m_Speed.SqrLength() > 0.0004f) || m_bDirtySpeed) && ((m_World->GetWorldAge() - m_TimeLastSpeedPacket) >= 2))
{
m_World->BroadcastEntityVelocity(*this,a_Exclude);
m_bDirtySpeed = false;
m_TimeLastSpeedPacket = m_World->GetWorldAge();
}
//Have to process position related packets this every two ticks
// Have to process position related packets this every two ticks
if (m_World->GetWorldAge() % 2 == 0)
{
int DiffX = (int) (floor(GetPosX() * 32.0) - floor(m_LastPosX * 32.0));
@ -1469,7 +1509,7 @@ void cEntity::SteerVehicle(float a_Forward, float a_Sideways)
Vector3d cEntity::GetLookVector(void) const
{
Matrix4d m;
m.Init(Vector3f(), 0, m_Rot.x, -m_Rot.y);
m.Init(Vector3d(), 0, m_Rot.x, -m_Rot.y);
Vector3d Look = m.Transform(Vector3d(0, 0, 1));
return Look;
}

View File

@ -32,6 +32,8 @@
#define POSZ_TOINT (int)floor(GetPosZ())
#define POS_TOINT Vector3i(POSXTOINT, POSYTOINT, POSZTOINT)
#define GET_AND_VERIFY_CURRENT_CHUNK(ChunkVarName, X, Z) cChunk * ChunkVarName = a_Chunk.GetNeighborChunk(X, Z); if ((ChunkVarName == NULL) || !ChunkVarName->IsValid()) { return; }
@ -88,23 +90,42 @@ public:
} ;
// tolua_end
enum
enum eEntityStatus
{
ENTITY_STATUS_HURT = 2,
ENTITY_STATUS_DEAD = 3,
ENTITY_STATUS_WOLF_TAMING = 6,
ENTITY_STATUS_WOLF_TAMED = 7,
ENTITY_STATUS_WOLF_SHAKING = 8,
ENTITY_STATUS_EATING_ACCEPTED = 9,
ENTITY_STATUS_SHEEP_EATING = 10,
ENTITY_STATUS_GOLEM_ROSING = 11,
ENTITY_STATUS_VILLAGER_HEARTS = 12,
ENTITY_STATUS_VILLAGER_ANGRY = 13,
ENTITY_STATUS_VILLAGER_HAPPY = 14,
ENTITY_STATUS_WITCH_MAGICKING = 15,
// TODO: Investiagate 0, 1, and 5 as Wiki.vg is not certain
// Entity becomes coloured red
esGenericHurt = 2,
// Entity plays death animation (entity falls to ground)
esGenericDead = 3,
// Iron Golem plays attack animation (arms lift and fall)
esIronGolemAttacking = 4,
// Wolf taming particles spawn (smoke)
esWolfTaming = 6,
// Wolf tamed particles spawn (hearts)
esWolfTamed = 7,
// Wolf plays water removal animation (shaking and water particles)
esWolfDryingWater = 8,
// Informs client that eating was accepted
esPlayerEatingAccepted = 9,
// Sheep plays eating animation (head lowers to ground)
esSheepEating = 10,
// Iron Golem holds gift to villager children
esIronGolemGivingPlant = 11,
// Villager spawns heart particles
esVillagerBreeding = 12,
// Villager spawns thunderclound particles
esVillagerAngry = 13,
// Villager spawns green crosses
esVillagerHappy = 14,
// Witch spawns magic particle (TODO: investigation into what this is)
esWitchMagicking = 15,
// It seems 16 (zombie conversion) is now done with metadata
ENTITY_STATUS_FIREWORK_EXPLODE= 17,
// Informs client to explode a firework based on its metadata
esFireworkExploding = 17,
} ;
enum
@ -118,7 +139,8 @@ public:
BURN_TICKS = 200, ///< How long to keep an entity burning after it has stood in lava / fire
MAX_AIR_LEVEL = 300, ///< Maximum air an entity can have
DROWNING_TICKS = 20, ///< Number of ticks per heart of damage
VOID_BOUNDARY = -46 ///< At what position Y to begin applying void damage
VOID_BOUNDARY = -46, ///< At what position Y to begin applying void damage
FALL_DAMAGE_HEIGHT = 4 ///< At what position Y fall damage is applied
} ;
cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height);
@ -159,7 +181,7 @@ public:
cWorld * GetWorld(void) const { return m_World; }
double GetHeadYaw (void) const { return m_HeadYaw; }
double GetHeadYaw (void) const { return m_HeadYaw; } // In degrees
double GetHeight (void) const { return m_Height; }
double GetMass (void) const { return m_Mass; }
const Vector3d & GetPosition (void) const { return m_Pos; }
@ -167,9 +189,9 @@ public:
double GetPosY (void) const { return m_Pos.y; }
double GetPosZ (void) const { return m_Pos.z; }
const Vector3d & GetRot (void) const { return m_Rot; } // OBSOLETE, use individual GetYaw(), GetPitch, GetRoll() components
double GetYaw (void) const { return m_Rot.x; }
double GetPitch (void) const { return m_Rot.y; }
double GetRoll (void) const { return m_Rot.z; }
double GetYaw (void) const { return m_Rot.x; } // In degrees, [-180, +180)
double GetPitch (void) const { return m_Rot.y; } // In degrees, [-180, +180), but normal client clips to [-90, +90]
double GetRoll (void) const { return m_Rot.z; } // In degrees, unused in current client
Vector3d GetLookVector(void) const;
const Vector3d & GetSpeed (void) const { return m_Speed; }
double GetSpeedX (void) const { return m_Speed.x; }
@ -189,9 +211,9 @@ public:
void SetPosition(double a_PosX, double a_PosY, double a_PosZ);
void SetPosition(const Vector3d & a_Pos) { SetPosition(a_Pos.x, a_Pos.y, a_Pos.z); }
void SetRot (const Vector3f & a_Rot); // OBSOLETE, use individual SetYaw(), SetPitch(), SetRoll() components
void SetYaw (double a_Yaw);
void SetPitch (double a_Pitch);
void SetRoll (double a_Roll);
void SetYaw (double a_Yaw); // In degrees, normalizes to [-180, +180)
void SetPitch (double a_Pitch); // In degrees, normalizes to [-180, +180)
void SetRoll (double a_Roll); // In degrees, normalizes to [-180, +180)
void SetSpeed (double a_SpeedX, double a_SpeedY, double a_SpeedZ);
void SetSpeed (const Vector3d & a_Speed) { SetSpeed(a_Speed.x, a_Speed.y, a_Speed.z); }
void SetSpeedX (double a_SpeedX);
@ -307,6 +329,11 @@ public:
int GetMaxHealth(void) const { return m_MaxHealth; }
/// Sets whether the entity is fireproof
void SetIsFireproof(bool a_IsFireproof);
bool IsFireproof(void) const { return m_IsFireproof; }
/// Puts the entity on fire for the specified amount of ticks
void StartBurning(int a_TicksLeftBurning);
@ -413,6 +440,9 @@ protected:
cWorld * m_World;
/// Whether the entity is capable of taking fire or lava damage.
bool m_IsFireproof;
/// Time, in ticks, since the last damage dealt by being on fire. Valid only if on fire (IsOnFire())
int m_TicksSinceLastBurnDamage;

View File

@ -132,7 +132,7 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
return;
}
int PosY = (int)floor(GetPosY());
int PosY = POSY_TOINT;
if ((PosY <= 0) || (PosY >= cChunkDef::Height))
{
// Outside the world, just process normal falling physics
@ -141,8 +141,8 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
return;
}
int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width;
int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width;
int RelPosX = POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width;
int RelPosZ = POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width;
cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ);
if (Chunk == NULL)
{
@ -195,7 +195,7 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
super::HandlePhysics(a_Dt, *Chunk);
}
if (m_bIsOnDetectorRail && !Vector3i((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ())).Equals(m_DetectorRailPosition))
if (m_bIsOnDetectorRail && !Vector3i(POSX_TOINT, POSY_TOINT, POSZ_TOINT).Equals(m_DetectorRailPosition))
{
m_World->SetBlock(m_DetectorRailPosition.x, m_DetectorRailPosition.y, m_DetectorRailPosition.z, E_BLOCK_DETECTOR_RAIL, m_World->GetBlockMeta(m_DetectorRailPosition) & 0x07);
m_bIsOnDetectorRail = false;
@ -203,7 +203,7 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
else if (WasDetectorRail)
{
m_bIsOnDetectorRail = true;
m_DetectorRailPosition = Vector3i((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()));
m_DetectorRailPosition = Vector3i(POSX_TOINT, POSY_TOINT, POSZ_TOINT);
}
// Broadcast positioning changes to client
@ -719,11 +719,11 @@ bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta)
{
if (GetSpeedZ() > 0)
{
BLOCKTYPE Block = m_World->GetBlock((int)floor(GetPosX()), (int)floor(GetPosY()), (int)ceil(GetPosZ()));
BLOCKTYPE Block = m_World->GetBlock(POSX_TOINT, POSY_TOINT, (int)ceil(GetPosZ()));
if (!IsBlockRail(Block) && cBlockInfo::IsSolid(Block))
{
// We could try to detect a block in front based purely on coordinates, but xoft made a bounding box system - why not use? :P
cBoundingBox bbBlock(Vector3d((int)floor(GetPosX()), (int)floor(GetPosY()), (int)ceil(GetPosZ())), 0.5, 1);
cBoundingBox bbBlock(Vector3d(POSX_TOINT, POSY_TOINT, (int)ceil(GetPosZ())), 0.5, 1);
cBoundingBox bbMinecart(Vector3d(GetPosX(), floor(GetPosY()), GetPosZ()), GetWidth() / 2, GetHeight());
if (bbBlock.DoesIntersect(bbMinecart))
@ -736,10 +736,10 @@ bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta)
}
else if (GetSpeedZ() < 0)
{
BLOCKTYPE Block = m_World->GetBlock((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()) - 1);
BLOCKTYPE Block = m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT - 1);
if (!IsBlockRail(Block) && cBlockInfo::IsSolid(Block))
{
cBoundingBox bbBlock(Vector3d((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()) - 1), 0.5, 1);
cBoundingBox bbBlock(Vector3d(POSX_TOINT, POSY_TOINT, POSZ_TOINT - 1), 0.5, 1);
cBoundingBox bbMinecart(Vector3d(GetPosX(), floor(GetPosY()), GetPosZ() - 1), GetWidth() / 2, GetHeight());
if (bbBlock.DoesIntersect(bbMinecart))
@ -756,10 +756,10 @@ bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta)
{
if (GetSpeedX() > 0)
{
BLOCKTYPE Block = m_World->GetBlock((int)ceil(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()));
BLOCKTYPE Block = m_World->GetBlock((int)ceil(GetPosX()), POSY_TOINT, POSZ_TOINT);
if (!IsBlockRail(Block) && cBlockInfo::IsSolid(Block))
{
cBoundingBox bbBlock(Vector3d((int)ceil(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ())), 0.5, 1);
cBoundingBox bbBlock(Vector3d((int)ceil(GetPosX()), POSY_TOINT, POSZ_TOINT), 0.5, 1);
cBoundingBox bbMinecart(Vector3d(GetPosX(), floor(GetPosY()), GetPosZ()), GetWidth() / 2, GetHeight());
if (bbBlock.DoesIntersect(bbMinecart))
@ -772,10 +772,10 @@ bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta)
}
else if (GetSpeedX() < 0)
{
BLOCKTYPE Block = m_World->GetBlock((int)floor(GetPosX()) - 1, (int)floor(GetPosY()), (int)floor(GetPosZ()));
BLOCKTYPE Block = m_World->GetBlock(POSX_TOINT - 1, POSY_TOINT, POSZ_TOINT);
if (!IsBlockRail(Block) && cBlockInfo::IsSolid(Block))
{
cBoundingBox bbBlock(Vector3d((int)floor(GetPosX()) - 1, (int)floor(GetPosY()), (int)floor(GetPosZ())), 0.5, 1);
cBoundingBox bbBlock(Vector3d(POSX_TOINT - 1, POSY_TOINT, POSZ_TOINT), 0.5, 1);
cBoundingBox bbMinecart(Vector3d(GetPosX() - 1, floor(GetPosY()), GetPosZ()), GetWidth() / 2, GetHeight());
if (bbBlock.DoesIntersect(bbMinecart))
@ -793,10 +793,10 @@ bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta)
case E_META_RAIL_CURVED_ZP_XM:
case E_META_RAIL_CURVED_ZP_XP:
{
BLOCKTYPE BlockXM = m_World->GetBlock((int)floor(GetPosX()) - 1, (int)floor(GetPosY()), (int)floor(GetPosZ()));
BLOCKTYPE BlockXP = m_World->GetBlock((int)floor(GetPosX()) + 1, (int)floor(GetPosY()), (int)floor(GetPosZ()));
BLOCKTYPE BlockZM = m_World->GetBlock((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()) + 1);
BLOCKTYPE BlockZP = m_World->GetBlock((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()) + 1);
BLOCKTYPE BlockXM = m_World->GetBlock(POSX_TOINT - 1, POSY_TOINT, POSZ_TOINT);
BLOCKTYPE BlockXP = m_World->GetBlock(POSX_TOINT + 1, POSY_TOINT, POSZ_TOINT);
BLOCKTYPE BlockZM = m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT - 1);
BLOCKTYPE BlockZP = m_World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT + 1);
if (
(!IsBlockRail(BlockXM) && cBlockInfo::IsSolid(BlockXM)) ||
(!IsBlockRail(BlockXP) && cBlockInfo::IsSolid(BlockXP)) ||
@ -805,7 +805,7 @@ bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta)
)
{
SetSpeed(0, 0, 0);
SetPosition((int)floor(GetPosX()) + 0.5, GetPosY(), (int)floor(GetPosZ()) + 0.5);
SetPosition(POSX_TOINT + 0.5, GetPosY(), POSZ_TOINT + 0.5);
return true;
}
break;
@ -822,7 +822,7 @@ bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta)
{
cMinecartCollisionCallback MinecartCollisionCallback(GetPosition(), GetHeight(), GetWidth(), GetUniqueID(), ((m_Attachee == NULL) ? -1 : m_Attachee->GetUniqueID()));
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk((int)floor(GetPosX()), (int)floor(GetPosZ()), ChunkX, ChunkZ);
cChunkDef::BlockToChunk(POSX_TOINT, POSZ_TOINT, ChunkX, ChunkZ);
m_World->ForEachEntityInChunk(ChunkX, ChunkZ, MinecartCollisionCallback);
if (!MinecartCollisionCallback.FoundIntersection())

View File

@ -98,45 +98,44 @@ void cPickup::Tick(float a_Dt, cChunk & a_Chunk)
if (!m_bCollected)
{
int BlockY = (int) floor(GetPosY());
int BlockY = POSY_TOINT;
int BlockX = POSX_TOINT;
int BlockZ = POSZ_TOINT;
if ((BlockY >= 0) && (BlockY < cChunkDef::Height)) // Don't do anything except for falling when outside the world
{
int BlockX = (int) floor(GetPosX());
int BlockZ = (int) floor(GetPosZ());
// Position might have changed due to physics. So we have to make sure we have the correct chunk.
cChunk * CurrentChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ);
if (CurrentChunk != NULL) // Make sure the chunk is loaded
{
int RelBlockX = BlockX - (CurrentChunk->GetPosX() * cChunkDef::Width);
int RelBlockZ = BlockZ - (CurrentChunk->GetPosZ() * cChunkDef::Width);
GET_AND_VERIFY_CURRENT_CHUNK(CurrentChunk, BlockX, BlockZ)
int RelBlockX = BlockX - (CurrentChunk->GetPosX() * cChunkDef::Width);
int RelBlockZ = BlockZ - (CurrentChunk->GetPosZ() * cChunkDef::Width);
// If the pickup is on the bottommost block position, make it think the void is made of air: (#131)
BLOCKTYPE BlockBelow = (BlockY > 0) ? CurrentChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR;
BLOCKTYPE BlockIn = CurrentChunk->GetBlock(RelBlockX, BlockY, RelBlockZ);
// If the pickup is on the bottommost block position, make it think the void is made of air: (#131)
BLOCKTYPE BlockBelow = (BlockY > 0) ? CurrentChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR;
BLOCKTYPE BlockIn = CurrentChunk->GetBlock(RelBlockX, BlockY, RelBlockZ);
if (
IsBlockLava(BlockBelow) || (BlockBelow == E_BLOCK_FIRE) ||
IsBlockLava(BlockIn) || (BlockIn == E_BLOCK_FIRE)
)
if (
IsBlockLava(BlockBelow) || (BlockBelow == E_BLOCK_FIRE) ||
IsBlockLava(BlockIn) || (BlockIn == E_BLOCK_FIRE)
)
{
m_bCollected = true;
m_Timer = 0; // We have to reset the timer.
m_Timer += a_Dt; // In case we have to destroy the pickup in the same tick.
if (m_Timer > 500.f)
{
m_bCollected = true;
m_Timer = 0; // We have to reset the timer.
m_Timer += a_Dt; // In case we have to destroy the pickup in the same tick.
if (m_Timer > 500.f)
{
Destroy(true);
return;
}
Destroy(true);
return;
}
}
if (!IsDestroyed()) // Don't try to combine if someone has tried to combine me
if (!IsDestroyed()) // Don't try to combine if someone has tried to combine me
{
cPickupCombiningCallback PickupCombiningCallback(GetPosition(), this);
m_World->ForEachEntity(PickupCombiningCallback); // Not ForEachEntityInChunk, otherwise pickups don't combine across chunk boundaries
if (PickupCombiningCallback.FoundMatchingPickup())
{
cPickupCombiningCallback PickupCombiningCallback(GetPosition(), this);
m_World->ForEachEntity(PickupCombiningCallback); // Not ForEachEntityInChunk, otherwise pickups don't combine across chunk boundaries
if (PickupCombiningCallback.FoundMatchingPickup())
{
m_World->BroadcastEntityMetadata(*this);
}
m_World->BroadcastEntityMetadata(*this);
}
}
}
@ -156,7 +155,7 @@ void cPickup::Tick(float a_Dt, cChunk & a_Chunk)
return;
}
if (GetPosY() < -8) // Out of this world and no more visible!
if (GetPosY() < VOID_BOUNDARY) // Out of this world and no more visible!
{
Destroy(true);
return;

View File

@ -49,9 +49,6 @@ public:
bool IsPlayerCreated(void) const { return m_bIsPlayerCreated; } // tolua_export
private:
Vector3d m_ResultingSpeed; //Can be used to modify the resulting speed for the current tick ;)
Vector3d m_WaterSpeed;
/** The number of ticks that the entity has existed / timer between collect and destroy; in msec */
float m_Timer;

View File

@ -85,9 +85,10 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
if (!LoadFromDisk())
{
m_Inventory.Clear();
SetPosX(cRoot::Get()->GetDefaultWorld()->GetSpawnX());
SetPosY(cRoot::Get()->GetDefaultWorld()->GetSpawnY());
SetPosZ(cRoot::Get()->GetDefaultWorld()->GetSpawnZ());
cWorld * DefaultWorld = cRoot::Get()->GetDefaultWorld();
SetPosX(DefaultWorld->GetSpawnX());
SetPosY(DefaultWorld->GetSpawnY());
SetPosZ(DefaultWorld->GetSpawnZ());
LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}",
a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ()
@ -437,7 +438,7 @@ void cPlayer::SetTouchGround(bool a_bTouchGround)
cWorld * World = GetWorld();
if ((GetPosY() >= 0) && (GetPosY() < cChunkDef::Height))
{
BLOCKTYPE BlockType = World->GetBlock((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()));
BLOCKTYPE BlockType = World->GetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT);
if (BlockType != E_BLOCK_AIR)
{
m_bTouchGround = true;
@ -466,7 +467,7 @@ void cPlayer::SetTouchGround(bool a_bTouchGround)
TakeDamage(dtFalling, NULL, Damage, Damage, 0);
// Fall particles
GetWorld()->BroadcastSoundParticleEffect(2006, (int)floor(GetPosX()), (int)GetPosY() - 1, (int)floor(GetPosZ()), Damage /* Used as particle effect speed modifier */);
GetWorld()->BroadcastSoundParticleEffect(2006, POSX_TOINT, (int)GetPosY() - 1, POSZ_TOINT, Damage /* Used as particle effect speed modifier */);
}
m_LastGroundHeight = (float)GetPosY();
@ -590,7 +591,7 @@ void cPlayer::FinishEating(void)
m_EatingFinishTick = -1;
// Send the packets:
m_ClientHandle->SendEntityStatus(*this, ENTITY_STATUS_EATING_ACCEPTED);
m_ClientHandle->SendEntityStatus(*this, esPlayerEatingAccepted);
m_World->BroadcastEntityAnimation(*this, 0);
m_World->BroadcastEntityMetadata(*this);
@ -1124,6 +1125,17 @@ void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ)
void cPlayer::SendRotation(double a_YawDegrees, double a_PitchDegrees)
{
SetYaw(a_YawDegrees);
SetPitch(a_PitchDegrees);
m_ClientHandle->SendPlayerMoveLook();
}
Vector3d cPlayer::GetThrowStartPos(void) const
{
Vector3d res = GetEyePosition();
@ -1154,9 +1166,9 @@ Vector3d cPlayer::GetThrowSpeed(double a_SpeedCoeff) const
void cPlayer::ForceSetSpeed(Vector3d a_Direction)
void cPlayer::ForceSetSpeed(const Vector3d & a_Speed)
{
SetSpeed(a_Direction);
SetSpeed(a_Speed);
m_ClientHandle->SendEntityVelocity(*this);
}
@ -1508,22 +1520,16 @@ void cPlayer::LoadPermissionsFromDisk()
cIniFile IniFile;
if (IniFile.ReadFile("users.ini"))
{
std::string Groups = IniFile.GetValue(m_PlayerName, "Groups", "");
if (!Groups.empty())
AString Groups = IniFile.GetValueSet(m_PlayerName, "Groups", "Default");
AStringVector Split = StringSplitAndTrim(Groups, ",");
for (AStringVector::const_iterator itr = Split.begin(), end = Split.end(); itr != end; ++itr)
{
AStringVector Split = StringSplitAndTrim(Groups, ",");
for (AStringVector::const_iterator itr = Split.begin(), end = Split.end(); itr != end; ++itr)
if (!cRoot::Get()->GetGroupManager()->ExistsGroup(*itr))
{
if (!cRoot::Get()->GetGroupManager()->ExistsGroup(*itr))
{
LOGWARNING("The group %s for player %s was not found!", itr->c_str(), m_PlayerName.c_str());
}
AddToGroup(*itr);
LOGWARNING("The group %s for player %s was not found!", itr->c_str(), m_PlayerName.c_str());
}
}
else
{
AddToGroup("Default");
AddToGroup(*itr);
}
AString Color = IniFile.GetValue(m_PlayerName, "Color", "-");
@ -1535,8 +1541,10 @@ void cPlayer::LoadPermissionsFromDisk()
else
{
cGroupManager::GenerateDefaultUsersIni(IniFile);
IniFile.AddValue("Groups", m_PlayerName, "Default");
AddToGroup("Default");
}
IniFile.WriteFile("users.ini");
ResolvePermissions();
}
@ -1888,9 +1896,9 @@ void cPlayer::ApplyFoodExhaustionFromMovement()
void cPlayer::Detach()
{
super::Detach();
int PosX = (int)floor(GetPosX());
int PosY = (int)floor(GetPosY());
int PosZ = (int)floor(GetPosZ());
int PosX = POSX_TOINT;
int PosY = POSY_TOINT;
int PosZ = POSZ_TOINT;
// Search for a position within an area to teleport player after detachment
// Position must be solid land, and occupied by a nonsolid block

View File

@ -129,6 +129,12 @@ public:
// tolua_begin
/** Sends the "look" packet to the player, forcing them to set their rotation to the specified values.
a_YawDegrees is clipped to range [-180, +180),
a_PitchDegrees is clipped to range [-180, +180) but the client only uses [-90, +90]
*/
void SendRotation(double a_YawDegrees, double a_PitchDegrees);
/** Returns the position where projectiles thrown by this player should start, player eye position + adjustment */
Vector3d GetThrowStartPos(void) const;
@ -175,7 +181,7 @@ public:
void LoginSetGameMode(eGameMode a_GameMode);
/** Forces the player to move in the given direction. */
void ForceSetSpeed(Vector3d a_Direction); // tolua_export
void ForceSetSpeed(const Vector3d & a_Speed); // tolua_export
/** Tries to move to a new position, with attachment-related checks (y == -999) */
void MoveTo(const Vector3d & a_NewPos); // tolua_export

View File

@ -371,13 +371,14 @@ void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
SetYawFromSpeed();
SetPitchFromSpeed();
// DEBUG:
/*
LOGD("Projectile %d: pos {%.02f, %.02f, %.02f}, speed {%.02f, %.02f, %.02f}, rot {%.02f, %.02f}",
m_UniqueID,
GetPosX(), GetPosY(), GetPosZ(),
GetSpeedX(), GetSpeedY(), GetSpeedZ(),
GetYaw(), GetPitch()
);
*/
}
@ -439,6 +440,7 @@ cArrowEntity::cArrowEntity(cPlayer & a_Player, double a_Force) :
m_IsCritical((a_Force >= 1)),
m_Timer(0),
m_HitGroundTimer(0),
m_HasTeleported(false),
m_bIsCollected(false),
m_HitBlockPos(0, 0, 0)
{
@ -561,12 +563,12 @@ void cArrowEntity::Tick(float a_Dt, cChunk & a_Chunk)
// We can afford to do this because xoft's algorithm for trajectory is near perfect, so things are pretty close anyway without sync
// Besides, this seems to be what the vanilla server does, note how arrows teleport half a second after they hit to the server position
if (m_HitGroundTimer != -1) // Sent a teleport already, don't do again
if (!m_HasTeleported) // Sent a teleport already, don't do again
{
if (m_HitGroundTimer > 1000.f) // Send after a second, could be less, but just in case
{
m_World->BroadcastTeleportEntity(*this);
m_HitGroundTimer = -1;
m_HasTeleported = true;
}
else
{
@ -609,6 +611,32 @@ cThrownEggEntity::cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y,
void cThrownEggEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
TrySpawnChicken(a_HitPos);
Destroy();
}
void cThrownEggEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
{
int TotalDamage = 0;
// TODO: If entity is Ender Crystal, destroy it
TrySpawnChicken(a_HitPos);
a_EntityHit.TakeDamage(dtRangedAttack, this, TotalDamage, 1);
Destroy(true);
}
void cThrownEggEntity::TrySpawnChicken(const Vector3d & a_HitPos)
{
if (m_World->GetTickRandomNumber(7) == 1)
{
@ -621,7 +649,6 @@ void cThrownEggEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_H
m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
}
Destroy();
}
@ -642,16 +669,40 @@ cThrownEnderPearlEntity::cThrownEnderPearlEntity(cEntity * a_Creator, double a_X
void cThrownEnderPearlEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
// TODO: Tweak a_HitPos based on block face.
TeleportCreator(a_HitPos);
Destroy();
}
void cThrownEnderPearlEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
{
int TotalDamage = 0;
// TODO: If entity is Ender Crystal, destroy it
TeleportCreator(a_HitPos);
a_EntityHit.TakeDamage(dtRangedAttack, this, TotalDamage, 1);
Destroy(true);
}
void cThrownEnderPearlEntity::TeleportCreator(const Vector3d & a_HitPos)
{
// Teleport the creator here, make them take 5 damage:
if (m_Creator != NULL)
{
// TODO: The coords might need some tweaking based on the block face
m_Creator->TeleportToCoords(a_HitPos.x + 0.5, a_HitPos.y + 1.7, a_HitPos.z + 0.5);
m_Creator->TakeDamage(dtEnderPearl, this, 5, 0);
}
Destroy();
}
@ -695,6 +746,7 @@ void cThrownSnowballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d &
TotalDamage = 1;
}
}
// TODO: If entity is Ender Crystal, destroy it
a_EntityHit.TakeDamage(dtRangedAttack, this, TotalDamage, 1);
Destroy(true);
@ -790,7 +842,7 @@ void cFireworkEntity::Tick(float a_Dt, cChunk & a_Chunk)
if (m_ExplodeTimer == m_FireworkItem.m_FireworkItem.m_FlightTimeInTicks)
{
m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_FIREWORK_EXPLODE);
m_World->BroadcastEntityStatus(*this, esFireworkExploding);
Destroy();
}

View File

@ -166,6 +166,9 @@ protected:
/// Timer for client arrow position confirmation via TeleportEntity
float m_HitGroundTimer;
// Whether the arrow has already been teleported into the proper position in the ground.
bool m_HasTeleported;
/// If true, the arrow is in the process of being collected - don't go to anyone else
bool m_bIsCollected;
@ -205,7 +208,11 @@ protected:
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
// Randomly decides whether to spawn a chicken where the egg lands.
void TrySpawnChicken(const Vector3d & a_HitPos);
// tolua_begin
} ;
@ -233,6 +240,10 @@ protected:
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
// Teleports the creator where the ender pearl lands.
void TeleportCreator(const Vector3d & a_HitPos);
// tolua_begin

View File

@ -172,3 +172,13 @@ float cFastRandom::NextFloat(float a_Range, int a_Salt)
int cFastRandom::GenerateRandomInteger(int a_Begin, int a_End)
{
cFastRandom Random;
return Random.NextInt(a_End - a_Begin + 1) + a_Begin;
}

View File

@ -43,6 +43,9 @@ public:
/// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M; a_Salt is additional source of randomness
float NextFloat(float a_Range, int a_Salt);
/** Returns a random int in the range [a_Begin .. a_End] */
int GenerateRandomInteger(int a_Begin, int a_End);
protected:
int m_Seed;

View File

@ -56,7 +56,6 @@ void cFurnaceRecipe::ReloadRecipes(void)
std::ifstream f;
char a_File[] = "furnace.txt";
f.open(a_File, std::ios::in);
std::string input;
if (!f.good())
{

View File

@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../")
file(GLOB SOURCE
"*.cpp"
"*.h"
)
add_library(Generating ${SOURCE})

View File

@ -200,13 +200,14 @@ void cCaveTunnel::Randomize(cNoise & a_Noise)
for (int i = 0; i < 4; i++)
{
// For each already present point, insert a point in between it and its predecessor, shifted randomly.
int PrevX = m_Points.front().m_BlockX;
int PrevY = m_Points.front().m_BlockY;
int PrevZ = m_Points.front().m_BlockZ;
int PrevR = m_Points.front().m_Radius;
cCaveDefPoint & Point = m_Points.front();
int PrevX = Point.m_BlockX;
int PrevY = Point.m_BlockY;
int PrevZ = Point.m_BlockZ;
int PrevR = Point.m_Radius;
cCaveDefPoints Pts;
Pts.reserve(m_Points.size() * 2 + 1);
Pts.push_back(m_Points.front());
Pts.push_back(Point);
for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr)
{
int Random = a_Noise.IntNoise3DInt(PrevX, PrevY, PrevZ + i) / 11;
@ -244,11 +245,12 @@ bool cCaveTunnel::RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints &
a_Dst.clear();
a_Dst.reserve(Num * 2 + 2);
cCaveDefPoints::const_iterator itr = a_Src.begin() + 1;
a_Dst.push_back(a_Src.front());
int PrevX = a_Src.front().m_BlockX;
int PrevY = a_Src.front().m_BlockY;
int PrevZ = a_Src.front().m_BlockZ;
int PrevR = a_Src.front().m_Radius;
const cCaveDefPoint & Source = a_Src.front();
a_Dst.push_back(Source);
int PrevX = Source.m_BlockX;
int PrevY = Source.m_BlockY;
int PrevZ = Source.m_BlockZ;
int PrevR = Source.m_Radius;
for (int i = 0; i <= Num; ++i, ++itr)
{
int dx = itr->m_BlockX - PrevX;
@ -310,9 +312,10 @@ void cCaveTunnel::FinishLinear(void)
std::swap(Pts, m_Points);
m_Points.reserve(Pts.size() * 3);
int PrevX = Pts.front().m_BlockX;
int PrevY = Pts.front().m_BlockY;
int PrevZ = Pts.front().m_BlockZ;
cCaveDefPoint & PrevPoint = Pts.front();
int PrevX = PrevPoint.m_BlockX;
int PrevY = PrevPoint.m_BlockY;
int PrevZ = PrevPoint.m_BlockZ;
for (cCaveDefPoints::const_iterator itr = Pts.begin() + 1, end = Pts.end(); itr != end; ++itr)
{
int x1 = itr->m_BlockX;
@ -433,9 +436,10 @@ void cCaveTunnel::FinishLinear(void)
void cCaveTunnel::CalcBoundingBox(void)
{
m_MinBlockX = m_MaxBlockX = m_Points.front().m_BlockX;
m_MinBlockY = m_MaxBlockY = m_Points.front().m_BlockY;
m_MinBlockZ = m_MaxBlockZ = m_Points.front().m_BlockZ;
cCaveDefPoint & Point = m_Points.front();
m_MinBlockX = m_MaxBlockX = Point.m_BlockX;
m_MinBlockY = m_MaxBlockY = Point.m_BlockY;
m_MinBlockZ = m_MaxBlockZ = Point.m_BlockZ;
for (cCaveDefPoints::const_iterator itr = m_Points.begin() + 1, end = m_Points.end(); itr != end; ++itr)
{
m_MinBlockX = std::min(m_MinBlockX, itr->m_BlockX - itr->m_Radius);

View File

@ -70,6 +70,40 @@ public:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Performance test of the NetherFort generator:
/*
#include "OSSupport/Timer.h"
static class cNetherFortPerfTest
{
public:
cNetherFortPerfTest(void)
{
cTimer Timer;
long long StartTime = Timer.GetNowTime();
const int GridSize = 512;
const int MaxDepth = 12;
const int NumIterations = 100;
for (int i = 0; i < NumIterations; i++)
{
cNetherFortGen FortGen(i, GridSize, MaxDepth);
delete new cNetherFortGen::cNetherFort(FortGen, 0, 0, GridSize, MaxDepth, i);
}
long long EndTime = Timer.GetNowTime();
printf("%d forts took %lld msec (%f sec) to generate\n", NumIterations, EndTime - StartTime, ((double)(EndTime - StartTime)) / 1000);
exit(0);
}
} g_PerfTest;
//*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cNetherFortGen:
@ -80,9 +114,9 @@ cNetherFortGen::cNetherFortGen(int a_Seed, int a_GridSize, int a_MaxDepth) :
m_MaxDepth(a_MaxDepth)
{
// Initialize the prefabs:
for (size_t i = 0; i < g_NetherFortPrefabs1Count; i++)
for (size_t i = 0; i < g_NetherFortPrefabsCount; i++)
{
cPrefab * Prefab = new cPrefab(g_NetherFortPrefabs1[i]);
cPrefab * Prefab = new cPrefab(g_NetherFortPrefabs[i]);
m_AllPieces.push_back(Prefab);
if (Prefab->HasConnectorType(0))
{
@ -95,15 +129,17 @@ cNetherFortGen::cNetherFortGen(int a_Seed, int a_GridSize, int a_MaxDepth) :
}
// Initialize the starting piece prefabs:
for (size_t i = 0; i < g_NetherFortStartingPrefabs1Count; i++)
for (size_t i = 0; i < g_NetherFortStartingPrefabsCount; i++)
{
m_StartingPieces.push_back(new cPrefab(g_NetherFortStartingPrefabs1[i]));
m_StartingPieces.push_back(new cPrefab(g_NetherFortStartingPrefabs[i]));
}
/*
// DEBUG: Try one round of placement:
cPlacedPieces Pieces;
cBFSPieceGenerator pg(*this, a_Seed);
pg.PlacePieces(0, 64, 0, a_MaxDepth, Pieces);
*/
}
@ -256,6 +292,15 @@ cPieces cNetherFortGen::GetStartingPieces(void)
int cNetherFortGen::GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cConnector & a_ExistingConnector, const cPiece & a_NewPiece)
{
return ((const cPrefab &)a_NewPiece).GetPieceWeight(a_PlacedPiece, a_ExistingConnector);
}
void cNetherFortGen::PiecePlaced(const cPiece & a_Piece)
{
UNUSED(a_Piece);

View File

@ -26,6 +26,7 @@ public:
virtual ~cNetherFortGen();
protected:
friend class cNetherFortPerfTest; // fwd: NetherFortGen.cpp
class cNetherFort; // fwd: NetherFortGen.cpp
typedef std::list<cNetherFort *> cNetherForts;
@ -77,6 +78,7 @@ protected:
// cPiecePool overrides:
virtual cPieces GetPiecesWithConnector(int a_ConnectorType) override;
virtual cPieces GetStartingPieces(void) override;
virtual int GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cConnector & a_ExistingConnector, const cPiece & a_NewPiece) override;
virtual void PiecePlaced(const cPiece & a_Piece) override;
virtual void Reset(void) override;
} ;

View File

@ -392,14 +392,17 @@ bool cPieceGenerator::TryPlacePieceAtConnector(
Connections.reserve(AvailablePieces.size());
Vector3i ConnPos = a_Connector.m_Pos; // The position at which the new connector should be placed - 1 block away from the connector
AddFaceDirection(ConnPos.x, ConnPos.y, ConnPos.z, a_Connector.m_Direction);
/*
// DEBUG:
printf("Placing piece at connector pos {%d, %d, %d}, direction %s\n", ConnPos.x, ConnPos.y, ConnPos.z, BlockFaceToString(a_Connector.m_Direction).c_str());
//*/
int WeightTotal = 0;
for (cPieces::iterator itrP = AvailablePieces.begin(), endP = AvailablePieces.end(); itrP != endP; ++itrP)
{
// Get the relative chance of this piece being generated in this path:
int Weight = m_PiecePool.GetPieceWeight(a_ParentPiece, a_Connector, **itrP);
if (Weight <= 0)
{
continue;
}
// Try fitting each of the piece's connector:
cPiece::cConnectors Connectors = (*itrP)->GetConnectors();
for (cPiece::cConnectors::iterator itrC = Connectors.begin(), endC = Connectors.end(); itrC != endC; ++itrC)
{
@ -419,7 +422,9 @@ bool cPieceGenerator::TryPlacePieceAtConnector(
// Doesn't fit in this rotation
continue;
}
Connections.push_back(cConnection(**itrP, *itrC, NumCCWRotations));
// Fits, add it to list of possibile connections:
Connections.push_back(cConnection(**itrP, *itrC, NumCCWRotations, Weight));
WeightTotal += Weight;
} // for itrC - Connectors[]
} // for itrP - AvailablePieces[]
if (Connections.empty())
@ -427,21 +432,23 @@ bool cPieceGenerator::TryPlacePieceAtConnector(
// No available connections, bail out
return false;
}
ASSERT(WeightTotal > 0);
// Choose a random connection from the list:
int rnd = m_Noise.IntNoise3DInt(a_Connector.m_Pos.x, a_Connector.m_Pos.y, a_Connector.m_Pos.z) / 7;
cConnection & Conn = Connections[rnd % Connections.size()];
// Choose a random connection from the list, based on the weights:
int rnd = (m_Noise.IntNoise3DInt(a_Connector.m_Pos.x, a_Connector.m_Pos.y, a_Connector.m_Pos.z) / 7) % WeightTotal;
size_t ChosenIndex = 0;
for (cConnections::const_iterator itr = Connections.begin(), end = Connections.end(); itr != end; ++itr, ++ChosenIndex)
{
rnd -= itr->m_Weight;
if (rnd <= 0)
{
// This is the piece to choose
break;
}
}
cConnection & Conn = Connections[ChosenIndex];
// Place the piece:
/*
// DEBUG
printf("Chosen connector at {%d, %d, %d}, direction %s, needs %d rotations\n",
Conn.m_Connector.m_Pos.x, Conn.m_Connector.m_Pos.y, Conn.m_Connector.m_Pos.z,
BlockFaceToString(Conn.m_Connector.m_Direction).c_str(),
Conn.m_NumCCWRotations
);
//*/
Vector3i NewPos = Conn.m_Piece->RotatePos(Conn.m_Connector.m_Pos, Conn.m_NumCCWRotations);
ConnPos -= NewPos;
cPlacedPiece * PlacedPiece = new cPlacedPiece(&a_ParentPiece, *(Conn.m_Piece), ConnPos, Conn.m_NumCCWRotations);
@ -449,12 +456,6 @@ bool cPieceGenerator::TryPlacePieceAtConnector(
// Add the new piece's connectors to the list of free connectors:
cPiece::cConnectors Connectors = Conn.m_Piece->GetConnectors();
/*
// DEBUG:
printf("Adding %u connectors to the pool\n", Connectors.size() - 1);
//*/
for (cPiece::cConnectors::const_iterator itr = Connectors.begin(), end = Connectors.end(); itr != end; ++itr)
{
if (itr->m_Pos.Equals(Conn.m_Connector.m_Pos))
@ -524,10 +525,11 @@ void cPieceGenerator::DebugConnectorPool(const cPieceGenerator::cFreeConnectors
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cPieceGenerator::cConnection:
cPieceGenerator::cConnection::cConnection(cPiece & a_Piece, cPiece::cConnector & a_Connector, int a_NumCCWRotations) :
cPieceGenerator::cConnection::cConnection(cPiece & a_Piece, cPiece::cConnector & a_Connector, int a_NumCCWRotations, int a_Weight) :
m_Piece(&a_Piece),
m_Connector(a_Connector),
m_NumCCWRotations(a_NumCCWRotations)
m_NumCCWRotations(a_NumCCWRotations),
m_Weight(a_Weight)
{
}

View File

@ -85,6 +85,13 @@ typedef std::vector<cPiece *> cPieces;
// fwd:
class cPlacedPiece;
/** This class is an interface that provides pieces for the generator. It can keep track of what pieces were
placed and adjust the returned piece vectors. */
class cPiecePool
@ -101,6 +108,16 @@ public:
Multiple starting points are supported, one of the returned piece will be chosen. */
virtual cPieces GetStartingPieces(void) = 0;
/** Returns the relative weight with which the a_NewPiece is to be selected for placing under a_PlacedPiece through a_ExistingConnector.
This allows the pool to tweak the piece's chances, based on the previous pieces in the tree and the connector used.
The higher the number returned, the higher the chance the piece will be chosen. 0 means the piece will never be chosen.
*/
virtual int GetPieceWeight(
const cPlacedPiece & a_PlacedPiece,
const cPiece::cConnector & a_ExistingConnector,
const cPiece & a_NewPiece
) { return 1; }
/** Called after a piece is placed, to notify the pool that it has been used.
The pool may adjust the pieces it will return the next time. */
virtual void PiecePlaced(const cPiece & a_Piece) = 0;
@ -157,8 +174,9 @@ protected:
cPiece * m_Piece; // The piece being connected
cPiece::cConnector m_Connector; // The piece's connector being used (relative non-rotated coords)
int m_NumCCWRotations; // Number of rotations necessary to match the two connectors
int m_Weight; // Relative chance that this connection will be chosen
cConnection(cPiece & a_Piece, cPiece::cConnector & a_Connector, int a_NumCCWRotations);
cConnection(cPiece & a_Piece, cPiece::cConnector & a_Connector, int a_NumCCWRotations, int a_Weight);
};
typedef std::vector<cConnection> cConnections;

View File

@ -23,6 +23,10 @@ static const cPrefab::sDef g_TestPrefabDef =
// Size:
7, 6, 7, // SizeX = 7, SizeY = 6, SizeZ = 7
// Hitbox (relative to bounding box):
0, 0, 0, // MinX, MinY, MinZ
6, 5, 6, // MaxX, MaxY, MaxZ
// Block definitions:
".: 0: 0\n" /* 0 */
"a:112: 0\n" /* netherbrick */
@ -91,7 +95,19 @@ static const cPrefab::sDef g_TestPrefabDef =
7, /* 1, 2, 3 CCW rotations */
// Merge strategy:
cBlockArea::msImprint
cBlockArea::msImprint,
// ShouldExtendFloor:
false,
// DefaultWeight:
10,
// DepthWeight:
"",
// AddWeightIfSame:
1000,
};
static cPrefab g_TestPrefab(g_TestPrefabDef);
@ -103,15 +119,22 @@ static cPrefab g_TestPrefab(g_TestPrefabDef);
cPrefab::cPrefab(const cPrefab::sDef & a_Def) :
m_Size(a_Def.m_SizeX, a_Def.m_SizeY, a_Def.m_SizeZ),
m_HitBox(0, 0, 0, a_Def.m_SizeX - 1, a_Def.m_SizeY - 1, a_Def.m_SizeZ - 1),
m_HitBox(
a_Def.m_HitboxMinX, a_Def.m_HitboxMinY, a_Def.m_HitboxMinZ,
a_Def.m_HitboxMaxX, a_Def.m_HitboxMaxY, a_Def.m_HitboxMaxZ
),
m_AllowedRotations(a_Def.m_AllowedRotations),
m_MergeStrategy(a_Def.m_MergeStrategy)
m_MergeStrategy(a_Def.m_MergeStrategy),
m_ShouldExtendFloor(a_Def.m_ShouldExtendFloor),
m_DefaultWeight(a_Def.m_DefaultWeight),
m_AddWeightIfSame(a_Def.m_AddWeightIfSame)
{
m_BlockArea[0].Create(m_Size);
CharMap cm;
ParseCharMap(cm, a_Def.m_CharMap);
ParseBlockImage(cm, a_Def.m_Image);
ParseConnectors(a_Def.m_Connectors);
ParseDepthWeight(a_Def.m_DepthWeight);
// 1 CCW rotation:
if ((m_AllowedRotations & 0x01) != 0)
@ -142,12 +165,53 @@ cPrefab::cPrefab(const cPrefab::sDef & a_Def) :
void cPrefab::Draw(cChunkDesc & a_Dest, const cPlacedPiece * a_Placement) const
{
// Draw the basic image:
Vector3i Placement = a_Placement->GetCoords();
int ChunkStartX = a_Dest.GetChunkX() * cChunkDef::Width;
int ChunkStartZ = a_Dest.GetChunkZ() * cChunkDef::Width;
Placement.Move(-ChunkStartX, 0, -ChunkStartZ);
a_Dest.WriteBlockArea(m_BlockArea[a_Placement->GetNumCCWRotations()], Placement.x, Placement.y, Placement.z, m_MergeStrategy);
const cBlockArea & Image = m_BlockArea[a_Placement->GetNumCCWRotations()];
a_Dest.WriteBlockArea(Image, Placement.x, Placement.y, Placement.z, m_MergeStrategy);
// If requested, draw the floor (from the bottom of the prefab down to the nearest non-air)
int MaxX = Image.GetSizeX();
int MaxZ = Image.GetSizeZ();
for (int z = 0; z < MaxZ; z++)
{
int RelZ = Placement.z + z;
if ((RelZ < 0) || (RelZ >= cChunkDef::Width))
{
// Z coord outside the chunk
continue;
}
for (int x = 0; x < MaxX; x++)
{
int RelX = Placement.x + x;
if ((RelX < 0) || (RelX >= cChunkDef::Width))
{
// X coord outside the chunk
continue;
}
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
Image.GetRelBlockTypeMeta(x, 0, z, BlockType, BlockMeta);
if ((BlockType == E_BLOCK_AIR) || (BlockType == E_BLOCK_SPONGE))
{
// Do not expand air nor sponge blocks
continue;
}
for (int y = Placement.y - 1; y >= 0; y--)
{
BLOCKTYPE ExistingBlock = a_Dest.GetBlockType(RelX, y, RelZ);
if (ExistingBlock != E_BLOCK_AIR)
{
// End the expansion for this column, reached the end
break;
}
a_Dest.SetBlockTypeMeta(RelX, y, RelZ, BlockType, BlockMeta);
} // for y
} // for x
} // for z
}
@ -170,6 +234,26 @@ bool cPrefab::HasConnectorType(int a_ConnectorType) const
int cPrefab::GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cConnector & a_ExistingConnector) const
{
// Use the default or per-depth weight:
cDepthWeight::const_iterator itr = m_DepthWeight.find(a_PlacedPiece.GetDepth() + 1);
int res = (itr == m_DepthWeight.end()) ? m_DefaultWeight : itr->second;
// If the piece is the same as the parent, apply the m_AddWeightIfSame modifier:
const cPiece * ParentPiece = &a_PlacedPiece.GetPiece();
const cPiece * ThisPiece = this;
if (ThisPiece == ParentPiece)
{
res += m_AddWeightIfSame;
}
return res;
}
void cPrefab::ParseCharMap(CharMap & a_CharMapOut, const char * a_CharMapDef)
{
ASSERT(a_CharMapDef != NULL);
@ -277,6 +361,54 @@ void cPrefab::ParseConnectors(const char * a_ConnectorsDef)
void cPrefab::ParseDepthWeight(const char * a_DepthWeightDef)
{
// The member needn't be defined at all, if so, skip:
if (a_DepthWeightDef == NULL)
{
return;
}
// Split into individual records: "Record | Record | Record"
AStringVector Defs = StringSplitAndTrim(a_DepthWeightDef, "|");
// Add each record's contents:
for (AStringVector::const_iterator itr = Defs.begin(), end = Defs.end(); itr != end; ++itr)
{
// Split into components: "Depth : Weight"
AStringVector Components = StringSplitAndTrim(*itr, ":");
if (Components.size() != 2)
{
LOGWARNING("Bad prefab DepthWeight record: \"%s\", skipping.", itr->c_str());
continue;
}
// Parse depth:
int Depth = atoi(Components[0].c_str());
if ((Depth == 0) && (Components[0] != "0"))
{
LOGWARNING("Bad prefab DepthWeight record, cannot parse depth \"%s\", skipping.", Components[0].c_str());
continue;
}
// Parse weight:
int Weight = atoi(Components[1].c_str());
if ((Weight == 0) && (Components[1] != "0"))
{
LOGWARNING("Bad prefab DepthWeight record, cannot parse weight \"%s\", skipping.", Components[1].c_str());
continue;
}
// Save to map:
ASSERT(m_DepthWeight.find(Depth) == m_DepthWeight.end()); // Not a duplicate
m_DepthWeight[Depth] = Weight;
} // for itr - Defs[]
}
cPiece::cConnectors cPrefab::GetConnectors(void) const
{
return m_Connectors;

View File

@ -37,11 +37,51 @@ public:
int m_SizeX;
int m_SizeY;
int m_SizeZ;
/** The hitbox used for collision-checking between prefabs. Relative to the bounds. */
int m_HitboxMinX, m_HitboxMinY, m_HitboxMinZ;
int m_HitboxMaxX, m_HitboxMaxY, m_HitboxMaxZ;
/** The mapping between characters in m_Image and the blocktype / blockmeta.
Format: "Char: BlockType: BlockMeta \n Char: BlockType : BlockMeta \n ..." */
const char * m_CharMap;
/** The actual image to be used for the prefab. Organized YZX (Y changes the least often).
Each character represents a single block, the type is mapped through m_CharMap. */
const char * m_Image;
/** List of connectors.
Format: "Type: X, Y, Z : Direction \n Type : X, Y, Z : Direction \n ...".
Type is an arbitrary number, Direction is the BlockFace constant value (0 .. 5). */
const char * m_Connectors;
/** Bitmask specifying the allowed rotations.
N rotations CCW are allowed if bit N is set. */
int m_AllowedRotations;
/** The merge strategy to use while drawing the prefab. */
cBlockArea::eMergeStrategy m_MergeStrategy;
/** If set to true, the prefab will extend its lowermost blocks until a solid block is found,
thus creating a foundation for the prefab. This is used for houses to be "on the ground", as well as
nether fortresses not to float. */
bool m_ShouldExtendFloor;
/** Chance of this piece being used, if no other modifier is active. */
int m_DefaultWeight;
/** Chances of this piece being used, per depth of the generated piece tree.
The starting piece has a depth of 0, the pieces connected to it are depth 1, etc.
The specified depth stands for the depth of the new piece (not the existing already-placed piece),
so valid depths start at 1.
Format: "Depth : Weight | Depth : Weight | Depth : Weight ..."
Depths that are not specified will use the m_DefaultWeight value. */
const char * m_DepthWeight;
/** The weight to add to this piece's base per-depth chance if the previous piece is the same.
Can be positive or negative.
This is used e. g. to make nether bridges prefer spanning multiple segments or to penalize turrets next to each other. */
int m_AddWeightIfSame;
};
cPrefab(const sDef & a_Def);
@ -51,6 +91,10 @@ public:
/** Returns true if the prefab has any connector of the specified type. */
bool HasConnectorType(int a_ConnectorType) const;
/** Returns the weight (chance) of this prefab generating as the next piece after the specified placed piece.
PiecePool implementations can use this for their GetPieceWeight() implementations. */
int GetPieceWeight(const cPlacedPiece & a_PlacedPiece, const cPiece::cConnector & a_ExistingConnector) const;
protected:
/** Packs complete definition of a single block, for per-letter assignment. */
@ -60,9 +104,12 @@ protected:
NIBBLETYPE m_BlockMeta;
};
/** Maps letters in the sDef::m_Image onto a number, BlockType * 16 | BlockMeta */
/** Maps letters in the sDef::m_Image onto a sBlockTypeDef block type definition. */
typedef sBlockTypeDef CharMap[256];
/** Maps generator tree depth to weight. */
typedef std::map<int, int> cDepthWeight;
/** The cBlockArea that contains the block definitions for the prefab.
The index identifies the number of CCW rotations applied (0 = no rotation, 1 = 1 CCW rotation, ...). */
@ -71,7 +118,7 @@ protected:
/** The size of the prefab */
Vector3i m_Size;
/** The hitbox of the prefab. In first version is the same as the m_BlockArea dimensions */
/** The hitbox used for collision-checking between prefabs. */
cCuboid m_HitBox;
/** The connectors through which the piece connects to other pieces */
@ -82,6 +129,26 @@ protected:
/** The merge strategy to use when drawing the prefab into a block area */
cBlockArea::eMergeStrategy m_MergeStrategy;
/** If set to true, the prefab will extend its lowermost blocks until a solid block is found,
thus creating a foundation for the prefab. This is used for houses to be "on the ground", as well as
nether fortresses not to float. */
bool m_ShouldExtendFloor;
/** Chance of this piece being used, if no other modifier is active. */
int m_DefaultWeight;
/** Chances of this piece being used, per depth of the generated piece tree.
The starting piece has a depth of 0, the pieces connected to it are depth 1, etc.
The specified depth stands for the depth of the new piece (not the existing already-placed piece),
so valid depths start at 1.
Depths that are not specified will use the m_DefaultWeight value. */
cDepthWeight m_DepthWeight;
/** The weight to add to this piece's base per-depth chance if the previous piece is the same.
Can be positive or negative.
This is used e. g. to make nether bridges prefer spanning multiple segments or to penalize turrets next to each other. */
int m_AddWeightIfSame;
// cPiece overrides:
@ -98,6 +165,9 @@ protected:
/** Parses the connectors definition text into m_Connectors member. */
void ParseConnectors(const char * a_ConnectorsDef);
/** Parses the per-depth weight into m_DepthWeight member. */
void ParseDepthWeight(const char * a_DepthWeightDef);
};

View File

@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../../")
file(GLOB SOURCE
"*.cpp"
"*.h"
)
add_library(Generating_Prefabs ${SOURCE})

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
// NetherFortPrefabs.h
// Declares the data used for nether fortress prefabs
// Declares the prefabs in the group NetherFort
#include "../Prefab.h"
@ -9,7 +9,7 @@
extern const cPrefab::sDef g_NetherFortPrefabs1[];
extern const cPrefab::sDef g_NetherFortStartingPrefabs1[];
extern const size_t g_NetherFortPrefabs1Count;
extern const size_t g_NetherFortStartingPrefabs1Count;
extern const cPrefab::sDef g_NetherFortPrefabs[];
extern const cPrefab::sDef g_NetherFortStartingPrefabs[];
extern const size_t g_NetherFortPrefabsCount;
extern const size_t g_NetherFortStartingPrefabsCount;

View File

@ -269,7 +269,7 @@ void cStructGenRavines::cRavine::GenerateBaseDefPoints(int a_BlockX, int a_Block
int CenterZ = a_BlockZ + OffsetZ;
// Get the base angle in which the ravine "axis" goes:
float Angle = (float)(((float)((a_Noise.IntNoise3DInt(20 * a_BlockX, 70 * a_BlockZ, 6000) / 9) % 16384)) / 16384.0 * 3.141592653);
float Angle = (float)(((float)((a_Noise.IntNoise3DInt(20 * a_BlockX, 70 * a_BlockZ, 6000) / 9) % 16384)) / 16384.0 * M_PI);
float xc = sin(Angle);
float zc = cos(Angle);
@ -311,12 +311,13 @@ void cStructGenRavines::cRavine::RefineDefPoints(const cRavDefPoints & a_Src, cR
a_Dst.clear();
a_Dst.reserve(Num * 2 + 2);
cRavDefPoints::const_iterator itr = a_Src.begin() + 1;
a_Dst.push_back(a_Src.front());
int PrevX = a_Src.front().m_BlockX;
int PrevZ = a_Src.front().m_BlockZ;
int PrevR = a_Src.front().m_Radius;
int PrevT = a_Src.front().m_Top;
int PrevB = a_Src.front().m_Bottom;
const cRavDefPoint & Source = a_Src.front();
a_Dst.push_back(Source);
int PrevX = Source.m_BlockX;
int PrevZ = Source.m_BlockZ;
int PrevR = Source.m_Radius;
int PrevT = Source.m_Top;
int PrevB = Source.m_Bottom;
for (int i = 0; i <= Num; ++i, ++itr)
{
int dx = itr->m_BlockX - PrevX;

View File

@ -7,7 +7,7 @@
void cGroup::AddCommand( AString a_Command )
void cGroup::AddCommand( const AString & a_Command )
{
m_Commands[ a_Command ] = true;
}
@ -16,7 +16,7 @@ void cGroup::AddCommand( AString a_Command )
void cGroup::AddPermission( AString a_Permission )
void cGroup::AddPermission( const AString & a_Permission )
{
m_Permissions[ a_Permission ] = true;
}

View File

@ -11,11 +11,11 @@ public: // tolua_export
cGroup() {}
~cGroup() {}
void SetName( AString a_Name ) { m_Name = a_Name; } // tolua_export
void SetName( const AString & a_Name ) { m_Name = a_Name; } // tolua_export
const AString & GetName() const { return m_Name; } // tolua_export
void SetColor( AString a_Color ) { m_Color = a_Color; } // tolua_export
void AddCommand( AString a_Command ); // tolua_export
void AddPermission( AString a_Permission ); // tolua_export
void SetColor( const AString & a_Color ) { m_Color = a_Color; } // tolua_export
void AddCommand( const AString & a_Command ); // tolua_export
void AddPermission( const AString & a_Permission ); // tolua_export
void InheritFrom( cGroup* a_Group ); // tolua_export
typedef std::map< AString, bool > PermissionMap;

View File

@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../")
file(GLOB SOURCE
"*.cpp"
"*.h"
)
add_library(HTTPServer ${SOURCE})

View File

@ -20,7 +20,7 @@ cEnvelopeParser::cEnvelopeParser(cCallbacks & a_Callbacks) :
int cEnvelopeParser::Parse(const char * a_Data, int a_Size)
size_t cEnvelopeParser::Parse(const char * a_Data, size_t a_Size)
{
if (!m_IsInHeaders)
{
@ -55,7 +55,7 @@ int cEnvelopeParser::Parse(const char * a_Data, int a_Size)
{
// An error has occurred
m_IsInHeaders = false;
return -1;
return AString::npos;
}
Last = idxCRLF + 2;
idxCRLF = m_IncomingData.find("\r\n", idxCRLF + 2);

View File

@ -22,7 +22,7 @@ public:
// Force a virtual destructor in descendants:
virtual ~cCallbacks() {}
/// Called when a full header line is parsed
/** Called when a full header line is parsed */
virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) = 0;
} ;
@ -30,40 +30,41 @@ public:
cEnvelopeParser(cCallbacks & a_Callbacks);
/** Parses the incoming data.
Returns the number of bytes consumed from the input. The bytes not consumed are not part of the envelope header
Returns the number of bytes consumed from the input. The bytes not consumed are not part of the envelope header.
Returns AString::npos on error
*/
int Parse(const char * a_Data, int a_Size);
size_t Parse(const char * a_Data, size_t a_Size);
/// Makes the parser forget everything parsed so far, so that it can be reused for parsing another datastream
/** Makes the parser forget everything parsed so far, so that it can be reused for parsing another datastream */
void Reset(void);
/// Returns true if more input is expected for the envelope header
/** Returns true if more input is expected for the envelope header */
bool IsInHeaders(void) const { return m_IsInHeaders; }
/// Sets the IsInHeaders flag; used by cMultipartParser to simplify the parser initial conditions
/** Sets the IsInHeaders flag; used by cMultipartParser to simplify the parser initial conditions */
void SetIsInHeaders(bool a_IsInHeaders) { m_IsInHeaders = a_IsInHeaders; }
public:
/// Callbacks to call for the various events
/** Callbacks to call for the various events */
cCallbacks & m_Callbacks;
/// Set to true while the parser is still parsing the envelope headers. Once set to true, the parser will not consume any more data.
/** Set to true while the parser is still parsing the envelope headers. Once set to true, the parser will not consume any more data. */
bool m_IsInHeaders;
/// Buffer for the incoming data until it is parsed
/** Buffer for the incoming data until it is parsed */
AString m_IncomingData;
/// Holds the last parsed key; used for line-wrapped values
/** Holds the last parsed key; used for line-wrapped values */
AString m_LastKey;
/// Holds the last parsed value; used for line-wrapped values
/** Holds the last parsed value; used for line-wrapped values */
AString m_LastValue;
/// Notifies the callback of the key/value stored in m_LastKey/m_LastValue, then erases them
/** Notifies the callback of the key/value stored in m_LastKey/m_LastValue, then erases them */
void NotifyLast(void);
/// Parses one line of header data. Returns true if successful
/** Parses one line of header data. Returns true if successful */
bool ParseLine(const char * a_Data, size_t a_Size);
} ;

View File

@ -67,10 +67,10 @@ void cHTTPConnection::Send(const cHTTPResponse & a_Response)
void cHTTPConnection::Send(const void * a_Data, int a_Size)
void cHTTPConnection::Send(const void * a_Data, size_t a_Size)
{
ASSERT(m_State == wcsSendingResp);
AppendPrintf(m_OutgoingData, "%x\r\n", a_Size);
AppendPrintf(m_OutgoingData, SIZE_T_FMT_HEX "\r\n", a_Size);
m_OutgoingData.append((const char *)a_Data, a_Size);
m_OutgoingData.append("\r\n");
m_HTTPServer.NotifyConnectionWrite(*this);
@ -144,7 +144,7 @@ void cHTTPConnection::Terminate(void)
void cHTTPConnection::DataReceived(const char * a_Data, int a_Size)
void cHTTPConnection::DataReceived(const char * a_Data, size_t a_Size)
{
switch (m_State)
{
@ -155,8 +155,8 @@ void cHTTPConnection::DataReceived(const char * a_Data, int a_Size)
m_CurrentRequest = new cHTTPRequest;
}
int BytesConsumed = m_CurrentRequest->ParseHeaders(a_Data, a_Size);
if (BytesConsumed < 0)
size_t BytesConsumed = m_CurrentRequest->ParseHeaders(a_Data, a_Size);
if (BytesConsumed == AString::npos)
{
delete m_CurrentRequest;
m_CurrentRequest = NULL;
@ -174,7 +174,7 @@ void cHTTPConnection::DataReceived(const char * a_Data, int a_Size)
m_State = wcsRecvBody;
m_HTTPServer.NewRequest(*this, *m_CurrentRequest);
m_CurrentRequestBodyRemaining = m_CurrentRequest->GetContentLength();
if (m_CurrentRequestBodyRemaining < 0)
if (m_CurrentRequestBodyRemaining == AString::npos)
{
// The body length was not specified in the request, assume zero
m_CurrentRequestBodyRemaining = 0;
@ -197,7 +197,7 @@ void cHTTPConnection::DataReceived(const char * a_Data, int a_Size)
ASSERT(m_CurrentRequest != NULL);
if (m_CurrentRequestBodyRemaining > 0)
{
int BytesToConsume = std::min(m_CurrentRequestBodyRemaining, a_Size);
size_t BytesToConsume = std::min(m_CurrentRequestBodyRemaining, (size_t)a_Size);
m_HTTPServer.RequestBody(*this, *m_CurrentRequest, a_Data, BytesToConsume);
m_CurrentRequestBodyRemaining -= BytesToConsume;
}

View File

@ -51,7 +51,7 @@ public:
void Send(const cHTTPResponse & a_Response);
/** Sends the data as the response (may be called multiple times) */
void Send(const void * a_Data, int a_Size);
void Send(const void * a_Data, size_t a_Size);
/** Sends the data as the response (may be called multiple times) */
void Send(const AString & a_Data) { Send(a_Data.data(), a_Data.size()); }
@ -87,11 +87,11 @@ protected:
/** Number of bytes that remain to read for the complete body of the message to be received.
Valid only in wcsRecvBody */
int m_CurrentRequestBodyRemaining;
size_t m_CurrentRequestBodyRemaining;
// cSocketThreads::cCallback overrides:
virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client
virtual void DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client
virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client
virtual void SocketClosed (void) override; // The socket has been closed for any reason
} ;

View File

@ -52,7 +52,7 @@ cHTTPFormParser::cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callba
cHTTPFormParser::cHTTPFormParser(eKind a_Kind, const char * a_Data, int a_Size, cCallbacks & a_Callbacks) :
cHTTPFormParser::cHTTPFormParser(eKind a_Kind, const char * a_Data, size_t a_Size, cCallbacks & a_Callbacks) :
m_Callbacks(a_Callbacks),
m_Kind(a_Kind),
m_IsValid(true)
@ -64,7 +64,7 @@ cHTTPFormParser::cHTTPFormParser(eKind a_Kind, const char * a_Data, int a_Size,
void cHTTPFormParser::Parse(const char * a_Data, int a_Size)
void cHTTPFormParser::Parse(const char * a_Data, size_t a_Size)
{
if (!m_IsValid)
{
@ -243,7 +243,7 @@ void cHTTPFormParser::OnPartHeader(const AString & a_Key, const AString & a_Valu
void cHTTPFormParser::OnPartData(const char * a_Data, int a_Size)
void cHTTPFormParser::OnPartData(const char * a_Data, size_t a_Size)
{
if (m_CurrentPartName.empty())
{

View File

@ -43,7 +43,7 @@ public:
virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) = 0;
/// Called when more file data has come for the current file in the form data
virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) = 0;
virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, size_t a_Size) = 0;
/// Called when the current file part has ended in the form data
virtual void OnFileEnd(cHTTPFormParser & a_Parser) = 0;
@ -54,10 +54,10 @@ public:
cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callbacks);
/// Creates a parser with the specified content type that reads data from a string
cHTTPFormParser(eKind a_Kind, const char * a_Data, int a_Size, cCallbacks & a_Callbacks);
cHTTPFormParser(eKind a_Kind, const char * a_Data, size_t a_Size, cCallbacks & a_Callbacks);
/// Adds more data into the parser, as the request body is received
void Parse(const char * a_Data, int a_Size);
void Parse(const char * a_Data, size_t a_Size);
/** Notifies that there's no more data incoming and the parser should finish its parsing.
Returns true if parsing successful
@ -106,7 +106,7 @@ protected:
// cMultipartParser::cCallbacks overrides:
virtual void OnPartStart (void) override;
virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) override;
virtual void OnPartData (const char * a_Data, int a_Size) override;
virtual void OnPartData (const char * a_Data, size_t a_Size) override;
virtual void OnPartEnd (void) override;
} ;

View File

@ -25,7 +25,7 @@
cHTTPMessage::cHTTPMessage(eKind a_Kind) :
m_Kind(a_Kind),
m_ContentLength(-1)
m_ContentLength(AString::npos)
{
}
@ -81,23 +81,23 @@ cHTTPRequest::cHTTPRequest(void) :
int cHTTPRequest::ParseHeaders(const char * a_Data, int a_Size)
size_t cHTTPRequest::ParseHeaders(const char * a_Data, size_t a_Size)
{
if (!m_IsValid)
{
return -1;
return AString::npos;
}
if (m_Method.empty())
{
// The first line hasn't been processed yet
int res = ParseRequestLine(a_Data, a_Size);
if ((res < 0) || (res == a_Size))
size_t res = ParseRequestLine(a_Data, a_Size);
if ((res == AString::npos) || (res == a_Size))
{
return res;
}
int res2 = m_EnvelopeParser.Parse(a_Data + res, a_Size - res);
if (res2 < 0)
size_t res2 = m_EnvelopeParser.Parse(a_Data + res, a_Size - res);
if (res2 == AString::npos)
{
m_IsValid = false;
return res2;
@ -107,8 +107,8 @@ int cHTTPRequest::ParseHeaders(const char * a_Data, int a_Size)
if (m_EnvelopeParser.IsInHeaders())
{
int res = m_EnvelopeParser.Parse(a_Data, a_Size);
if (res < 0)
size_t res = m_EnvelopeParser.Parse(a_Data, a_Size);
if (res == AString::npos)
{
m_IsValid = false;
}
@ -138,7 +138,7 @@ AString cHTTPRequest::GetBareURL(void) const
int cHTTPRequest::ParseRequestLine(const char * a_Data, int a_Size)
size_t cHTTPRequest::ParseRequestLine(const char * a_Data, size_t a_Size)
{
m_IncomingHeaderData.append(a_Data, a_Size);
size_t IdxEnd = m_IncomingHeaderData.size();
@ -158,7 +158,7 @@ int cHTTPRequest::ParseRequestLine(const char * a_Data, int a_Size)
if (LineStart >= IdxEnd)
{
m_IsValid = false;
return -1;
return AString::npos;
}
int NumSpaces = 0;
@ -186,7 +186,7 @@ int cHTTPRequest::ParseRequestLine(const char * a_Data, int a_Size)
{
// Too many spaces in the request
m_IsValid = false;
return -1;
return AString::npos;
}
}
NumSpaces += 1;
@ -198,13 +198,13 @@ int cHTTPRequest::ParseRequestLine(const char * a_Data, int a_Size)
{
// LF too early, without a CR, without two preceeding spaces or too soon after the second space
m_IsValid = false;
return -1;
return AString::npos;
}
// Check that there's HTTP/version at the end
if (strncmp(a_Data + URLEnd + 1, "HTTP/1.", 7) != 0)
{
m_IsValid = false;
return -1;
return AString::npos;
}
m_Method = m_IncomingHeaderData.substr(LineStart, MethodEnd - LineStart);
m_URL = m_IncomingHeaderData.substr(MethodEnd + 1, URLEnd - MethodEnd - 1);

View File

@ -39,10 +39,10 @@ public:
void AddHeader(const AString & a_Key, const AString & a_Value);
void SetContentType (const AString & a_ContentType) { m_ContentType = a_ContentType; }
void SetContentLength(int a_ContentLength) { m_ContentLength = a_ContentLength; }
void SetContentLength(size_t a_ContentLength) { m_ContentLength = a_ContentLength; }
const AString & GetContentType (void) const { return m_ContentType; }
int GetContentLength(void) const { return m_ContentLength; }
size_t GetContentLength(void) const { return m_ContentLength; }
protected:
typedef std::map<AString, AString> cNameValueMap;
@ -54,8 +54,10 @@ protected:
/** Type of the content; parsed by AddHeader(), set directly by SetContentLength() */
AString m_ContentType;
/** Length of the content that is to be received. -1 when the object is created, parsed by AddHeader() or set directly by SetContentLength() */
int m_ContentLength;
/** Length of the content that is to be received.
AString::npos when the object is created.
Parsed by AddHeader() or set directly by SetContentLength() */
size_t m_ContentLength;
} ;
@ -72,12 +74,12 @@ public:
cHTTPRequest(void);
/** Parses the request line and then headers from the received data.
Returns the number of bytes consumed or a negative number for error
Returns the number of bytes consumed or AString::npos number for error
*/
int ParseHeaders(const char * a_Data, int a_Size);
size_t ParseHeaders(const char * a_Data, size_t a_Size);
/** Returns true if the request did contain a Content-Length header */
bool HasReceivedContentLength(void) const { return (m_ContentLength >= 0); }
bool HasReceivedContentLength(void) const { return (m_ContentLength != AString::npos); }
/** Returns the method used in the request */
const AString & GetMethod(void) const { return m_Method; }
@ -145,7 +147,7 @@ protected:
/** Parses the incoming data for the first line (RequestLine)
Returns the number of bytes consumed, or -1 for an error
*/
int ParseRequestLine(const char * a_Data, int a_Size);
size_t ParseRequestLine(const char * a_Data, size_t a_Size);
// cEnvelopeParser::cCallbacks overrides:
virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) override;

View File

@ -38,7 +38,7 @@ class cDebugCallbacks :
}
virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) override
virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size) override
{
UNUSED(a_Connection);
@ -100,7 +100,7 @@ class cDebugCallbacks :
}
virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) override
virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, size_t a_Size) override
{
// TODO
}
@ -242,7 +242,7 @@ void cHTTPServer::NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Re
void cHTTPServer::RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size)
void cHTTPServer::RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size)
{
m_Callbacks->OnRequestBody(a_Connection, a_Request, a_Data, a_Size);
}

View File

@ -44,8 +44,9 @@ public:
*/
virtual void OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0;
/// Called when another part of request body has arrived.
virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size) = 0;
/** Called when another part of request body has arrived.
May be called multiple times for a single request. */
virtual void OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size) = 0;
/// Called when the request body has been fully received in previous calls to OnRequestBody()
virtual void OnRequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request) = 0;
@ -90,8 +91,9 @@ protected:
/// Called by cHTTPConnection when it finishes parsing the request header
void NewRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request);
/// Called by cHTTPConenction when it receives more data for the request body
void RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size);
/** Called by cHTTPConenction when it receives more data for the request body.
May be called multiple times for a single request. */
void RequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, size_t a_Size);
/// Called by cHTTPConnection when it detects that the request has finished (all of its body has been received)
void RequestFinished(cHTTPConnection & a_Connection, cHTTPRequest & a_Request);

View File

@ -97,8 +97,6 @@ cMultipartParser::cMultipartParser(const AString & a_ContentType, cCallbacks & a
m_EnvelopeParser(*this),
m_HasHadData(false)
{
static AString s_Multipart = "multipart/";
// Check that the content type is multipart:
AString ContentType(a_ContentType);
if (strncmp(ContentType.c_str(), "multipart/", 10) != 0)
@ -146,7 +144,7 @@ cMultipartParser::cMultipartParser(const AString & a_ContentType, cCallbacks & a
void cMultipartParser::Parse(const char * a_Data, int a_Size)
void cMultipartParser::Parse(const char * a_Data, size_t a_Size)
{
// Skip parsing if invalid
if (!m_IsValid)
@ -160,8 +158,8 @@ void cMultipartParser::Parse(const char * a_Data, int a_Size)
{
if (m_EnvelopeParser.IsInHeaders())
{
int BytesConsumed = m_EnvelopeParser.Parse(m_IncomingData.data(), m_IncomingData.size());
if (BytesConsumed < 0)
size_t BytesConsumed = m_EnvelopeParser.Parse(m_IncomingData.data(), m_IncomingData.size());
if (BytesConsumed == AString::npos)
{
m_IsValid = false;
return;

View File

@ -25,50 +25,50 @@ public:
// Force a virtual destructor in descendants:
virtual ~cCallbacks() {}
/// Called when a new part starts
/** Called when a new part starts */
virtual void OnPartStart(void) = 0;
/// Called when a complete header line is received for a part
/** Called when a complete header line is received for a part */
virtual void OnPartHeader(const AString & a_Key, const AString & a_Value) = 0;
/// Called when body for a part is received
virtual void OnPartData(const char * a_Data, int a_Size) = 0;
/** Called when body for a part is received */
virtual void OnPartData(const char * a_Data, size_t a_Size) = 0;
/// Called when the current part ends
/** Called when the current part ends */
virtual void OnPartEnd(void) = 0;
} ;
/// Creates the parser, expects to find the boundary in a_ContentType
/** Creates the parser, expects to find the boundary in a_ContentType */
cMultipartParser(const AString & a_ContentType, cCallbacks & a_Callbacks);
/// Parses more incoming data
void Parse(const char * a_Data, int a_Size);
/** Parses more incoming data */
void Parse(const char * a_Data, size_t a_Size);
protected:
/// The callbacks to call for various parsing events
/** The callbacks to call for various parsing events */
cCallbacks & m_Callbacks;
/// True if the data parsed so far is valid; if false, further parsing is skipped
/** True if the data parsed so far is valid; if false, further parsing is skipped */
bool m_IsValid;
/// Parser for each part's envelope
/** Parser for each part's envelope */
cEnvelopeParser m_EnvelopeParser;
/// Buffer for the incoming data until it is parsed
/** Buffer for the incoming data until it is parsed */
AString m_IncomingData;
/// The boundary, excluding both the initial "--" and the terminating CRLF
/** The boundary, excluding both the initial "--" and the terminating CRLF */
AString m_Boundary;
/// Set to true if some data for the current part has already been signalized to m_Callbacks. Used for proper CRLF inserting.
/** Set to true if some data for the current part has already been signalized to m_Callbacks. Used for proper CRLF inserting. */
bool m_HasHadData;
/// Parse one line of incoming data. The CRLF has already been stripped from a_Data / a_Size
void ParseLine(const char * a_Data, int a_Size);
/** Parse one line of incoming data. The CRLF has already been stripped from a_Data / a_Size */
void ParseLine(const char * a_Data, size_t a_Size);
/// Parse one line of incoming data in the headers section of a part. The CRLF has already been stripped from a_Data / a_Size
void ParseHeaderLine(const char * a_Data, int a_Size);
/** Parse one line of incoming data in the headers section of a part. The CRLF has already been stripped from a_Data / a_Size */
void ParseHeaderLine(const char * a_Data, size_t a_Size);
// cEnvelopeParser overrides:
virtual void OnHeaderLine(const AString & a_Key, const AString & a_Value) override;

Some files were not shown because too many files have changed in this diff Show More