Merge branch 'master' into Slabs
This commit is contained in:
commit
f5fe368220
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -9,4 +9,4 @@
|
||||
url = https://github.com/bearbin/transapi.git
|
||||
[submodule "lib/polarssl"]
|
||||
path = lib/polarssl
|
||||
url = https://github.com/polarssl/polarssl
|
||||
url = https://github.com/mc-server/polarssl
|
||||
|
41
COMPILING.md
41
COMPILING.md
@ -45,7 +45,30 @@ It is possible to use an external profiler to learn more about how the code perf
|
||||
|
||||
There's a script file, `MCServer/profile_run.cmd` that encapsulates most of the profiling work, have a look at the comments at the top of that script for details on how to get it to work. You'll need to change to a profiled configuration (both debug and release can be profiled).
|
||||
|
||||
## Linux, MacOS, FreeBSD etc. ##
|
||||
## OSX ##
|
||||
Install git from its [website](http://git-scm.com) or homebrew: `brew install git`.
|
||||
|
||||
Install Xcode (commandline tools are recommended) from the App Store or https://developer.apple.com/downloads.
|
||||
|
||||
Install CMake from its [website](http://cmake.org) or homebrew: `brew install cmake`.
|
||||
|
||||
### Getting the sources ###
|
||||
```
|
||||
mkdir MCServer
|
||||
cd MCServer
|
||||
git clone https://github.com/mc-server/MCServer.git .
|
||||
git submodule init
|
||||
git submodule update
|
||||
```
|
||||
|
||||
### Building ###
|
||||
|
||||
Follow the instructions at [CMake on Unix-based platforms](#cmake-on-unix-based-platforms), using Xcode as cmake's generator. If no generator is specified, CMake will use the Makefile generator, in which case you must build with the `make` command.
|
||||
|
||||
After doing so, run the command `xcodebuild lib/polarssl/POLARSSL.xcodeproj` in the build directory, in order to build polarssl, a library that is required by MCServer. Lastly, run the command `xcodebuild` to build MCServer. Optionally, you may open the project files for polarssl and then MCServer in Xcode and build there.
|
||||
|
||||
|
||||
## Linux, FreeBSD etc. ##
|
||||
|
||||
Install git, cmake and gcc or clang, using your platform's package manager:
|
||||
```
|
||||
@ -61,6 +84,14 @@ git submodule init
|
||||
git submodule update
|
||||
```
|
||||
|
||||
### Building ###
|
||||
|
||||
Follow the instructions at [CMake on Unix-based platforms](#cmake-on-unix-based-platforms).
|
||||
|
||||
After doing so, run the command `make` in the build directory, and MCServer will build.
|
||||
|
||||
## CMake on Unix-based platforms ###
|
||||
|
||||
### Release Mode ###
|
||||
|
||||
Release mode is preferred for almost all cases, it has much better speed and less console spam. However, if you are developing MCServer actively, debug mode might be better.
|
||||
@ -69,8 +100,10 @@ Assuming you are in the MCServer folder created in the initial setup step, you n
|
||||
```
|
||||
mkdir Release
|
||||
cd Release
|
||||
cmake -DCMAKE_BUILD_TYPE=RELEASE .. && make
|
||||
cmake -DCMAKE_BUILD_TYPE=RELEASE ..
|
||||
```
|
||||
NOTE: CMake can generate project files for many different programs, such as Xcode, eclipse, and ninja. To use a different generator, first type `cmake --help`, and at the end, cmake will output the different generators that are available. To specify one, add `-G` followed by the name of the generator, in the `cmake` command. Note that the name is case-sensitive.
|
||||
|
||||
The executable will be built in the `MCServer/MCServer` folder and will be named `MCServer`.
|
||||
|
||||
### Debug Mode ###
|
||||
@ -81,8 +114,10 @@ Assuming you are in the MCServer folder created in the Getting the sources step,
|
||||
```
|
||||
mkdir Debug
|
||||
cd Debug
|
||||
cmake -DCMAKE_BUILD_TYPE=DEBUG .. && make
|
||||
cmake -DCMAKE_BUILD_TYPE=DEBUG ..
|
||||
```
|
||||
NOTE: CMake can generate project files for many different programs, such as Xcode, eclipse, and ninja. To use a different generator, first type `cmake --help`, and at the end, cmake will output the different generators that are available. To specify one, add `-G` followed by the name of the generator, in the `cmake` command. Note that the name is case-sensitive.
|
||||
|
||||
The executable will be built in the `MCServer/MCServer` folder and will be named `MCServer_debug`.
|
||||
|
||||
### 32 Bit Mode switch ###
|
||||
|
17
CoverityModel.cpp
Normal file
17
CoverityModel.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
|
||||
extern "C" {
|
||||
struct lua_State;
|
||||
struct tolua_Error
|
||||
{
|
||||
int index;
|
||||
int array;
|
||||
const char* type;
|
||||
};
|
||||
|
||||
void tolua_error (lua_State* L, const char* msg, tolua_Error* err)
|
||||
{
|
||||
__coverity_panic__();
|
||||
}
|
||||
|
||||
}
|
2
MCServer/.gitignore
vendored
2
MCServer/.gitignore
vendored
@ -28,3 +28,5 @@ motd.txt
|
||||
*.deuser
|
||||
*.dmp
|
||||
*.xml
|
||||
mcserver_api.lua
|
||||
|
||||
|
@ -699,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 =
|
||||
@ -2929,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" },
|
||||
}
|
||||
} ;
|
||||
|
||||
|
@ -2,23 +2,33 @@ return
|
||||
{
|
||||
HOOK_DISCONNECT =
|
||||
{
|
||||
CalledWhen = "A player has explicitly disconnected.",
|
||||
CalledWhen = [[
|
||||
A client has disconnected, either by explicitly sending the disconnect packet (in older protocols) or
|
||||
their connection was terminated
|
||||
]],
|
||||
DefaultFnName = "OnDisconnect", -- also used as pagename
|
||||
Desc = [[
|
||||
This hook is called when a client is about to be disconnected from the server, for whatever reason.
|
||||
|
||||
<p><b>Note that this hook will be removed after <1.7 protocol support is removed, as it was originally a hook for
|
||||
the client sending the server a disconnect packet, which no longer happens.</b></p>
|
||||
This hook is called when a client has disconnected from the server, for whatever reason. It is also
|
||||
called when the client sends the Disconnect packet (only in pre-1.7 protocols). This hook is not called
|
||||
for server ping connections.</p>
|
||||
<p>
|
||||
Note that the hook is called even for connections to players who failed to auth. In such a case there's
|
||||
no {{cPlayer}} object associated with the client.</p>
|
||||
<p>
|
||||
See also the {{OnHandshake|HOOK_HANDSHAKE}} hook which is called when the client connects (and presents
|
||||
a handshake message, so that they are not just status-pinging). If you need to store a per-player
|
||||
object, use the {{OnPlayerJoined|HOOK_PLAYER_JOINED}} and {{OnPlayerDestroyed|HOOK_PLAYER_DESTROYED}}
|
||||
hooks instead, those are guaranteed to have the {{cPlayer}} object associated.
|
||||
]],
|
||||
Params =
|
||||
{
|
||||
{ Name = "Player", Type = "{{cPlayer}}", Notes = "The player who has disconnected" },
|
||||
{ Name = "Client", Type = "{{cClientHandle}}", Notes = "The client who has disconnected" },
|
||||
{ Name = "Reason", Type = "string", Notes = "The reason that the client has sent in the disconnect packet" },
|
||||
},
|
||||
Returns = [[
|
||||
If the function returns false or no value, MCServer calls other plugins' callbacks for this event.
|
||||
If the function returns true, no other plugins are called for this event. In either case,
|
||||
the player is disconnected.
|
||||
the client is disconnected.
|
||||
]],
|
||||
}, -- HOOK_DISCONNECT
|
||||
}
|
||||
|
167
MCServer/Plugins/APIDump/UsingChunkStays.html
Normal file
167
MCServer/Plugins/APIDump/UsingChunkStays.html
Normal 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>
|
@ -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>
|
||||
|
@ -27,10 +27,14 @@ local function LoadAPIFiles(a_Folder, a_DstTable)
|
||||
-- We only want .lua files from the folder:
|
||||
if (cFile:IsFile(FileName) and fnam:match(".*%.lua$")) then
|
||||
local TablesFn, Err = loadfile(FileName);
|
||||
if (TablesFn == nil) then
|
||||
if (type(TablesFn) ~= "function") then
|
||||
LOGWARNING("Cannot load API descriptions from " .. FileName .. ", Lua error '" .. Err .. "'.");
|
||||
else
|
||||
local Tables = TablesFn();
|
||||
if (type(Tables) ~= "table") then
|
||||
LOGWARNING("Cannot load API descriptions from " .. FileName .. ", returned object is not a table (" .. type(Tables) .. ").");
|
||||
break
|
||||
end
|
||||
for k, cls in pairs(Tables) do
|
||||
a_DstTable[k] = cls;
|
||||
end
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -30,7 +30,7 @@ if "a%ftpsite%" == "a" (
|
||||
|
||||
cd MCServer
|
||||
copy /Y settings_apidump.ini settings.ini
|
||||
echo stop | MCServer
|
||||
echo api | MCServer
|
||||
cd ..
|
||||
|
||||
|
||||
|
@ -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()
|
||||
|
@ -36,14 +36,24 @@ set(SHARED_SRC
|
||||
../../src/StringUtils.cpp
|
||||
../../src/Log.cpp
|
||||
../../src/MCLogger.cpp
|
||||
../../src/Crypto.cpp
|
||||
../../src/PolarSSL++/AesCfb128Decryptor.cpp
|
||||
../../src/PolarSSL++/AesCfb128Encryptor.cpp
|
||||
../../src/PolarSSL++/CtrDrbgContext.cpp
|
||||
../../src/PolarSSL++/EntropyContext.cpp
|
||||
../../src/PolarSSL++/PublicKey.cpp
|
||||
../../src/PolarSSL++/RsaPrivateKey.cpp
|
||||
)
|
||||
set(SHARED_HDR
|
||||
../../src/ByteBuffer.h
|
||||
../../src/StringUtils.h
|
||||
../../src/Log.h
|
||||
../../src/MCLogger.h
|
||||
../../src/Crypto.h
|
||||
../../src/PolarSSL++/AesCfb128Decryptor.h
|
||||
../../src/PolarSSL++/AesCfb128Encryptor.h
|
||||
../../src/PolarSSL++/CtrDrbgContext.h
|
||||
../../src/PolarSSL++/EntropyContext.h
|
||||
../../src/PolarSSL++/PublicKey.h
|
||||
../../src/PolarSSL++/RsaPrivateKey.h
|
||||
)
|
||||
set(SHARED_OSS_SRC
|
||||
../../src/OSSupport/CriticalSection.cpp
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "Connection.h"
|
||||
#include "Server.h"
|
||||
#include <iostream>
|
||||
#include "PolarSSL++/PublicKey.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <direct.h> // For _mkdir()
|
||||
@ -471,7 +472,7 @@ bool cConnection::SendData(SOCKET a_Socket, cByteBuffer & a_Data, const char * a
|
||||
|
||||
|
||||
|
||||
bool cConnection::SendEncryptedData(SOCKET a_Socket, cAESCFBEncryptor & a_Encryptor, const char * a_Data, size_t a_Size, const char * a_Peer)
|
||||
bool cConnection::SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, const char * a_Data, size_t a_Size, const char * a_Peer)
|
||||
{
|
||||
DataLog(a_Data, a_Size, "Encrypting %d bytes to %s", a_Size, a_Peer);
|
||||
const Byte * Data = (const Byte *)a_Data;
|
||||
@ -495,7 +496,7 @@ bool cConnection::SendEncryptedData(SOCKET a_Socket, cAESCFBEncryptor & a_Encryp
|
||||
|
||||
|
||||
|
||||
bool cConnection::SendEncryptedData(SOCKET a_Socket, cAESCFBEncryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer)
|
||||
bool cConnection::SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer)
|
||||
{
|
||||
AString All;
|
||||
a_Data.ReadAll(All);
|
||||
@ -2197,11 +2198,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);
|
||||
@ -2219,6 +2248,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);
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
#include "ByteBuffer.h"
|
||||
#include "OSSupport/Timer.h"
|
||||
#include "PolarSSL++/AesCfb128Decryptor.h"
|
||||
#include "PolarSSL++/AesCfb128Encryptor.h"
|
||||
|
||||
|
||||
|
||||
@ -66,8 +68,8 @@ protected:
|
||||
cByteBuffer m_ClientBuffer;
|
||||
cByteBuffer m_ServerBuffer;
|
||||
|
||||
cAESCFBDecryptor m_ServerDecryptor;
|
||||
cAESCFBEncryptor m_ServerEncryptor;
|
||||
cAesCfb128Decryptor m_ServerDecryptor;
|
||||
cAesCfb128Encryptor m_ServerEncryptor;
|
||||
|
||||
AString m_ServerEncryptionBuffer; // Buffer for the data to be sent to the server once encryption is established
|
||||
|
||||
@ -109,10 +111,10 @@ protected:
|
||||
bool SendData(SOCKET a_Socket, cByteBuffer & a_Data, const char * a_Peer);
|
||||
|
||||
/// Sends data to the specfied socket, after encrypting it using a_Encryptor. If sending fails, prints a fail message using a_Peer and returns false
|
||||
bool SendEncryptedData(SOCKET a_Socket, cAESCFBEncryptor & a_Encryptor, const char * a_Data, size_t a_Size, const char * a_Peer);
|
||||
bool SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, const char * a_Data, size_t a_Size, const char * a_Peer);
|
||||
|
||||
/// Sends data to the specfied socket, after encrypting it using a_Encryptor. If sending fails, prints a fail message using a_Peer and returns false
|
||||
bool SendEncryptedData(SOCKET a_Socket, cAESCFBEncryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer);
|
||||
bool SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer);
|
||||
|
||||
/// Decodes packets coming from the client, sends appropriate counterparts to the server; returns false if the connection is to be dropped
|
||||
bool DecodeClientsPackets(const char * a_Data, int a_Size);
|
||||
|
@ -216,6 +216,20 @@ typedef unsigned char Byte;
|
||||
// Pretty much the same as ASSERT() but stays in Release builds
|
||||
#define VERIFY( x ) ( !!(x) || ( LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), exit(1), 0 ) )
|
||||
|
||||
// Allow both Older versions of MSVC and newer versions of everything use a shared_ptr:
|
||||
// Note that we cannot typedef, because C++ doesn't allow (partial) templates to be typedeffed.
|
||||
#if (defined(_MSC_VER) && (_MSC_VER < 1600))
|
||||
// MSVC before 2010 doesn't have std::shared_ptr, but has std::tr1::shared_ptr, defined in <memory> included earlier
|
||||
#define SharedPtr std::tr1::shared_ptr
|
||||
#elif (defined(_MSC_VER) || (__cplusplus >= 201103L))
|
||||
// C++11 has std::shared_ptr in <memory>, included earlier
|
||||
#define SharedPtr std::shared_ptr
|
||||
#else
|
||||
// C++03 has std::tr1::shared_ptr in <tr1/memory>
|
||||
#include <tr1/memory>
|
||||
#define SharedPtr std::tr1::shared_ptr
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
@ -232,12 +246,6 @@ public:
|
||||
|
||||
|
||||
|
||||
#include "../../src/Crypto.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define LOGERROR printf
|
||||
#define LOGINFO printf
|
||||
#define LOGWARNING printf
|
||||
|
@ -20,7 +20,7 @@ You need to set the server *not* to verify usernames ("online-mode=false" in ser
|
||||
|
||||
|
||||
ProtoProxy is not much dependent on the protocol - it will work with unknown packets, it just won't parse them into human-readable format.
|
||||
The latest protocol which has been tested is 1.6.1 (#73).
|
||||
The latest protocol which has been tested is 1.7.9 (#5).
|
||||
|
||||
|
||||
*/
|
||||
|
@ -9,6 +9,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PolarSSL++/RsaPrivateKey.h"
|
||||
|
||||
|
||||
|
||||
|
||||
@ -17,7 +19,7 @@
|
||||
class cServer
|
||||
{
|
||||
SOCKET m_ListenSocket;
|
||||
cRSAPrivateKey m_PrivateKey;
|
||||
cRsaPrivateKey m_PrivateKey;
|
||||
AString m_PublicKeyDER;
|
||||
short m_ConnectPort;
|
||||
|
||||
@ -27,7 +29,7 @@ public:
|
||||
int Init(short a_ListenPort, short a_ConnectPort);
|
||||
void Run(void);
|
||||
|
||||
cRSAPrivateKey & GetPrivateKey(void) { return m_PrivateKey; }
|
||||
cRsaPrivateKey & GetPrivateKey(void) { return m_PrivateKey; }
|
||||
const AString & GetPublicKeyDER (void) { return m_PublicKeyDER; }
|
||||
|
||||
short GetConnectPort(void) const { return m_ConnectPort; }
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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})
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../")
|
||||
|
||||
file(GLOB SOURCE
|
||||
"*.c"
|
||||
"*.h"
|
||||
)
|
||||
|
||||
add_library(luaexpat ${SOURCE})
|
||||
|
@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../../src/")
|
||||
|
||||
file(GLOB SOURCE
|
||||
"*.cpp"
|
||||
"*.h"
|
||||
)
|
||||
|
||||
add_library(md5 ${SOURCE})
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 2cb1a0c4009ecf368ecc74eb428394e10f9e6d00
|
||||
Subproject commit 1ed82759c68f92c4acc7e3f33b850cf9f01c8aba
|
@ -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()
|
||||
|
1476
lib/tolua++/src/bin/container_lua.h
Normal file
1476
lib/tolua++/src/bin/container_lua.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../../src/")
|
||||
|
||||
file(GLOB SOURCE
|
||||
"*.c"
|
||||
"*.h"
|
||||
)
|
||||
|
||||
if(NOT TARGET zlib)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -42,7 +42,7 @@ bool cLuaChunkStay::AddChunks(int a_ChunkCoordTableStackPos)
|
||||
|
||||
// Add each set of coords:
|
||||
int NumChunks = luaL_getn(L, a_ChunkCoordTableStackPos);
|
||||
m_Chunks.reserve(NumChunks);
|
||||
m_Chunks.reserve((size_t)NumChunks);
|
||||
for (int idx = 1; idx <= NumChunks; idx++)
|
||||
{
|
||||
// Push the idx-th element of the array onto stack top, check that it's a table:
|
||||
|
@ -4,12 +4,12 @@
|
||||
#include <time.h>
|
||||
// tolua_begin
|
||||
|
||||
unsigned int GetTime()
|
||||
inline unsigned int GetTime()
|
||||
{
|
||||
return (unsigned int)time(0);
|
||||
}
|
||||
|
||||
std::string GetChar( std::string & a_Str, unsigned int a_Idx )
|
||||
inline std::string GetChar( std::string & a_Str, unsigned int a_Idx )
|
||||
{
|
||||
return std::string(1, a_Str[ a_Idx ]);
|
||||
}
|
||||
|
@ -37,7 +37,7 @@
|
||||
/****************************
|
||||
* Better error reporting for Lua
|
||||
**/
|
||||
int tolua_do_error(lua_State* L, const char * a_pMsg, tolua_Error * a_pToLuaError)
|
||||
static int tolua_do_error(lua_State* L, const char * a_pMsg, tolua_Error * a_pToLuaError)
|
||||
{
|
||||
// Retrieve current function name
|
||||
lua_Debug entry;
|
||||
@ -57,7 +57,7 @@ int tolua_do_error(lua_State* L, const char * a_pMsg, tolua_Error * a_pToLuaErro
|
||||
|
||||
|
||||
|
||||
int lua_do_error(lua_State* L, const char * a_pFormat, ...)
|
||||
static int lua_do_error(lua_State* L, const char * a_pFormat, ...)
|
||||
{
|
||||
// Retrieve current function name
|
||||
lua_Debug entry;
|
||||
@ -235,7 +235,7 @@ static int tolua_Base64Decode(lua_State * tolua_S)
|
||||
|
||||
|
||||
|
||||
cPluginLua * GetLuaPlugin(lua_State * L)
|
||||
static cPluginLua * GetLuaPlugin(lua_State * L)
|
||||
{
|
||||
// Get the plugin identification out of LuaState:
|
||||
lua_getglobal(L, LUA_PLUGIN_INSTANCE_VAR_NAME);
|
||||
@ -1750,7 +1750,6 @@ static int tolua_cWorld_ChunkStay(lua_State * tolua_S)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
cLuaChunkStay * ChunkStay = new cLuaChunkStay(*Plugin);
|
||||
|
||||
// Read the params:
|
||||
cWorld * World = (cWorld *)tolua_tousertype(tolua_S, 1, NULL);
|
||||
@ -1760,8 +1759,12 @@ static int tolua_cWorld_ChunkStay(lua_State * tolua_S)
|
||||
L.LogStackTrace();
|
||||
return 0;
|
||||
}
|
||||
|
||||
cLuaChunkStay * ChunkStay = new cLuaChunkStay(*Plugin);
|
||||
|
||||
if (!ChunkStay->AddChunks(2))
|
||||
{
|
||||
delete ChunkStay;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1773,20 +1776,20 @@ static int tolua_cWorld_ChunkStay(lua_State * tolua_S)
|
||||
|
||||
|
||||
|
||||
static int tolua_cPlayer_GetGroups(lua_State* tolua_S)
|
||||
static int tolua_cPlayer_GetGroups(lua_State * tolua_S)
|
||||
{
|
||||
cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S, 1, NULL);
|
||||
cPlayer * self = (cPlayer *)tolua_tousertype(tolua_S, 1, NULL);
|
||||
|
||||
const cPlayer::GroupList & AllGroups = self->GetGroups();
|
||||
|
||||
lua_createtable(tolua_S, AllGroups.size(), 0);
|
||||
lua_createtable(tolua_S, (int)AllGroups.size(), 0);
|
||||
int newTable = lua_gettop(tolua_S);
|
||||
int index = 1;
|
||||
cPlayer::GroupList::const_iterator iter = AllGroups.begin();
|
||||
while(iter != AllGroups.end())
|
||||
while (iter != AllGroups.end())
|
||||
{
|
||||
const cGroup* Group = *iter;
|
||||
tolua_pushusertype( tolua_S, (void*)Group, "const cGroup" );
|
||||
const cGroup * Group = *iter;
|
||||
tolua_pushusertype(tolua_S, (void *)Group, "const cGroup");
|
||||
lua_rawseti(tolua_S, newTable, index);
|
||||
++iter;
|
||||
++index;
|
||||
@ -1798,20 +1801,20 @@ static int tolua_cPlayer_GetGroups(lua_State* tolua_S)
|
||||
|
||||
|
||||
|
||||
static int tolua_cPlayer_GetResolvedPermissions(lua_State* tolua_S)
|
||||
static int tolua_cPlayer_GetResolvedPermissions(lua_State * tolua_S)
|
||||
{
|
||||
cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S, 1, NULL);
|
||||
cPlayer * self = (cPlayer*) tolua_tousertype(tolua_S, 1, NULL);
|
||||
|
||||
cPlayer::StringList AllPermissions = self->GetResolvedPermissions();
|
||||
|
||||
lua_createtable(tolua_S, AllPermissions.size(), 0);
|
||||
lua_createtable(tolua_S, (int)AllPermissions.size(), 0);
|
||||
int newTable = lua_gettop(tolua_S);
|
||||
int index = 1;
|
||||
cPlayer::StringList::iterator iter = AllPermissions.begin();
|
||||
while(iter != AllPermissions.end())
|
||||
while (iter != AllPermissions.end())
|
||||
{
|
||||
std::string& Permission = *iter;
|
||||
tolua_pushstring( tolua_S, Permission.c_str() );
|
||||
std::string & Permission = *iter;
|
||||
lua_pushlstring(tolua_S, Permission.c_str(), Permission.length());
|
||||
lua_rawseti(tolua_S, newTable, index);
|
||||
++iter;
|
||||
++index;
|
||||
@ -2073,18 +2076,18 @@ static int tolua_get_HTTPRequest_FormData(lua_State* tolua_S)
|
||||
|
||||
static int tolua_cWebAdmin_GetPlugins(lua_State * tolua_S)
|
||||
{
|
||||
cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S, 1, NULL);
|
||||
cWebAdmin * self = (cWebAdmin *)tolua_tousertype(tolua_S, 1, NULL);
|
||||
|
||||
const cWebAdmin::PluginList & AllPlugins = self->GetPlugins();
|
||||
|
||||
lua_createtable(tolua_S, AllPlugins.size(), 0);
|
||||
lua_createtable(tolua_S, (int)AllPlugins.size(), 0);
|
||||
int newTable = lua_gettop(tolua_S);
|
||||
int index = 1;
|
||||
cWebAdmin::PluginList::const_iterator iter = AllPlugins.begin();
|
||||
while(iter != AllPlugins.end())
|
||||
while (iter != AllPlugins.end())
|
||||
{
|
||||
const cWebPlugin* Plugin = *iter;
|
||||
tolua_pushusertype( tolua_S, (void*)Plugin, "const cWebPlugin" );
|
||||
const cWebPlugin * Plugin = *iter;
|
||||
tolua_pushusertype(tolua_S, (void *)Plugin, "const cWebPlugin");
|
||||
lua_rawseti(tolua_S, newTable, index);
|
||||
++iter;
|
||||
++index;
|
||||
|
@ -56,7 +56,7 @@ public:
|
||||
virtual bool OnChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ) = 0;
|
||||
virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) = 0;
|
||||
virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0;
|
||||
virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) = 0;
|
||||
virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) = 0;
|
||||
virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) = 0;
|
||||
virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0;
|
||||
virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0;
|
||||
|
@ -400,14 +400,14 @@ bool cPluginLua::OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGri
|
||||
|
||||
|
||||
|
||||
bool cPluginLua::OnDisconnect(cPlayer * a_Player, const AString & a_Reason)
|
||||
bool cPluginLua::OnDisconnect(cClientHandle & a_Client, const AString & a_Reason)
|
||||
{
|
||||
cCSLock Lock(m_CriticalSection);
|
||||
bool res = false;
|
||||
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_DISCONNECT];
|
||||
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
|
||||
{
|
||||
m_LuaState.Call((int)(**itr), a_Player, a_Reason, cLuaState::Return, res);
|
||||
m_LuaState.Call((int)(**itr), &a_Client, a_Reason, cLuaState::Return, res);
|
||||
if (res)
|
||||
{
|
||||
return true;
|
||||
@ -1042,7 +1042,7 @@ bool cPluginLua::OnPluginMessage(cClientHandle & a_Client, const AString & a_Cha
|
||||
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLUGIN_MESSAGE];
|
||||
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
|
||||
{
|
||||
m_LuaState.Call((int)(**itr), &a_Client, a_Channel, a_Message);
|
||||
m_LuaState.Call((int)(**itr), &a_Client, a_Channel, a_Message, cLuaState::Return, res);
|
||||
if (res)
|
||||
{
|
||||
return true;
|
||||
|
@ -79,7 +79,7 @@ public:
|
||||
virtual bool OnChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override;
|
||||
virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) override;
|
||||
virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
|
||||
virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) override;
|
||||
virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) override;
|
||||
virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) override;
|
||||
virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override;
|
||||
virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override;
|
||||
|
@ -442,7 +442,7 @@ bool cPluginManager::CallHookCraftingNoRecipe(const cPlayer * a_Player, const cC
|
||||
|
||||
|
||||
|
||||
bool cPluginManager::CallHookDisconnect(cPlayer * a_Player, const AString & a_Reason)
|
||||
bool cPluginManager::CallHookDisconnect(cClientHandle & a_Client, const AString & a_Reason)
|
||||
{
|
||||
HookMap::iterator Plugins = m_Hooks.find(HOOK_DISCONNECT);
|
||||
if (Plugins == m_Hooks.end())
|
||||
@ -451,7 +451,7 @@ bool cPluginManager::CallHookDisconnect(cPlayer * a_Player, const AString & a_Re
|
||||
}
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnDisconnect(a_Player, a_Reason))
|
||||
if ((*itr)->OnDisconnect(a_Client, a_Reason))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ public: // tolua_export
|
||||
bool CallHookChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ);
|
||||
bool CallHookCollectingPickup (cPlayer * a_Player, cPickup & a_Pickup);
|
||||
bool CallHookCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
|
||||
bool CallHookDisconnect (cPlayer * a_Player, const AString & a_Reason);
|
||||
bool CallHookDisconnect (cClientHandle & a_Client, const AString & a_Reason);
|
||||
bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == NULL, it is a console cmd
|
||||
bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData);
|
||||
bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData);
|
||||
|
Binary file not shown.
180
src/BiomeDef.cpp
180
src/BiomeDef.cpp
@ -7,6 +7,88 @@
|
||||
#include "BiomeDef.h"
|
||||
|
||||
|
||||
|
||||
|
||||
// The "map" used for biome <-> string conversions:
|
||||
static struct {
|
||||
EMCSBiome m_Biome;
|
||||
const char * m_String;
|
||||
} g_BiomeMap[] =
|
||||
{
|
||||
{biOcean, "Ocean"} ,
|
||||
{biPlains, "Plains"},
|
||||
{biDesert, "Desert"},
|
||||
{biExtremeHills, "ExtremeHills"},
|
||||
{biForest, "Forest"},
|
||||
{biTaiga, "Taiga"},
|
||||
{biSwampland, "Swampland"},
|
||||
{biRiver, "River"},
|
||||
{biNether, "Hell"},
|
||||
{biNether, "Nether"},
|
||||
{biEnd, "Sky"},
|
||||
{biEnd, "End"},
|
||||
{biFrozenOcean, "FrozenOcean"},
|
||||
{biFrozenRiver, "FrozenRiver"},
|
||||
{biIcePlains, "IcePlains"},
|
||||
{biIcePlains, "Tundra"},
|
||||
{biIceMountains, "IceMountains"},
|
||||
{biMushroomIsland, "MushroomIsland"},
|
||||
{biMushroomShore, "MushroomShore"},
|
||||
{biBeach, "Beach"},
|
||||
{biDesertHills, "DesertHills"},
|
||||
{biForestHills, "ForestHills"},
|
||||
{biTaigaHills, "TaigaHills"},
|
||||
{biExtremeHillsEdge, "ExtremeHillsEdge"},
|
||||
{biJungle, "Jungle"},
|
||||
{biJungleHills, "JungleHills"},
|
||||
|
||||
// Release 1.7 biomes:
|
||||
{biJungleEdge, "JungleEdge"},
|
||||
{biDeepOcean, "DeepOcean"},
|
||||
{biStoneBeach, "StoneBeach"},
|
||||
{biColdBeach, "ColdBeach"},
|
||||
{biBirchForest, "BirchForest"},
|
||||
{biBirchForestHills, "BirchForestHills"},
|
||||
{biRoofedForest, "RoofedForest"},
|
||||
{biColdTaiga, "ColdTaiga"},
|
||||
{biColdTaigaHills, "ColdTaigaHills"},
|
||||
{biMegaTaiga, "MegaTaiga"},
|
||||
{biMegaTaigaHills, "MegaTaigaHills"},
|
||||
{biExtremeHillsPlus, "ExtremeHillsPlus"},
|
||||
{biSavanna, "Savanna"},
|
||||
{biSavannaPlateau, "SavannaPlateau"},
|
||||
{biMesa, "Mesa"},
|
||||
{biMesaPlateauF, "MesaPlateauF"},
|
||||
{biMesaPlateau, "MesaPlateau"},
|
||||
|
||||
// Release 1.7 variants:
|
||||
{biSunflowerPlains, "SunflowerPlains"},
|
||||
{biDesertM, "DesertM"},
|
||||
{biExtremeHillsM, "ExtremeHillsM"},
|
||||
{biFlowerForest, "FlowerForest"},
|
||||
{biTaigaM, "TaigaM"},
|
||||
{biSwamplandM, "SwamplandM"},
|
||||
{biIcePlainsSpikes, "IcePlainsSpikes"},
|
||||
{biJungleM, "JungleM"},
|
||||
{biJungleEdgeM, "JungleEdgeM"},
|
||||
{biBirchForestM, "BirchForestM"},
|
||||
{biBirchForestHillsM, "BirchForestHillsM"},
|
||||
{biRoofedForestM, "RoofedForestM"},
|
||||
{biColdTaigaM, "ColdTaigaM"},
|
||||
{biMegaSpruceTaiga, "MegaSpruceTaiga"},
|
||||
{biMegaSpruceTaigaHills, "MegaSpruceTaigaHills"},
|
||||
{biExtremeHillsPlusM, "ExtremeHillsPlusM"},
|
||||
{biSavannaM, "SavannaM"},
|
||||
{biSavannaPlateauM, "SavannaPlateauM"},
|
||||
{biMesaBryce, "MesaBryce"},
|
||||
{biMesaPlateauFM, "MesaPlateauFM"},
|
||||
{biMesaPlateauM, "MesaPlateauM"},
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
EMCSBiome StringToBiome(const AString & a_BiomeString)
|
||||
{
|
||||
// If it is a number, return it:
|
||||
@ -25,87 +107,11 @@ EMCSBiome StringToBiome(const AString & a_BiomeString)
|
||||
return biInvalidBiome;
|
||||
}
|
||||
|
||||
// Convert using the built-in map:
|
||||
static struct {
|
||||
EMCSBiome m_Biome;
|
||||
const char * m_String;
|
||||
} BiomeMap[] =
|
||||
for (size_t i = 0; i < ARRAYCOUNT(g_BiomeMap); i++)
|
||||
{
|
||||
{biOcean, "Ocean"} ,
|
||||
{biPlains, "Plains"},
|
||||
{biDesert, "Desert"},
|
||||
{biExtremeHills, "ExtremeHills"},
|
||||
{biForest, "Forest"},
|
||||
{biTaiga, "Taiga"},
|
||||
{biSwampland, "Swampland"},
|
||||
{biRiver, "River"},
|
||||
{biNether, "Hell"},
|
||||
{biNether, "Nether"},
|
||||
{biEnd, "Sky"},
|
||||
{biEnd, "End"},
|
||||
{biFrozenOcean, "FrozenOcean"},
|
||||
{biFrozenRiver, "FrozenRiver"},
|
||||
{biIcePlains, "IcePlains"},
|
||||
{biIcePlains, "Tundra"},
|
||||
{biIceMountains, "IceMountains"},
|
||||
{biMushroomIsland, "MushroomIsland"},
|
||||
{biMushroomShore, "MushroomShore"},
|
||||
{biBeach, "Beach"},
|
||||
{biDesertHills, "DesertHills"},
|
||||
{biForestHills, "ForestHills"},
|
||||
{biTaigaHills, "TaigaHills"},
|
||||
{biExtremeHillsEdge, "ExtremeHillsEdge"},
|
||||
{biJungle, "Jungle"},
|
||||
{biJungleHills, "JungleHills"},
|
||||
|
||||
// Release 1.7 biomes:
|
||||
{biJungleEdge, "JungleEdge"},
|
||||
{biDeepOcean, "DeepOcean"},
|
||||
{biStoneBeach, "StoneBeach"},
|
||||
{biColdBeach, "ColdBeach"},
|
||||
{biBirchForest, "BirchForest"},
|
||||
{biBirchForestHills, "BirchForestHills"},
|
||||
{biRoofedForest, "RoofedForest"},
|
||||
{biColdTaiga, "ColdTaiga"},
|
||||
{biColdTaigaHills, "ColdTaigaHills"},
|
||||
{biMegaTaiga, "MegaTaiga"},
|
||||
{biMegaTaigaHills, "MegaTaigaHills"},
|
||||
{biExtremeHillsPlus, "ExtremeHillsPlus"},
|
||||
{biSavanna, "Savanna"},
|
||||
{biSavannaPlateau, "SavannaPlateau"},
|
||||
{biMesa, "Mesa"},
|
||||
{biMesaPlateauF, "MesaPlateauF"},
|
||||
{biMesaPlateau, "MesaPlateau"},
|
||||
|
||||
// Release 1.7 variants:
|
||||
{biSunflowerPlains, "SunflowerPlains"},
|
||||
{biDesertM, "DesertM"},
|
||||
{biExtremeHillsM, "ExtremeHillsM"},
|
||||
{biFlowerForest, "FlowerForest"},
|
||||
{biTaigaM, "TaigaM"},
|
||||
{biSwamplandM, "SwamplandM"},
|
||||
{biIcePlainsSpikes, "IcePlainsSpikes"},
|
||||
{biJungleM, "JungleM"},
|
||||
{biJungleEdgeM, "JungleEdgeM"},
|
||||
{biBirchForestM, "BirchForestM"},
|
||||
{biBirchForestHillsM, "BirchForestHillsM"},
|
||||
{biRoofedForestM, "RoofedForestM"},
|
||||
{biColdTaigaM, "ColdTaigaM"},
|
||||
{biMegaSpruceTaiga, "MegaSpruceTaiga"},
|
||||
{biMegaSpruceTaigaHills, "MegaSpruceTaigaHills"},
|
||||
{biExtremeHillsPlusM, "ExtremeHillsPlusM"},
|
||||
{biSavannaM, "SavannaM"},
|
||||
{biSavannaPlateauM, "SavannaPlateauM"},
|
||||
{biMesaBryce, "MesaBryce"},
|
||||
{biMesaPlateauFM, "MesaPlateauFM"},
|
||||
{biMesaPlateauM, "MesaPlateauM"},
|
||||
} ;
|
||||
|
||||
for (size_t i = 0; i < ARRAYCOUNT(BiomeMap); i++)
|
||||
{
|
||||
if (NoCaseCompare(BiomeMap[i].m_String, a_BiomeString) == 0)
|
||||
if (NoCaseCompare(g_BiomeMap[i].m_String, a_BiomeString) == 0)
|
||||
{
|
||||
return BiomeMap[i].m_Biome;
|
||||
return g_BiomeMap[i].m_Biome;
|
||||
}
|
||||
} // for i - BiomeMap[]
|
||||
return biInvalidBiome;
|
||||
@ -115,6 +121,22 @@ EMCSBiome StringToBiome(const AString & a_BiomeString)
|
||||
|
||||
|
||||
|
||||
AString BiomeToString(int a_Biome)
|
||||
{
|
||||
for (size_t i = 0; i < ARRAYCOUNT(g_BiomeMap); i++)
|
||||
{
|
||||
if (g_BiomeMap[i].m_Biome == a_Biome)
|
||||
{
|
||||
return g_BiomeMap[i].m_String;
|
||||
}
|
||||
}
|
||||
return AString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool IsBiomeNoDownfall(EMCSBiome a_Biome)
|
||||
{
|
||||
switch (a_Biome)
|
||||
|
@ -104,10 +104,13 @@ enum EMCSBiome
|
||||
biMaxVariantBiome = biNumVariantBiomes - 1, // The maximum biome value
|
||||
} ;
|
||||
|
||||
/// Translates a biome string to biome enum. Takes either a number or a biome alias (built-in). Returns biInvalidBiome on failure.
|
||||
/** Translates a biome string to biome enum. Takes either a number or a biome alias (built-in). Returns biInvalidBiome on failure. */
|
||||
extern EMCSBiome StringToBiome(const AString & a_BiomeString);
|
||||
|
||||
/// Returns true if the biome has no downfall - deserts and savannas
|
||||
/** Translates biome enum into biome string. Returns empty string on failure (unknown biome). */
|
||||
extern AString BiomeToString(int a_Biome);
|
||||
|
||||
/** Returns true if the biome has no downfall - deserts and savannas */
|
||||
extern bool IsBiomeNoDownfall(EMCSBiome a_Biome);
|
||||
|
||||
|
||||
|
@ -14,17 +14,29 @@
|
||||
|
||||
|
||||
|
||||
// Disable MSVC warnings: "conditional expression is constant"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4127)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
typedef void (CombinatorFunc)(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta);
|
||||
|
||||
// This wild construct allows us to pass a function argument and still have it inlined by the compiler :)
|
||||
/// Merges two blocktypes and blockmetas of the specified sizes and offsets using the specified combinator function
|
||||
template<typename Combinator> void InternalMergeBlocks(
|
||||
template<bool MetasValid, CombinatorFunc Combinator>
|
||||
void InternalMergeBlocks(
|
||||
BLOCKTYPE * a_DstTypes, const BLOCKTYPE * a_SrcTypes,
|
||||
NIBBLETYPE * a_DstMetas, const NIBBLETYPE * a_SrcMetas,
|
||||
int a_SizeX, int a_SizeY, int a_SizeZ,
|
||||
int a_SrcOffX, int a_SrcOffY, int a_SrcOffZ,
|
||||
int a_DstOffX, int a_DstOffY, int a_DstOffZ,
|
||||
int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ,
|
||||
int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ,
|
||||
Combinator a_Combinator
|
||||
int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ
|
||||
)
|
||||
{
|
||||
UNUSED(a_SrcSizeY);
|
||||
@ -41,7 +53,15 @@ template<typename Combinator> void InternalMergeBlocks(
|
||||
int DstIdx = DstBaseZ + a_DstOffX;
|
||||
for (int x = 0; x < a_SizeX; x++)
|
||||
{
|
||||
a_Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], a_DstMetas[DstIdx], a_SrcMetas[SrcIdx]);
|
||||
if (MetasValid)
|
||||
{
|
||||
Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], a_DstMetas[DstIdx], a_SrcMetas[SrcIdx]);
|
||||
}
|
||||
else
|
||||
{
|
||||
BLOCKTYPE FakeDestMeta = 0;
|
||||
Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], FakeDestMeta, (NIBBLETYPE)0);
|
||||
}
|
||||
++DstIdx;
|
||||
++SrcIdx;
|
||||
} // for x
|
||||
@ -54,10 +74,14 @@ template<typename Combinator> void InternalMergeBlocks(
|
||||
|
||||
|
||||
/// Combinator used for cBlockArea::msOverwrite merging
|
||||
static inline void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
|
||||
template<bool MetaValid>
|
||||
void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
|
||||
{
|
||||
a_DstType = a_SrcType;
|
||||
a_DstMeta = a_SrcMeta;
|
||||
if (MetaValid)
|
||||
{
|
||||
a_DstMeta = a_SrcMeta;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -65,12 +89,16 @@ static inline void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_S
|
||||
|
||||
|
||||
/// Combinator used for cBlockArea::msFillAir merging
|
||||
static inline void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
|
||||
template<bool MetaValid>
|
||||
void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
|
||||
{
|
||||
if (a_DstType == E_BLOCK_AIR)
|
||||
{
|
||||
a_DstType = a_SrcType;
|
||||
a_DstMeta = a_SrcMeta;
|
||||
if (MetaValid)
|
||||
{
|
||||
a_DstMeta = a_SrcMeta;
|
||||
}
|
||||
}
|
||||
// "else" is the default, already in place
|
||||
}
|
||||
@ -80,12 +108,16 @@ static inline void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_Src
|
||||
|
||||
|
||||
/// Combinator used for cBlockArea::msImprint merging
|
||||
static inline void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
|
||||
template<bool MetaValid>
|
||||
void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
|
||||
{
|
||||
if (a_SrcType != E_BLOCK_AIR)
|
||||
{
|
||||
a_DstType = a_SrcType;
|
||||
a_DstMeta = a_SrcMeta;
|
||||
if (MetaValid)
|
||||
{
|
||||
a_DstMeta = a_SrcMeta;
|
||||
}
|
||||
}
|
||||
// "else" is the default, already in place
|
||||
}
|
||||
@ -95,7 +127,8 @@ static inline void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_Src
|
||||
|
||||
|
||||
/// Combinator used for cBlockArea::msLake merging
|
||||
static inline void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
|
||||
template<bool MetaValid>
|
||||
void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
|
||||
{
|
||||
// Sponge is the NOP block
|
||||
if (a_SrcType == E_BLOCK_SPONGE)
|
||||
@ -107,7 +140,10 @@ static inline void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcTyp
|
||||
if (a_SrcType == E_BLOCK_AIR)
|
||||
{
|
||||
a_DstType = E_BLOCK_AIR;
|
||||
a_DstMeta = 0;
|
||||
if (MetaValid)
|
||||
{
|
||||
a_DstMeta = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -132,7 +168,10 @@ static inline void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcTyp
|
||||
case E_BLOCK_STATIONARY_LAVA:
|
||||
{
|
||||
a_DstType = a_SrcType;
|
||||
a_DstMeta = a_SrcMeta;
|
||||
if (MetaValid)
|
||||
{
|
||||
a_DstMeta = a_SrcMeta;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -146,7 +185,10 @@ static inline void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcTyp
|
||||
case E_BLOCK_MYCELIUM:
|
||||
{
|
||||
a_DstType = E_BLOCK_STONE;
|
||||
a_DstMeta = 0;
|
||||
if (MetaValid)
|
||||
{
|
||||
a_DstMeta = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -159,13 +201,17 @@ static inline void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcTyp
|
||||
|
||||
|
||||
/** Combinator used for cBlockArea::msSpongePrint merging */
|
||||
static inline void MergeCombinatorSpongePrint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
|
||||
template<bool MetaValid>
|
||||
void MergeCombinatorSpongePrint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
|
||||
{
|
||||
// Sponge overwrites nothing, everything else overwrites anything
|
||||
if (a_SrcType != E_BLOCK_SPONGE)
|
||||
{
|
||||
a_DstType = a_SrcType;
|
||||
a_DstMeta = a_SrcMeta;
|
||||
if (MetaValid)
|
||||
{
|
||||
a_DstMeta = a_SrcMeta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,17 +220,24 @@ static inline void MergeCombinatorSpongePrint(BLOCKTYPE & a_DstType, BLOCKTYPE a
|
||||
|
||||
|
||||
/** Combinator used for cBlockArea::msDifference merging */
|
||||
static inline void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
|
||||
template<bool MetaValid>
|
||||
void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
|
||||
{
|
||||
if ((a_DstType == a_SrcType) && (a_DstMeta == a_SrcMeta))
|
||||
if ((a_DstType == a_SrcType) && (!MetaValid || (a_DstMeta == a_SrcMeta)))
|
||||
{
|
||||
a_DstType = E_BLOCK_AIR;
|
||||
a_DstMeta = 0;
|
||||
if (MetaValid)
|
||||
{
|
||||
a_DstMeta = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
a_DstType = a_SrcType;
|
||||
a_DstMeta = a_SrcMeta;
|
||||
if (MetaValid)
|
||||
{
|
||||
a_DstMeta = a_SrcMeta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,16 +246,25 @@ static inline void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_
|
||||
|
||||
|
||||
/** Combinator used for cBlockArea::msMask merging */
|
||||
static inline void MergeCombinatorMask(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
|
||||
template<bool MetaValid>
|
||||
void MergeCombinatorMask(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
|
||||
{
|
||||
// If the blocks are the same, keep the dest; otherwise replace with air
|
||||
if ((a_SrcType != a_DstType) || (a_SrcMeta != a_DstMeta))
|
||||
if ((a_SrcType != a_DstType) || !MetaValid || (a_SrcMeta != a_DstMeta))
|
||||
{
|
||||
a_DstType = E_BLOCK_AIR;
|
||||
a_DstMeta = 0;
|
||||
if (MetaValid)
|
||||
{
|
||||
a_DstMeta = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Re-enable previously disabled MSVC warnings
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
@ -484,7 +546,7 @@ void cBlockArea::CopyTo(cBlockArea & a_Into) const
|
||||
a_Into.Clear();
|
||||
a_Into.SetSize(m_Size.x, m_Size.y, m_Size.z, GetDataTypes());
|
||||
a_Into.m_Origin = m_Origin;
|
||||
int BlockCount = GetBlockCount();
|
||||
size_t BlockCount = GetBlockCount();
|
||||
if (HasBlockTypes())
|
||||
{
|
||||
memcpy(a_Into.m_BlockTypes, m_BlockTypes, BlockCount * sizeof(BLOCKTYPE));
|
||||
@ -532,7 +594,7 @@ void cBlockArea::DumpToRawFile(const AString & a_FileName)
|
||||
f.Write(&SizeZ, 4);
|
||||
unsigned char DataTypes = (unsigned char)GetDataTypes();
|
||||
f.Write(&DataTypes, 1);
|
||||
int NumBlocks = GetBlockCount();
|
||||
size_t NumBlocks = GetBlockCount();
|
||||
if (HasBlockTypes())
|
||||
{
|
||||
f.Write(m_BlockTypes, NumBlocks * sizeof(BLOCKTYPE));
|
||||
@ -637,155 +699,19 @@ void cBlockArea::Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMa
|
||||
|
||||
void cBlockArea::Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy)
|
||||
{
|
||||
// Block types are compulsory, block metas are voluntary
|
||||
if (!HasBlockTypes() || !a_Src.HasBlockTypes())
|
||||
{
|
||||
LOGWARNING("%s: cannot merge because one of the areas doesn't have blocktypes.", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
// Dst is *this, Src is a_Src
|
||||
int SrcOffX = std::max(0, -a_RelX); // Offset in Src where to start reading
|
||||
int DstOffX = std::max(0, a_RelX); // Offset in Dst where to start writing
|
||||
int SizeX = std::min(a_Src.GetSizeX() - SrcOffX, GetSizeX() - DstOffX); // How many blocks to copy
|
||||
|
||||
int SrcOffY = std::max(0, -a_RelY); // Offset in Src where to start reading
|
||||
int DstOffY = std::max(0, a_RelY); // Offset in Dst where to start writing
|
||||
int SizeY = std::min(a_Src.GetSizeY() - SrcOffY, GetSizeY() - DstOffY); // How many blocks to copy
|
||||
|
||||
int SrcOffZ = std::max(0, -a_RelZ); // Offset in Src where to start reading
|
||||
int DstOffZ = std::max(0, a_RelZ); // Offset in Dst where to start writing
|
||||
int SizeZ = std::min(a_Src.GetSizeZ() - SrcOffZ, GetSizeZ() - DstOffZ); // How many blocks to copy
|
||||
|
||||
const NIBBLETYPE * SrcMetas = a_Src.GetBlockMetas();
|
||||
NIBBLETYPE * DstMetas = m_BlockMetas;
|
||||
|
||||
bool IsDummyMetas = ((SrcMetas == NULL) || (DstMetas == NULL));
|
||||
|
||||
if (IsDummyMetas)
|
||||
{
|
||||
SrcMetas = new NIBBLETYPE[a_Src.GetBlockCount()];
|
||||
DstMetas = new NIBBLETYPE[GetBlockCount()];
|
||||
MergeByStrategy<false>(a_Src, a_RelX, a_RelY, a_RelZ, a_Strategy, SrcMetas, DstMetas);
|
||||
}
|
||||
|
||||
switch (a_Strategy)
|
||||
else
|
||||
{
|
||||
case msOverwrite:
|
||||
{
|
||||
InternalMergeBlocks(
|
||||
m_BlockTypes, a_Src.GetBlockTypes(),
|
||||
DstMetas, SrcMetas,
|
||||
SizeX, SizeY, SizeZ,
|
||||
SrcOffX, SrcOffY, SrcOffZ,
|
||||
DstOffX, DstOffY, DstOffZ,
|
||||
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
|
||||
m_Size.x, m_Size.y, m_Size.z,
|
||||
MergeCombinatorOverwrite
|
||||
);
|
||||
break;
|
||||
} // case msOverwrite
|
||||
|
||||
case msFillAir:
|
||||
{
|
||||
InternalMergeBlocks(
|
||||
m_BlockTypes, a_Src.GetBlockTypes(),
|
||||
DstMetas, SrcMetas,
|
||||
SizeX, SizeY, SizeZ,
|
||||
SrcOffX, SrcOffY, SrcOffZ,
|
||||
DstOffX, DstOffY, DstOffZ,
|
||||
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
|
||||
m_Size.x, m_Size.y, m_Size.z,
|
||||
MergeCombinatorFillAir
|
||||
);
|
||||
break;
|
||||
} // case msFillAir
|
||||
|
||||
case msImprint:
|
||||
{
|
||||
InternalMergeBlocks(
|
||||
m_BlockTypes, a_Src.GetBlockTypes(),
|
||||
DstMetas, SrcMetas,
|
||||
SizeX, SizeY, SizeZ,
|
||||
SrcOffX, SrcOffY, SrcOffZ,
|
||||
DstOffX, DstOffY, DstOffZ,
|
||||
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
|
||||
m_Size.x, m_Size.y, m_Size.z,
|
||||
MergeCombinatorImprint
|
||||
);
|
||||
break;
|
||||
} // case msImprint
|
||||
|
||||
case msLake:
|
||||
{
|
||||
InternalMergeBlocks(
|
||||
m_BlockTypes, a_Src.GetBlockTypes(),
|
||||
DstMetas, SrcMetas,
|
||||
SizeX, SizeY, SizeZ,
|
||||
SrcOffX, SrcOffY, SrcOffZ,
|
||||
DstOffX, DstOffY, DstOffZ,
|
||||
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
|
||||
m_Size.x, m_Size.y, m_Size.z,
|
||||
MergeCombinatorLake
|
||||
);
|
||||
break;
|
||||
} // case msLake
|
||||
|
||||
case msSpongePrint:
|
||||
{
|
||||
InternalMergeBlocks(
|
||||
m_BlockTypes, a_Src.GetBlockTypes(),
|
||||
DstMetas, SrcMetas,
|
||||
SizeX, SizeY, SizeZ,
|
||||
SrcOffX, SrcOffY, SrcOffZ,
|
||||
DstOffX, DstOffY, DstOffZ,
|
||||
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
|
||||
m_Size.x, m_Size.y, m_Size.z,
|
||||
MergeCombinatorSpongePrint
|
||||
);
|
||||
break;
|
||||
} // case msSpongePrint
|
||||
|
||||
case msDifference:
|
||||
{
|
||||
InternalMergeBlocks(
|
||||
m_BlockTypes, a_Src.GetBlockTypes(),
|
||||
DstMetas, SrcMetas,
|
||||
SizeX, SizeY, SizeZ,
|
||||
SrcOffX, SrcOffY, SrcOffZ,
|
||||
DstOffX, DstOffY, DstOffZ,
|
||||
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
|
||||
m_Size.x, m_Size.y, m_Size.z,
|
||||
MergeCombinatorDifference
|
||||
);
|
||||
break;
|
||||
} // case msDifference
|
||||
|
||||
case msMask:
|
||||
{
|
||||
InternalMergeBlocks(
|
||||
m_BlockTypes, a_Src.GetBlockTypes(),
|
||||
DstMetas, SrcMetas,
|
||||
SizeX, SizeY, SizeZ,
|
||||
SrcOffX, SrcOffY, SrcOffZ,
|
||||
DstOffX, DstOffY, DstOffZ,
|
||||
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
|
||||
m_Size.x, m_Size.y, m_Size.z,
|
||||
MergeCombinatorMask
|
||||
);
|
||||
break;
|
||||
} // case msMask
|
||||
|
||||
default:
|
||||
{
|
||||
LOGWARNING("Unknown block area merge strategy: %d", a_Strategy);
|
||||
ASSERT(!"Unknown block area merge strategy");
|
||||
break;
|
||||
}
|
||||
} // switch (a_Strategy)
|
||||
|
||||
if (IsDummyMetas)
|
||||
{
|
||||
delete[] SrcMetas;
|
||||
delete[] DstMetas;
|
||||
MergeByStrategy<true>(a_Src, a_RelX, a_RelY, a_RelZ, a_Strategy, SrcMetas, DstMetas);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2079,7 +2005,7 @@ void cBlockArea::ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, i
|
||||
int NewSizeX = m_Size.x + a_SubMinX + a_AddMaxX;
|
||||
int NewSizeY = m_Size.y + a_SubMinY + a_AddMaxY;
|
||||
int NewSizeZ = m_Size.z + a_SubMinZ + a_AddMaxZ;
|
||||
int BlockCount = NewSizeX * NewSizeY * NewSizeZ;
|
||||
size_t BlockCount = (size_t)(NewSizeX * NewSizeY * NewSizeZ);
|
||||
BLOCKTYPE * NewBlockTypes = new BLOCKTYPE[BlockCount];
|
||||
memset(NewBlockTypes, 0, BlockCount * sizeof(BLOCKTYPE));
|
||||
int OldIndex = 0;
|
||||
@ -2109,7 +2035,7 @@ void cBlockArea::ExpandNibbles(NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMa
|
||||
int NewSizeX = m_Size.x + a_SubMinX + a_AddMaxX;
|
||||
int NewSizeY = m_Size.y + a_SubMinY + a_AddMaxY;
|
||||
int NewSizeZ = m_Size.z + a_SubMinZ + a_AddMaxZ;
|
||||
int BlockCount = NewSizeX * NewSizeY * NewSizeZ;
|
||||
size_t BlockCount = (size_t)(NewSizeX * NewSizeY * NewSizeZ);
|
||||
NIBBLETYPE * NewNibbles = new NIBBLETYPE[BlockCount];
|
||||
memset(NewNibbles, 0, BlockCount * sizeof(NIBBLETYPE));
|
||||
int OldIndex = 0;
|
||||
@ -2161,4 +2087,137 @@ void cBlockArea::RelSetData(
|
||||
|
||||
|
||||
|
||||
template<bool MetasValid>
|
||||
void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy, const NIBBLETYPE * SrcMetas, NIBBLETYPE * DstMetas)
|
||||
{
|
||||
// Block types are compulsory, block metas are voluntary
|
||||
if (!HasBlockTypes() || !a_Src.HasBlockTypes())
|
||||
{
|
||||
LOGWARNING("%s: cannot merge because one of the areas doesn't have blocktypes.", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
// Dst is *this, Src is a_Src
|
||||
int SrcOffX = std::max(0, -a_RelX); // Offset in Src where to start reading
|
||||
int DstOffX = std::max(0, a_RelX); // Offset in Dst where to start writing
|
||||
int SizeX = std::min(a_Src.GetSizeX() - SrcOffX, GetSizeX() - DstOffX); // How many blocks to copy
|
||||
|
||||
int SrcOffY = std::max(0, -a_RelY); // Offset in Src where to start reading
|
||||
int DstOffY = std::max(0, a_RelY); // Offset in Dst where to start writing
|
||||
int SizeY = std::min(a_Src.GetSizeY() - SrcOffY, GetSizeY() - DstOffY); // How many blocks to copy
|
||||
|
||||
int SrcOffZ = std::max(0, -a_RelZ); // Offset in Src where to start reading
|
||||
int DstOffZ = std::max(0, a_RelZ); // Offset in Dst where to start writing
|
||||
int SizeZ = std::min(a_Src.GetSizeZ() - SrcOffZ, GetSizeZ() - DstOffZ); // How many blocks to copy
|
||||
|
||||
switch (a_Strategy)
|
||||
{
|
||||
case cBlockArea::msOverwrite:
|
||||
{
|
||||
InternalMergeBlocks<MetasValid, MergeCombinatorOverwrite<MetasValid> >(
|
||||
m_BlockTypes, a_Src.GetBlockTypes(),
|
||||
DstMetas, SrcMetas,
|
||||
SizeX, SizeY, SizeZ,
|
||||
SrcOffX, SrcOffY, SrcOffZ,
|
||||
DstOffX, DstOffY, DstOffZ,
|
||||
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
|
||||
m_Size.x, m_Size.y, m_Size.z
|
||||
);
|
||||
break;
|
||||
} // case msOverwrite
|
||||
|
||||
case cBlockArea::msFillAir:
|
||||
{
|
||||
InternalMergeBlocks<MetasValid, MergeCombinatorFillAir<MetasValid> >(
|
||||
m_BlockTypes, a_Src.GetBlockTypes(),
|
||||
DstMetas, SrcMetas,
|
||||
SizeX, SizeY, SizeZ,
|
||||
SrcOffX, SrcOffY, SrcOffZ,
|
||||
DstOffX, DstOffY, DstOffZ,
|
||||
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
|
||||
m_Size.x, m_Size.y, m_Size.z
|
||||
);
|
||||
break;
|
||||
} // case msFillAir
|
||||
|
||||
case cBlockArea::msImprint:
|
||||
{
|
||||
InternalMergeBlocks<MetasValid, MergeCombinatorImprint<MetasValid> >(
|
||||
m_BlockTypes, a_Src.GetBlockTypes(),
|
||||
DstMetas, SrcMetas,
|
||||
SizeX, SizeY, SizeZ,
|
||||
SrcOffX, SrcOffY, SrcOffZ,
|
||||
DstOffX, DstOffY, DstOffZ,
|
||||
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
|
||||
m_Size.x, m_Size.y, m_Size.z
|
||||
);
|
||||
break;
|
||||
} // case msImprint
|
||||
|
||||
case cBlockArea::msLake:
|
||||
{
|
||||
InternalMergeBlocks<MetasValid, MergeCombinatorLake<MetasValid> >(
|
||||
m_BlockTypes, a_Src.GetBlockTypes(),
|
||||
DstMetas, SrcMetas,
|
||||
SizeX, SizeY, SizeZ,
|
||||
SrcOffX, SrcOffY, SrcOffZ,
|
||||
DstOffX, DstOffY, DstOffZ,
|
||||
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
|
||||
m_Size.x, m_Size.y, m_Size.z
|
||||
);
|
||||
break;
|
||||
} // case msLake
|
||||
|
||||
case cBlockArea::msSpongePrint:
|
||||
{
|
||||
InternalMergeBlocks<MetasValid, MergeCombinatorSpongePrint<MetasValid> >(
|
||||
m_BlockTypes, a_Src.GetBlockTypes(),
|
||||
DstMetas, SrcMetas,
|
||||
SizeX, SizeY, SizeZ,
|
||||
SrcOffX, SrcOffY, SrcOffZ,
|
||||
DstOffX, DstOffY, DstOffZ,
|
||||
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
|
||||
m_Size.x, m_Size.y, m_Size.z
|
||||
);
|
||||
break;
|
||||
} // case msSpongePrint
|
||||
|
||||
case cBlockArea::msDifference:
|
||||
{
|
||||
InternalMergeBlocks<MetasValid, MergeCombinatorDifference<MetasValid> >(
|
||||
m_BlockTypes, a_Src.GetBlockTypes(),
|
||||
DstMetas, SrcMetas,
|
||||
SizeX, SizeY, SizeZ,
|
||||
SrcOffX, SrcOffY, SrcOffZ,
|
||||
DstOffX, DstOffY, DstOffZ,
|
||||
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
|
||||
m_Size.x, m_Size.y, m_Size.z
|
||||
);
|
||||
break;
|
||||
} // case msDifference
|
||||
|
||||
case cBlockArea::msMask:
|
||||
{
|
||||
InternalMergeBlocks<MetasValid, MergeCombinatorMask<MetasValid> >(
|
||||
m_BlockTypes, a_Src.GetBlockTypes(),
|
||||
DstMetas, SrcMetas,
|
||||
SizeX, SizeY, SizeZ,
|
||||
SrcOffX, SrcOffY, SrcOffZ,
|
||||
DstOffX, DstOffY, DstOffZ,
|
||||
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
|
||||
m_Size.x, m_Size.y, m_Size.z
|
||||
);
|
||||
break;
|
||||
} // case msMask
|
||||
|
||||
default:
|
||||
{
|
||||
LOGWARNING("Unknown block area merge strategy: %d", a_Strategy);
|
||||
ASSERT(!"Unknown block area merge strategy");
|
||||
break;
|
||||
}
|
||||
} // switch (a_Strategy)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -294,7 +294,7 @@ public:
|
||||
NIBBLETYPE * GetBlockMetas (void) const { return m_BlockMetas; } // NOTE: one byte per block!
|
||||
NIBBLETYPE * GetBlockLight (void) const { return m_BlockLight; } // NOTE: one byte per block!
|
||||
NIBBLETYPE * GetBlockSkyLight(void) const { return m_BlockSkyLight; } // NOTE: one byte per block!
|
||||
int GetBlockCount(void) const { return m_Size.x * m_Size.y * m_Size.z; }
|
||||
size_t GetBlockCount(void) const { return (size_t)(m_Size.x * m_Size.y * m_Size.z); }
|
||||
int MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const;
|
||||
|
||||
protected:
|
||||
@ -363,6 +363,9 @@ protected:
|
||||
int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
|
||||
NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
|
||||
);
|
||||
|
||||
template<bool MetasValid>
|
||||
void MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy, const NIBBLETYPE * SrcMetas, NIBBLETYPE * DstMetas);
|
||||
// tolua_begin
|
||||
} ;
|
||||
// tolua_end
|
||||
|
116
src/BlockEntities/BeaconEntity.cpp
Normal file
116
src/BlockEntities/BeaconEntity.cpp
Normal 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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
44
src/BlockEntities/BeaconEntity.h
Normal file
44
src/BlockEntities/BeaconEntity.h
Normal 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;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../")
|
||||
|
||||
file(GLOB SOURCE
|
||||
"*.cpp"
|
||||
"*.h"
|
||||
)
|
||||
|
||||
add_library(BlockEntities ${SOURCE})
|
||||
|
@ -21,7 +21,8 @@
|
||||
cCommandBlockEntity::cCommandBlockEntity(int a_X, int a_Y, int a_Z, cWorld * a_World) :
|
||||
super(E_BLOCK_COMMAND_BLOCK, a_X, a_Y, a_Z, a_World),
|
||||
m_ShouldExecute(false),
|
||||
m_IsPowered(false)
|
||||
m_IsPowered(false),
|
||||
m_Result(0)
|
||||
{}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
cMobHeadEntity::cMobHeadEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
|
||||
super(E_BLOCK_HEAD, a_BlockX, a_BlockY, a_BlockZ, a_World),
|
||||
m_Type(SKULL_TYPE_SKELETON),
|
||||
m_Rotation(SKULL_ROTATION_NORTH),
|
||||
m_Owner("")
|
||||
{
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
a_Item.m_ItemDamage = atoi(Split[1].c_str());
|
||||
a_Item.m_ItemDamage = (short)atoi(Split[1].c_str());
|
||||
if ((a_Item.m_ItemDamage == 0) && (Split[1] != "0"))
|
||||
{
|
||||
// Parsing the number failed
|
||||
@ -324,7 +324,7 @@ eDimension StringToDimension(const AString & a_DimensionString)
|
||||
{ dimOverworld, "Normal"},
|
||||
{ dimOverworld, "World"},
|
||||
{ dimNether, "Nether"},
|
||||
{ dimNether, "Hell"}, // Alternate name for End
|
||||
{ dimNether, "Hell"}, // Alternate name for Nether
|
||||
{ dimEnd, "End"},
|
||||
{ dimEnd, "Sky"}, // Old name for End
|
||||
} ;
|
||||
@ -337,7 +337,8 @@ eDimension StringToDimension(const AString & a_DimensionString)
|
||||
} // for i - DimensionMap[]
|
||||
|
||||
// Not found
|
||||
return (eDimension)-1000;
|
||||
LOGWARNING("Unknown dimension: \"%s\". Setting to Overworld", a_DimensionString.c_str());
|
||||
return dimOverworld;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -133,6 +133,7 @@ void cBlockInfo::Initialize(void)
|
||||
ms_Info[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE].m_Transparent = true;
|
||||
ms_Info[E_BLOCK_ICE ].m_Transparent = true;
|
||||
ms_Info[E_BLOCK_IRON_DOOR ].m_Transparent = true;
|
||||
ms_Info[E_BLOCK_LADDER ].m_Transparent = true;
|
||||
ms_Info[E_BLOCK_LAVA ].m_Transparent = true;
|
||||
ms_Info[E_BLOCK_LEAVES ].m_Transparent = true;
|
||||
ms_Info[E_BLOCK_LEVER ].m_Transparent = true;
|
||||
|
@ -23,6 +23,13 @@ public:
|
||||
{
|
||||
a_Pickups.push_back(cItem(E_BLOCK_ANVIL, 1, a_BlockMeta >> 2));
|
||||
}
|
||||
|
||||
|
||||
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 cAnvilWindow(a_BlockX, a_BlockY, a_BlockZ);
|
||||
a_Player->OpenWindow(Window);
|
||||
}
|
||||
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
|
@ -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;
|
||||
}
|
||||
|
37
src/Blocks/BlockEnchantmentTable.h
Normal file
37
src/Blocks/BlockEnchantmentTable.h
Normal 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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
@ -68,7 +68,6 @@ public:
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
for (int newY = Y + 1; newY < cChunkDef::Height; newY++)
|
||||
{
|
||||
@ -84,7 +83,7 @@ public:
|
||||
// This is because the frame is a solid obsidian pillar
|
||||
if ((MaxY != 0) && (newY == Y + 1))
|
||||
{
|
||||
return EvaluatePortalBorder(X, newY, Z, MaxY, a_ChunkInterface);
|
||||
return EvaluatePortalBorder(X, newY, Z, MaxY, a_ChunkInterface) ? -1 /* -1 = found a frame */ : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -99,18 +98,18 @@ public:
|
||||
}
|
||||
|
||||
/// Evaluates if coords have a valid border on top, based on MaxY
|
||||
int EvaluatePortalBorder(int X, int FoundObsidianY, int Z, int MaxY, cChunkInterface & a_ChunkInterface)
|
||||
bool EvaluatePortalBorder(int X, int FoundObsidianY, int Z, int MaxY, cChunkInterface & a_ChunkInterface)
|
||||
{
|
||||
for (int checkBorder = FoundObsidianY + 1; checkBorder <= MaxY - 1; checkBorder++) // FoundObsidianY + 1: FoundObsidianY has already been checked in FindObsidianCeiling; MaxY - 1: portal doesn't need corners
|
||||
{
|
||||
if (a_ChunkInterface.GetBlock(X, checkBorder, Z) != E_BLOCK_OBSIDIAN)
|
||||
{
|
||||
// Base obsidian, base + 1 obsidian, base + x NOT obsidian -> not complete portal
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Everything was obsidian, found a border!
|
||||
return -1; // Return -1 for a frame border
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Finds entire frame in any direction with the coordinates of a base block and fills hole with nether portal (START HERE)
|
||||
@ -169,7 +168,7 @@ public:
|
||||
{
|
||||
return false; // Not valid slice, no portal can be formed
|
||||
}
|
||||
} XZP = X1 - 1; // Set boundary of frame interior, note that for some reason, the loop of X and the loop of Z go to different numbers, hence -1 here and -2 there
|
||||
} XZP = X1 - 1; // Set boundary of frame interior
|
||||
for (; ((a_ChunkInterface.GetBlock(X2, Y, Z) == E_BLOCK_OBSIDIAN) || (a_ChunkInterface.GetBlock(X2, Y + 1, Z) == E_BLOCK_OBSIDIAN)); X2--) // Go the other direction (XM)
|
||||
{
|
||||
int Value = FindObsidianCeiling(X2, Y, Z, a_ChunkInterface, MaxY);
|
||||
@ -199,13 +198,13 @@ public:
|
||||
if ((Value == -1) || (ValueTwo == -1))
|
||||
{
|
||||
FoundFrameZP = true;
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
else if ((Value != MaxY) && (ValueTwo != MaxY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} XZP = Z1 - 2;
|
||||
} XZP = Z1 - 1;
|
||||
for (; ((a_ChunkInterface.GetBlock(X, Y, Z2) == E_BLOCK_OBSIDIAN) || (a_ChunkInterface.GetBlock(X, Y + 1, Z2) == E_BLOCK_OBSIDIAN)); Z2--)
|
||||
{
|
||||
int Value = FindObsidianCeiling(X, Y, Z2, a_ChunkInterface, MaxY);
|
||||
@ -213,13 +212,13 @@ public:
|
||||
if ((Value == -1) || (ValueTwo == -1))
|
||||
{
|
||||
FoundFrameZM = true;
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
else if ((Value != MaxY) && (ValueTwo != MaxY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} XZM = Z2 + 2;
|
||||
} XZM = Z2 + 1;
|
||||
return (FoundFrameZP && FoundFrameZM);
|
||||
}
|
||||
};
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "BlockDirt.h"
|
||||
#include "BlockDoor.h"
|
||||
#include "BlockDropSpenser.h"
|
||||
#include "BlockEnchantmentTable.h"
|
||||
#include "BlockEnderchest.h"
|
||||
#include "BlockEntity.h"
|
||||
#include "BlockFarmland.h"
|
||||
@ -119,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);
|
||||
|
@ -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; \
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../")
|
||||
|
||||
file(GLOB SOURCE
|
||||
"*.cpp"
|
||||
"*.h"
|
||||
)
|
||||
|
||||
add_library(Blocks ${SOURCE})
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
@ -171,7 +171,7 @@ cByteBuffer::~cByteBuffer()
|
||||
|
||||
|
||||
|
||||
bool cByteBuffer::Write(const char * a_Bytes, size_t a_Count)
|
||||
bool cByteBuffer::Write(const void * a_Bytes, size_t a_Count)
|
||||
{
|
||||
CHECK_THREAD;
|
||||
CheckValid();
|
||||
@ -187,13 +187,14 @@ bool cByteBuffer::Write(const char * a_Bytes, size_t a_Count)
|
||||
}
|
||||
ASSERT(m_BufferSize >= m_WritePos);
|
||||
size_t TillEnd = m_BufferSize - m_WritePos;
|
||||
const char * Bytes = (const char *)a_Bytes;
|
||||
if (TillEnd <= a_Count)
|
||||
{
|
||||
// Need to wrap around the ringbuffer end
|
||||
if (TillEnd > 0)
|
||||
{
|
||||
memcpy(m_Buffer + m_WritePos, a_Bytes, TillEnd);
|
||||
a_Bytes += TillEnd;
|
||||
memcpy(m_Buffer + m_WritePos, Bytes, TillEnd);
|
||||
Bytes += TillEnd;
|
||||
a_Count -= TillEnd;
|
||||
WrittenBytes = TillEnd;
|
||||
}
|
||||
@ -203,7 +204,7 @@ bool cByteBuffer::Write(const char * a_Bytes, size_t a_Count)
|
||||
// We're guaranteed that we'll fit in a single write op
|
||||
if (a_Count > 0)
|
||||
{
|
||||
memcpy(m_Buffer + m_WritePos, a_Bytes, a_Count);
|
||||
memcpy(m_Buffer + m_WritePos, Bytes, a_Count);
|
||||
m_WritePos += a_Count;
|
||||
WrittenBytes += a_Count;
|
||||
}
|
||||
@ -326,7 +327,7 @@ bool cByteBuffer::ReadBEShort(short & a_Value)
|
||||
CheckValid();
|
||||
NEEDBYTES(2);
|
||||
ReadBuf(&a_Value, 2);
|
||||
a_Value = ntohs(a_Value);
|
||||
a_Value = (short)ntohs((u_short)a_Value);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -340,7 +341,7 @@ bool cByteBuffer::ReadBEInt(int & a_Value)
|
||||
CheckValid();
|
||||
NEEDBYTES(4);
|
||||
ReadBuf(&a_Value, 4);
|
||||
a_Value = ntohl(a_Value);
|
||||
a_Value = (int)ntohl((u_long)a_Value);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -419,7 +420,7 @@ bool cByteBuffer::ReadBEUTF16String16(AString & a_Value)
|
||||
ASSERT(!"Negative string length? Are you sure?");
|
||||
return true;
|
||||
}
|
||||
return ReadUTF16String(a_Value, Length);
|
||||
return ReadUTF16String(a_Value, (size_t)Length);
|
||||
}
|
||||
|
||||
|
||||
@ -437,7 +438,7 @@ bool cByteBuffer::ReadVarInt(UInt32 & a_Value)
|
||||
{
|
||||
NEEDBYTES(1);
|
||||
ReadBuf(&b, 1);
|
||||
Value = Value | (((Int64)(b & 0x7f)) << Shift);
|
||||
Value = Value | (((UInt32)(b & 0x7f)) << Shift);
|
||||
Shift += 7;
|
||||
} while ((b & 0x80) != 0);
|
||||
a_Value = Value;
|
||||
@ -461,7 +462,7 @@ bool cByteBuffer::ReadVarUTF8String(AString & a_Value)
|
||||
{
|
||||
LOGWARNING("%s: String too large: %u (%u KiB)", __FUNCTION__, Size, Size / 1024);
|
||||
}
|
||||
return ReadString(a_Value, (int)Size);
|
||||
return ReadString(a_Value, (size_t)Size);
|
||||
}
|
||||
|
||||
|
||||
@ -516,7 +517,7 @@ bool cByteBuffer::WriteBEShort(short a_Value)
|
||||
CHECK_THREAD;
|
||||
CheckValid();
|
||||
PUTBYTES(2);
|
||||
short Converted = htons(a_Value);
|
||||
u_short Converted = htons((u_short)a_Value);
|
||||
return WriteBuf(&Converted, 2);
|
||||
}
|
||||
|
||||
@ -529,7 +530,7 @@ bool cByteBuffer::WriteBEInt(int a_Value)
|
||||
CHECK_THREAD;
|
||||
CheckValid();
|
||||
PUTBYTES(4);
|
||||
int Converted = HostToNetwork4(&a_Value);
|
||||
UInt32 Converted = HostToNetwork4(&a_Value);
|
||||
return WriteBuf(&Converted, 4);
|
||||
}
|
||||
|
||||
@ -542,7 +543,7 @@ bool cByteBuffer::WriteBEInt64(Int64 a_Value)
|
||||
CHECK_THREAD;
|
||||
CheckValid();
|
||||
PUTBYTES(8);
|
||||
Int64 Converted = HostToNetwork8(&a_Value);
|
||||
UInt64 Converted = HostToNetwork8(&a_Value);
|
||||
return WriteBuf(&Converted, 8);
|
||||
}
|
||||
|
||||
@ -555,7 +556,7 @@ bool cByteBuffer::WriteBEFloat(float a_Value)
|
||||
CHECK_THREAD;
|
||||
CheckValid();
|
||||
PUTBYTES(4);
|
||||
int Converted = HostToNetwork4(&a_Value);
|
||||
UInt32 Converted = HostToNetwork4(&a_Value);
|
||||
return WriteBuf(&Converted, 4);
|
||||
}
|
||||
|
||||
@ -568,7 +569,7 @@ bool cByteBuffer::WriteBEDouble(double a_Value)
|
||||
CHECK_THREAD;
|
||||
CheckValid();
|
||||
PUTBYTES(8);
|
||||
Int64 Converted = HostToNetwork8(&a_Value);
|
||||
UInt64 Converted = HostToNetwork8(&a_Value);
|
||||
return WriteBuf(&Converted, 8);
|
||||
}
|
||||
|
||||
@ -612,7 +613,7 @@ bool cByteBuffer::WriteVarInt(UInt32 a_Value)
|
||||
|
||||
// A 32-bit integer can be encoded by at most 5 bytes:
|
||||
unsigned char b[5];
|
||||
int idx = 0;
|
||||
size_t idx = 0;
|
||||
do
|
||||
{
|
||||
b[idx] = (a_Value & 0x7f) | ((a_Value > 0x7f) ? 0x80 : 0x00);
|
||||
@ -631,7 +632,7 @@ bool cByteBuffer::WriteVarUTF8String(const AString & a_Value)
|
||||
CHECK_THREAD;
|
||||
CheckValid();
|
||||
PUTBYTES(a_Value.size() + 1); // This is a lower-bound on the bytes that will be actually written. Fail early.
|
||||
bool res = WriteVarInt(a_Value.size());
|
||||
bool res = WriteVarInt((UInt32)(a_Value.size()));
|
||||
if (!res)
|
||||
{
|
||||
return false;
|
||||
@ -756,7 +757,7 @@ bool cByteBuffer::ReadString(AString & a_String, size_t a_Count)
|
||||
|
||||
|
||||
|
||||
bool cByteBuffer::ReadUTF16String(AString & a_String, int a_NumChars)
|
||||
bool cByteBuffer::ReadUTF16String(AString & a_String, size_t a_NumChars)
|
||||
{
|
||||
// Reads 2 * a_NumChars bytes and interprets it as a UTF16 string, converting it into UTF8 string a_String
|
||||
CHECK_THREAD;
|
||||
@ -887,9 +888,7 @@ void cByteBuffer::AdvanceReadPos(size_t a_Count)
|
||||
|
||||
void cByteBuffer::CheckValid(void) const
|
||||
{
|
||||
ASSERT(m_ReadPos >= 0);
|
||||
ASSERT(m_ReadPos < m_BufferSize);
|
||||
ASSERT(m_WritePos >= 0);
|
||||
ASSERT(m_WritePos < m_BufferSize);
|
||||
}
|
||||
|
||||
|
@ -27,11 +27,11 @@ 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
|
||||
bool Write(const char * a_Bytes, size_t a_Count);
|
||||
bool Write(const void * a_Bytes, size_t a_Count);
|
||||
|
||||
/// Returns the number of bytes that can be successfully written to the ringbuffer
|
||||
size_t GetFreeSpace(void) const;
|
||||
@ -101,7 +101,7 @@ public:
|
||||
bool ReadString(AString & a_String, size_t a_Count);
|
||||
|
||||
/// Reads 2 * a_NumChars bytes and interprets it as a UTF16-BE string, converting it into UTF8 string a_String
|
||||
bool ReadUTF16String(AString & a_String, int a_NumChars);
|
||||
bool ReadUTF16String(AString & a_String, size_t a_NumChars);
|
||||
|
||||
/// Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer
|
||||
bool SkipRead(size_t a_Count);
|
||||
|
@ -5,7 +5,7 @@ include_directories (SYSTEM "${PROJECT_SOURCE_DIR}/../lib/")
|
||||
include_directories (SYSTEM "${PROJECT_SOURCE_DIR}/../lib/jsoncpp/include")
|
||||
include_directories (SYSTEM "${PROJECT_SOURCE_DIR}/../lib/polarssl/include")
|
||||
|
||||
set(FOLDERS OSSupport HTTPServer Items Blocks Protocol Generating)
|
||||
set(FOLDERS OSSupport HTTPServer Items Blocks Protocol Generating PolarSSL++)
|
||||
set(FOLDERS ${FOLDERS} WorldStorage Mobs Entities Simulator UI BlockEntities Generating/Prefabs)
|
||||
|
||||
|
||||
@ -57,6 +57,14 @@ if (NOT MSVC)
|
||||
Entities/Pickup.h
|
||||
Entities/Player.h
|
||||
Entities/ProjectileEntity.h
|
||||
Entities/ArrowEntity.h
|
||||
Entities/ThrownEggEntity.h
|
||||
Entities/ThrownEnderPearlEntity.h
|
||||
Entities/ExpBottleEntity.h
|
||||
Entities/ThrownSnowballEntity.h
|
||||
Entities/FireChargeEntity.h
|
||||
Entities/FireworkEntity.h
|
||||
Entities/GhastFireballEntity.h
|
||||
Entities/TNTEntity.h
|
||||
Entities/ExpOrb.h
|
||||
Entities/HangingEntity.h
|
||||
@ -123,6 +131,7 @@ if (NOT MSVC)
|
||||
|
||||
file(GLOB SOURCE
|
||||
"*.cpp"
|
||||
"*.h"
|
||||
)
|
||||
|
||||
list(REMOVE_ITEM SOURCE "${PROJECT_SOURCE_DIR}/StackWalker.cpp" "${PROJECT_SOURCE_DIR}/LeakFinder.cpp")
|
||||
@ -233,7 +242,7 @@ endif ()
|
||||
if (NOT MSVC)
|
||||
target_link_libraries(${EXECUTABLE} OSSupport HTTPServer Bindings Items Blocks)
|
||||
target_link_libraries(${EXECUTABLE} Protocol Generating Generating_Prefabs WorldStorage)
|
||||
target_link_libraries(${EXECUTABLE} Mobs Entities Simulator UI BlockEntities)
|
||||
target_link_libraries(${EXECUTABLE} Mobs Entities Simulator UI BlockEntities PolarSSL++)
|
||||
endif ()
|
||||
if (WIN32)
|
||||
target_link_libraries(${EXECUTABLE} expat tolualib ws2_32.lib Psapi.lib)
|
||||
|
158
src/Chunk.cpp
158
src/Chunk.cpp
@ -240,11 +240,24 @@ void cChunk::GetAllData(cChunkDataCallback & a_Callback)
|
||||
{
|
||||
a_Callback.HeightMap (&m_HeightMap);
|
||||
a_Callback.BiomeData (&m_BiomeMap);
|
||||
a_Callback.BlockTypes (m_BlockTypes);
|
||||
a_Callback.BlockMeta (m_BlockMeta);
|
||||
|
||||
COMPRESSED_BLOCKTYPE Blocks = m_BlockTypes;
|
||||
Blocks.resize(NumBlocks);
|
||||
a_Callback.BlockTypes (&Blocks[0]);
|
||||
|
||||
COMPRESSED_NIBBLETYPE Metas = m_BlockMeta;
|
||||
Metas.resize(NumBlocks / 2);
|
||||
a_Callback.BlockMeta (&Metas[0]);
|
||||
|
||||
a_Callback.LightIsValid (m_IsLightValid);
|
||||
a_Callback.BlockLight (m_BlockLight);
|
||||
a_Callback.BlockSkyLight(m_BlockSkyLight);
|
||||
|
||||
COMPRESSED_NIBBLETYPE BlockLights = m_BlockLight;
|
||||
BlockLights.resize(NumBlocks / 2);
|
||||
a_Callback.BlockLight (&BlockLights[0]);
|
||||
|
||||
COMPRESSED_NIBBLETYPE BlockSkyLights = m_BlockSkyLight;
|
||||
BlockSkyLights.resize(NumBlocks / 2, 0xff);
|
||||
a_Callback.BlockSkyLight(&BlockSkyLights[0]);
|
||||
|
||||
for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr)
|
||||
{
|
||||
@ -262,7 +275,7 @@ void cChunk::GetAllData(cChunkDataCallback & a_Callback)
|
||||
|
||||
|
||||
void cChunk::SetAllData(
|
||||
const BLOCKTYPE * a_BlockTypes,
|
||||
const BLOCKTYPE * a_BlockTypes,
|
||||
const NIBBLETYPE * a_BlockMeta,
|
||||
const NIBBLETYPE * a_BlockLight,
|
||||
const NIBBLETYPE * a_BlockSkyLight,
|
||||
@ -272,29 +285,61 @@ void cChunk::SetAllData(
|
||||
)
|
||||
{
|
||||
memcpy(m_BiomeMap, a_BiomeMap, sizeof(m_BiomeMap));
|
||||
|
||||
|
||||
if (a_HeightMap != NULL)
|
||||
{
|
||||
memcpy(m_HeightMap, a_HeightMap, sizeof(m_HeightMap));
|
||||
}
|
||||
|
||||
if (a_HeightMap == NULL)
|
||||
{
|
||||
CalculateHeightmap(a_BlockTypes);
|
||||
}
|
||||
|
||||
int IdxWhereNonEmptyStarts = 0;
|
||||
{ // Blocktype compression
|
||||
unsigned char Highest = 0;
|
||||
int X = 0, Z = 0;
|
||||
m_BlockTypes.clear();
|
||||
|
||||
for (int x = 0; x < Width; x++)
|
||||
{
|
||||
for (int z = 0; z < Width; z++)
|
||||
{
|
||||
unsigned char Height = m_HeightMap[x + z * Width];
|
||||
if (Height > Highest)
|
||||
{
|
||||
Highest = Height;
|
||||
X = x; Z = z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IdxWhereNonEmptyStarts = MakeIndexNoCheck(X, Highest + 1, Z);
|
||||
|
||||
m_BlockTypes.insert(m_BlockTypes.end(), &a_BlockTypes[0], &a_BlockTypes[IdxWhereNonEmptyStarts]);
|
||||
}
|
||||
|
||||
{ // Blockmeta compression
|
||||
m_BlockMeta.clear();
|
||||
m_BlockMeta.insert(m_BlockMeta.end(), &a_BlockMeta[0], &a_BlockMeta[IdxWhereNonEmptyStarts / 2]);
|
||||
}
|
||||
|
||||
memcpy(m_BlockTypes, a_BlockTypes, sizeof(m_BlockTypes));
|
||||
memcpy(m_BlockMeta, a_BlockMeta, sizeof(m_BlockMeta));
|
||||
if (a_BlockLight != NULL)
|
||||
{
|
||||
memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight));
|
||||
// Compress blocklight
|
||||
m_BlockLight.clear();
|
||||
m_BlockLight.insert(m_BlockLight.end(), &a_BlockLight[0], &a_BlockLight[IdxWhereNonEmptyStarts / 2]);
|
||||
}
|
||||
|
||||
if (a_BlockSkyLight != NULL)
|
||||
{
|
||||
memcpy(m_BlockSkyLight, a_BlockSkyLight, sizeof(m_BlockSkyLight));
|
||||
// Compress skylight
|
||||
m_BlockSkyLight.clear();
|
||||
m_BlockSkyLight.insert(m_BlockSkyLight.end(), &a_BlockSkyLight[0], &a_BlockSkyLight[IdxWhereNonEmptyStarts / 2]);
|
||||
}
|
||||
|
||||
m_IsLightValid = (a_BlockLight != NULL) && (a_BlockSkyLight != NULL);
|
||||
|
||||
if (a_HeightMap == NULL)
|
||||
{
|
||||
CalculateHeightmap();
|
||||
}
|
||||
|
||||
// Clear the block entities present - either the loader / saver has better, or we'll create empty ones:
|
||||
for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
|
||||
@ -332,8 +377,17 @@ void cChunk::SetLight(
|
||||
{
|
||||
// TODO: We might get cases of wrong lighting when a chunk changes in the middle of a lighting calculation.
|
||||
// Postponing until we see how bad it is :)
|
||||
memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight));
|
||||
memcpy(m_BlockSkyLight, a_SkyLight, sizeof(m_BlockSkyLight));
|
||||
|
||||
{ // Compress blocklight
|
||||
m_BlockLight.clear();
|
||||
m_BlockLight.insert(m_BlockLight.end(), &a_BlockLight[0], &a_BlockLight[m_BlockTypes.size() / 2]);
|
||||
}
|
||||
|
||||
{ // Compress skylight
|
||||
m_BlockSkyLight.clear();
|
||||
m_BlockSkyLight.insert(m_BlockSkyLight.end(), &a_SkyLight[0], &a_SkyLight[m_BlockTypes.size() / 2]);
|
||||
}
|
||||
|
||||
m_IsLightValid = true;
|
||||
}
|
||||
|
||||
@ -343,7 +397,8 @@ void cChunk::SetLight(
|
||||
|
||||
void cChunk::GetBlockTypes(BLOCKTYPE * a_BlockTypes)
|
||||
{
|
||||
memcpy(a_BlockTypes, m_BlockTypes, NumBlocks);
|
||||
std::copy(m_BlockTypes.begin(), m_BlockTypes.end(), a_BlockTypes);
|
||||
std::fill_n(&a_BlockTypes[m_BlockTypes.size()], NumBlocks - m_BlockTypes.size(), E_BLOCK_AIR);
|
||||
}
|
||||
|
||||
|
||||
@ -452,7 +507,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());
|
||||
}
|
||||
@ -600,7 +655,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:
|
||||
@ -630,7 +685,7 @@ void cChunk::Tick(float a_Dt)
|
||||
void cChunk::TickBlock(int a_RelX, int a_RelY, int a_RelZ)
|
||||
{
|
||||
unsigned Index = MakeIndex(a_RelX, a_RelY, a_RelZ);
|
||||
cBlockHandler * Handler = BlockHandler(m_BlockTypes[Index]);
|
||||
cBlockHandler * Handler = BlockHandler(GetBlock(Index));
|
||||
ASSERT(Handler != NULL); // Happenned on server restart, FS #243
|
||||
cChunkInterface ChunkInterface(this->GetWorld()->GetChunkMap());
|
||||
cBlockInServerPluginInterface PluginInterface(*this->GetWorld());
|
||||
@ -694,7 +749,7 @@ void cChunk::ProcessQueuedSetBlocks(void)
|
||||
{
|
||||
if (itr->m_Tick <= CurrTick)
|
||||
{
|
||||
if (itr->m_PreviousType != E_BLOCK_AIR) // PreviousType defaults to -1 if not specified
|
||||
if (itr->m_PreviousType != E_BLOCK_AIR) // PreviousType defaults to 0 if not specified
|
||||
{
|
||||
if (GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ) == itr->m_PreviousType)
|
||||
{
|
||||
@ -751,7 +806,7 @@ void cChunk::BroadcastPendingBlockChanges(void)
|
||||
|
||||
void cChunk::CheckBlocks()
|
||||
{
|
||||
if (m_ToTickBlocks.size() == 0)
|
||||
if (m_ToTickBlocks.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -811,7 +866,7 @@ void cChunk::TickBlocks(void)
|
||||
}
|
||||
|
||||
unsigned int Index = MakeIndexNoCheck(m_BlockTickX, m_BlockTickY, m_BlockTickZ);
|
||||
cBlockHandler * Handler = BlockHandler(m_BlockTypes[Index]);
|
||||
cBlockHandler * Handler = BlockHandler(GetBlock(Index));
|
||||
ASSERT(Handler != NULL); // Happenned on server restart, FS #243
|
||||
Handler->OnUpdate(ChunkInterface, *this->GetWorld(), PluginInterface, *this, m_BlockTickX, m_BlockTickY, m_BlockTickZ);
|
||||
} // for i - tickblocks
|
||||
@ -1296,9 +1351,10 @@ void cChunk::CreateBlockEntities(void)
|
||||
{
|
||||
for (int y = 0; y < Height; y++)
|
||||
{
|
||||
BLOCKTYPE BlockType = cChunkDef::GetBlock(m_BlockTypes, x, y, z);
|
||||
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:
|
||||
@ -1348,7 +1404,7 @@ void cChunk::WakeUpSimulators(void)
|
||||
int BlockZ = z + BaseZ;
|
||||
for (int y = GetHeight(x, z); y >= 0; y--)
|
||||
{
|
||||
BLOCKTYPE Block = cChunkDef::GetBlock(m_BlockTypes, x, y, z);
|
||||
BLOCKTYPE Block = GetBlock(x, y, z);
|
||||
|
||||
// The redstone sim takes multiple blocks, use the inbuilt checker
|
||||
if (RedstoneSimulator->IsAllowedBlock(Block))
|
||||
@ -1383,7 +1439,7 @@ void cChunk::WakeUpSimulators(void)
|
||||
|
||||
|
||||
|
||||
void cChunk::CalculateHeightmap()
|
||||
void cChunk::CalculateHeightmap(const BLOCKTYPE * a_BlockTypes)
|
||||
{
|
||||
for (int x = 0; x < Width; x++)
|
||||
{
|
||||
@ -1392,7 +1448,7 @@ void cChunk::CalculateHeightmap()
|
||||
for (int y = Height - 1; y > -1; y--)
|
||||
{
|
||||
int index = MakeIndex( x, y, z );
|
||||
if (m_BlockTypes[index] != E_BLOCK_AIR)
|
||||
if (a_BlockTypes[index] != E_BLOCK_AIR)
|
||||
{
|
||||
m_HeightMap[x + z * Width] = (unsigned char)y;
|
||||
break;
|
||||
@ -1429,6 +1485,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:
|
||||
@ -1515,7 +1572,7 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
|
||||
ASSERT(IsValid());
|
||||
|
||||
const int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
|
||||
const BLOCKTYPE OldBlockType = cChunkDef::GetBlock(m_BlockTypes, index);
|
||||
const BLOCKTYPE OldBlockType = GetBlock(index);
|
||||
const BLOCKTYPE OldBlockMeta = GetNibble(m_BlockMeta, index);
|
||||
if ((OldBlockType == a_BlockType) && (OldBlockMeta == a_BlockMeta))
|
||||
{
|
||||
@ -1523,7 +1580,11 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
|
||||
}
|
||||
|
||||
MarkDirty();
|
||||
|
||||
|
||||
if ((size_t)index >= m_BlockTypes.size())
|
||||
{
|
||||
m_BlockTypes.resize(index + 1);
|
||||
}
|
||||
m_BlockTypes[index] = a_BlockType;
|
||||
|
||||
// The client doesn't need to distinguish between stationary and nonstationary fluids:
|
||||
@ -1563,7 +1624,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 (m_BlockTypes[MakeIndexNoCheck(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;
|
||||
@ -1577,6 +1638,24 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
|
||||
|
||||
|
||||
|
||||
void cChunk::SetMeta(int a_BlockIdx, NIBBLETYPE a_Meta)
|
||||
{
|
||||
if (GetNibble(m_BlockMeta, a_BlockIdx) == a_Meta)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MarkDirty();
|
||||
SetNibble(m_BlockMeta, a_BlockIdx, a_Meta);
|
||||
Vector3i Coords(IndexToCoordinate(a_BlockIdx));
|
||||
|
||||
m_PendingSendBlocks.push_back(sSetBlock(m_PosX, m_PosZ, Coords.x, Coords.y, Coords.z, GetBlock(a_BlockIdx), a_Meta));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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.
|
||||
@ -2450,7 +2529,7 @@ BLOCKTYPE cChunk::GetBlock(int a_RelX, int a_RelY, int a_RelZ) const
|
||||
return 0; // Clip
|
||||
}
|
||||
|
||||
return m_BlockTypes[MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ)];
|
||||
return GetBlock(MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ));
|
||||
}
|
||||
|
||||
|
||||
@ -2464,8 +2543,13 @@ BLOCKTYPE cChunk::GetBlock(int a_BlockIdx) const
|
||||
ASSERT(!"GetBlock(idx) out of bounds!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_BlockTypes[ a_BlockIdx ];
|
||||
|
||||
if ((size_t)a_BlockIdx >= m_BlockTypes.size())
|
||||
{
|
||||
return E_BLOCK_AIR;
|
||||
}
|
||||
|
||||
return m_BlockTypes[a_BlockIdx];
|
||||
}
|
||||
|
||||
|
||||
@ -2475,7 +2559,7 @@ BLOCKTYPE cChunk::GetBlock(int a_BlockIdx) const
|
||||
void cChunk::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta)
|
||||
{
|
||||
int Idx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
|
||||
a_BlockType = cChunkDef::GetBlock (m_BlockTypes, Idx);
|
||||
a_BlockType = GetBlock(Idx);
|
||||
a_BlockMeta = cChunkDef::GetNibble(m_BlockMeta, Idx);
|
||||
}
|
||||
|
||||
@ -2486,7 +2570,7 @@ 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)
|
||||
{
|
||||
int Idx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
|
||||
a_BlockType = cChunkDef::GetBlock (m_BlockTypes, Idx);
|
||||
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);
|
||||
@ -2511,7 +2595,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;
|
||||
|
27
src/Chunk.h
27
src/Chunk.h
@ -267,7 +267,7 @@ public:
|
||||
void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z); // [x, y, z] in world block coords
|
||||
|
||||
void CalculateLighting(); // Recalculate right now
|
||||
void CalculateHeightmap();
|
||||
void CalculateHeightmap(const BLOCKTYPE * a_BlockTypes);
|
||||
|
||||
// Broadcast various packets to all clients of this chunk:
|
||||
// (Please keep these alpha-sorted)
|
||||
@ -320,15 +320,15 @@ public:
|
||||
m_BlockTickZ = a_RelZ;
|
||||
}
|
||||
|
||||
inline NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ); }
|
||||
inline NIBBLETYPE GetMeta(int a_BlockIdx) const {return cChunkDef::GetNibble(m_BlockMeta, a_BlockIdx); }
|
||||
inline void SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Meta) { cChunkDef::SetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ, a_Meta); }
|
||||
inline void SetMeta(int a_BlockIdx, NIBBLETYPE a_Meta) { cChunkDef::SetNibble(m_BlockMeta, a_BlockIdx, a_Meta); }
|
||||
inline NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const { return cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ); }
|
||||
inline NIBBLETYPE GetMeta(int a_BlockIdx) const { return cChunkDef::GetNibble(m_BlockMeta, a_BlockIdx); }
|
||||
inline void SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Meta) { SetMeta(MakeIndex(a_RelX, a_RelY, a_RelZ), a_Meta); }
|
||||
void SetMeta(int a_BlockIdx, NIBBLETYPE a_Meta);
|
||||
|
||||
inline NIBBLETYPE GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockLight, a_RelX, a_RelY, a_RelZ); }
|
||||
inline NIBBLETYPE GetSkyLight (int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_RelX, a_RelY, a_RelZ); }
|
||||
inline NIBBLETYPE GetSkyLight (int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_RelX, a_RelY, a_RelZ, true); }
|
||||
inline NIBBLETYPE GetBlockLight(int a_Idx) const {return cChunkDef::GetNibble(m_BlockLight, a_Idx); }
|
||||
inline NIBBLETYPE GetSkyLight (int a_Idx) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_Idx); }
|
||||
inline NIBBLETYPE GetSkyLight (int a_Idx) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_Idx, true); }
|
||||
|
||||
/** Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success */
|
||||
bool UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const;
|
||||
@ -382,14 +382,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)
|
||||
{
|
||||
}
|
||||
} ;
|
||||
@ -420,11 +420,10 @@ private:
|
||||
cWorld * m_World;
|
||||
cChunkMap * m_ChunkMap;
|
||||
|
||||
// TODO: Make these pointers and don't allocate what isn't needed
|
||||
BLOCKTYPE m_BlockTypes [cChunkDef::NumBlocks];
|
||||
NIBBLETYPE m_BlockMeta [cChunkDef::NumBlocks / 2];
|
||||
NIBBLETYPE m_BlockLight [cChunkDef::NumBlocks / 2];
|
||||
NIBBLETYPE m_BlockSkyLight[cChunkDef::NumBlocks / 2];
|
||||
COMPRESSED_BLOCKTYPE m_BlockTypes;
|
||||
COMPRESSED_NIBBLETYPE m_BlockMeta;
|
||||
COMPRESSED_NIBBLETYPE m_BlockLight;
|
||||
COMPRESSED_NIBBLETYPE m_BlockSkyLight;
|
||||
|
||||
cChunkDef::HeightMap m_HeightMap;
|
||||
cChunkDef::BiomeMap m_BiomeMap;
|
||||
|
@ -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,46 +225,67 @@ 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;
|
||||
if ((size_t)(a_BlockIdx / 2) >= a_Buffer.size())
|
||||
{
|
||||
return (a_IsSkyLightNibble ? 0xff : 0);
|
||||
}
|
||||
return (a_Buffer[(size_t)(a_BlockIdx / 2)] >> ((a_BlockIdx & 1) * 4)) & 0x0f;
|
||||
}
|
||||
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;
|
||||
size_t Index = (size_t)MakeIndexNoCheck(x, y, z);
|
||||
if ((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 void SetNibble(NIBBLETYPE * a_Buffer, int a_BlockIdx, NIBBLETYPE a_Nibble)
|
||||
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);
|
||||
return (a_Buffer[(size_t)(Index / 2)] >> ((Index & 1) * 4)) & 0x0f;
|
||||
}
|
||||
ASSERT(!"cChunkDef::GetNibble(): coords out of chunk range!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
);
|
||||
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)] = PackNibble(a_Buffer, (size_t)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) ||
|
||||
@ -270,25 +297,33 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
size_t Index = (size_t)MakeIndexNoCheck(x, y, z);
|
||||
if ((Index / 2) >= a_Buffer.size())
|
||||
{
|
||||
a_Buffer.resize(((Index / 2) + 1));
|
||||
}
|
||||
a_Buffer[(Index / 2)] = PackNibble(a_Buffer, Index, a_Nibble);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
||||
inline static NIBBLETYPE PackNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, size_t 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
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
inline static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos )
|
||||
inline static NIBBLETYPE ExpandNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, size_t a_Index)
|
||||
{
|
||||
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 );
|
||||
return (a_Buffer[a_Index / 2] >> ((a_Index & 1) * 4)) & 0x0f;
|
||||
}
|
||||
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -346,9 +346,8 @@ void cChunkMap::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity *
|
||||
void cChunkMap::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude)
|
||||
{
|
||||
cCSLock Lock(m_CSLayers);
|
||||
int x, y, z, ChunkX, ChunkZ;
|
||||
int x, z, ChunkX, ChunkZ;
|
||||
x = a_BlockX;
|
||||
y = a_BlockY;
|
||||
z = a_BlockZ;
|
||||
cChunkDef::BlockToChunk(x, z, ChunkX, ChunkZ);
|
||||
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
|
||||
@ -916,19 +915,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
|
||||
@ -1144,9 +1145,8 @@ BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
// First check if it isn't queued in the m_FastSetBlockQueue:
|
||||
{
|
||||
int X = a_BlockX, Y = a_BlockY, Z = a_BlockZ;
|
||||
int ChunkX, ChunkY, ChunkZ;
|
||||
int ChunkX, ChunkZ;
|
||||
cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ);
|
||||
ChunkY = 0;
|
||||
cCSLock Lock(m_CSFastSetBlock);
|
||||
for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr)
|
||||
{
|
||||
@ -1248,8 +1248,6 @@ void cChunkMap::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYP
|
||||
if ((Chunk != NULL) && Chunk->IsValid())
|
||||
{
|
||||
Chunk->SetMeta(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta);
|
||||
Chunk->MarkDirty();
|
||||
Chunk->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1654,7 +1652,10 @@ 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 not present at all
|
||||
(!Chunk->IsValid() && !a_Entity->IsPlayer()) // Chunk present, but no valid data; players need to spawn in such chunks (#953)
|
||||
)
|
||||
{
|
||||
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()
|
||||
@ -1689,7 +1690,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;
|
||||
}
|
||||
@ -1721,7 +1722,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;
|
||||
}
|
||||
@ -1971,7 +1972,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;
|
||||
}
|
||||
@ -1986,7 +1987,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;
|
||||
}
|
||||
@ -2001,7 +2002,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;
|
||||
}
|
||||
@ -2016,7 +2017,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;
|
||||
}
|
||||
@ -2031,7 +2032,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;
|
||||
}
|
||||
@ -2046,7 +2047,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;
|
||||
}
|
||||
@ -2064,7 +2065,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;
|
||||
}
|
||||
@ -2082,7 +2083,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;
|
||||
}
|
||||
@ -2100,7 +2101,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;
|
||||
}
|
||||
@ -2118,7 +2119,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;
|
||||
}
|
||||
@ -2136,7 +2137,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;
|
||||
}
|
||||
@ -2154,7 +2155,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;
|
||||
}
|
||||
@ -2171,7 +2172,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;
|
||||
}
|
||||
@ -2188,7 +2189,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;
|
||||
}
|
||||
@ -2206,7 +2207,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;
|
||||
}
|
||||
@ -2224,7 +2225,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;
|
||||
}
|
||||
@ -2242,7 +2243,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;
|
||||
}
|
||||
@ -2974,7 +2975,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[]
|
||||
}
|
||||
@ -3017,6 +3023,7 @@ void cChunkMap::DelChunkStay(cChunkStay & a_ChunkStay)
|
||||
}
|
||||
Chunk->Stay(false);
|
||||
} // for itr - Chunks[]
|
||||
a_ChunkStay.OnDisabled();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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,84 @@ void cClientHandle::Destroy(void)
|
||||
|
||||
|
||||
|
||||
void cClientHandle::GenerateOfflineUUID(void)
|
||||
{
|
||||
m_UUID = GenerateOfflineUUID(m_Username);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AString cClientHandle::FormatChatPrefix(bool ShouldAppendChatPrefixes, AString a_ChatPrefixS, AString m_Color1, AString m_Color2)
|
||||
{
|
||||
if (ShouldAppendChatPrefixes)
|
||||
return Printf("%s[%s] %s", m_Color1.c_str(), a_ChatPrefixS.c_str(), m_Color2.c_str());
|
||||
else
|
||||
return Printf("%s", m_Color1.c_str());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AString cClientHandle::FormatMessageType(bool ShouldAppendChatPrefixes, eMessageType a_ChatPrefix, const AString &a_AdditionalData)
|
||||
{
|
||||
switch (a_ChatPrefix)
|
||||
{
|
||||
case mtCustom: return "";
|
||||
case mtFailure: return FormatChatPrefix(ShouldAppendChatPrefixes, "INFO", cChatColor::Rose, cChatColor::White);
|
||||
case mtInformation: return FormatChatPrefix(ShouldAppendChatPrefixes, "INFO", cChatColor::Yellow, cChatColor::White);
|
||||
case mtSuccess: return FormatChatPrefix(ShouldAppendChatPrefixes, "INFO", cChatColor::Green, cChatColor::White);
|
||||
case mtWarning: return FormatChatPrefix(ShouldAppendChatPrefixes, "WARN", cChatColor::Rose, cChatColor::White);
|
||||
case mtFatal: return FormatChatPrefix(ShouldAppendChatPrefixes, "FATAL", cChatColor::Red, cChatColor::White);
|
||||
case mtDeath: return FormatChatPrefix(ShouldAppendChatPrefixes, "DEATH", cChatColor::Gray, cChatColor::White);
|
||||
case mtJoin: return FormatChatPrefix(ShouldAppendChatPrefixes, "JOIN", cChatColor::Yellow, cChatColor::White);
|
||||
case mtLeave: return FormatChatPrefix(ShouldAppendChatPrefixes, "LEAVE", cChatColor::Yellow, cChatColor::White);
|
||||
case mtPrivateMessage:
|
||||
{
|
||||
if (ShouldAppendChatPrefixes)
|
||||
{
|
||||
return Printf("%s[MSG: %s] %s%s", cChatColor::LightBlue.c_str(), a_AdditionalData.c_str(), cChatColor::White.c_str(), cChatColor::Italic.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
return Printf("%s: %s", a_AdditionalData.c_str(), cChatColor::LightBlue.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
ASSERT(!"Unhandled chat prefix type!");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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 +268,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 +277,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());
|
||||
|
||||
@ -250,6 +336,11 @@ void cClientHandle::Authenticate(void)
|
||||
|
||||
// Send scoreboard data
|
||||
World->GetScoreBoard().SendTo(*this);
|
||||
|
||||
// Delay the first ping until the client "settles down"
|
||||
// This should fix #889, "BadCast exception, cannot convert bit to fm" error in client
|
||||
cTimer t1;
|
||||
m_LastPingTime = t1.GetNowTime() + 3000; // Send the first KeepAlive packet in 3 seconds
|
||||
|
||||
cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player);
|
||||
}
|
||||
@ -412,14 +503,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);
|
||||
}
|
||||
|
||||
|
||||
@ -540,6 +633,10 @@ void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString
|
||||
// Client <-> Server branding exchange
|
||||
SendPluginMessage("MC|Brand", "MCServer");
|
||||
}
|
||||
else if (a_Channel == "MC|ItemName")
|
||||
{
|
||||
HandleAnvilItemName(a_Message.c_str(), a_Message.size());
|
||||
}
|
||||
else if (a_Channel == "REGISTER")
|
||||
{
|
||||
if (HasPluginChannel(a_Channel))
|
||||
@ -623,7 +720,7 @@ void cClientHandle::UnregisterPluginChannels(const AStringVector & a_ChannelList
|
||||
|
||||
|
||||
|
||||
void cClientHandle::HandleCommandBlockMessage(const char * a_Data, unsigned int a_Length)
|
||||
void cClientHandle::HandleCommandBlockMessage(const char * a_Data, size_t a_Length)
|
||||
{
|
||||
if (a_Length < 14)
|
||||
{
|
||||
@ -681,6 +778,29 @@ void cClientHandle::HandleCommandBlockMessage(const char * a_Data, unsigned int
|
||||
|
||||
|
||||
|
||||
void cClientHandle::HandleAnvilItemName(const char * a_Data, size_t a_Length)
|
||||
{
|
||||
if (a_Length < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((m_Player->GetWindow() == NULL) || (m_Player->GetWindow()->GetWindowType() != cWindow::wtAnvil))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AString Name(a_Data, a_Length);
|
||||
if (Name.length() <= 30)
|
||||
{
|
||||
((cAnvilWindow *)m_Player->GetWindow())->SetRepairedItemName(Name, m_Player);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, char a_Status)
|
||||
{
|
||||
LOGD("HandleLeftClick: {%i, %i, %i}; Face: %i; Stat: %i",
|
||||
@ -994,7 +1114,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 +1295,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);
|
||||
@ -1417,7 +1537,7 @@ void cClientHandle::HandleDisconnect(const AString & a_Reason)
|
||||
{
|
||||
LOGD("Received d/c packet from %s with reason \"%s\"", m_Username.c_str(), a_Reason.c_str());
|
||||
|
||||
cRoot::Get()->GetPluginManager()->CallHookDisconnect(m_Player, a_Reason);
|
||||
cRoot::Get()->GetPluginManager()->CallHookDisconnect(*this, a_Reason);
|
||||
|
||||
m_HasSentDC = true;
|
||||
Destroy();
|
||||
@ -1565,7 +1685,7 @@ void cClientHandle::SendData(const char * a_Data, size_t a_Size)
|
||||
{
|
||||
// There is a queued overflow. Append to it, then send as much from its front as possible
|
||||
m_OutgoingDataOverflow.append(a_Data, a_Size);
|
||||
int CanFit = m_OutgoingData.GetFreeSpace();
|
||||
size_t CanFit = m_OutgoingData.GetFreeSpace();
|
||||
if (CanFit > 128)
|
||||
{
|
||||
// No point in moving the data over if it's not large enough - too much effort for too little an effect
|
||||
@ -1677,13 +1797,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:
|
||||
@ -1803,7 +1926,7 @@ void cClientHandle::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlock
|
||||
void cClientHandle::SendChat(const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData)
|
||||
{
|
||||
bool ShouldAppendChatPrefixes = true;
|
||||
|
||||
|
||||
if (GetPlayer()->GetWorld() == NULL)
|
||||
{
|
||||
cWorld * World = cRoot::Get()->GetWorld(GetPlayer()->GetLoadedWorldName());
|
||||
@ -1822,89 +1945,9 @@ void cClientHandle::SendChat(const AString & a_Message, eMessageType a_ChatPrefi
|
||||
ShouldAppendChatPrefixes = false;
|
||||
}
|
||||
|
||||
AString Message;
|
||||
AString Message = FormatMessageType(ShouldAppendChatPrefixes, a_ChatPrefix, a_AdditionalData);
|
||||
|
||||
switch (a_ChatPrefix)
|
||||
{
|
||||
case mtCustom: break;
|
||||
case mtFailure:
|
||||
{
|
||||
if (ShouldAppendChatPrefixes)
|
||||
Message = Printf("%s[INFO] %s", cChatColor::Rose.c_str(), cChatColor::White.c_str());
|
||||
else
|
||||
Message = Printf("%s", cChatColor::Rose.c_str());
|
||||
break;
|
||||
}
|
||||
case mtInformation:
|
||||
{
|
||||
if (ShouldAppendChatPrefixes)
|
||||
Message = Printf("%s[INFO] %s", cChatColor::Yellow.c_str(), cChatColor::White.c_str());
|
||||
else
|
||||
Message = Printf("%s", cChatColor::Yellow.c_str());
|
||||
break;
|
||||
}
|
||||
case mtSuccess:
|
||||
{
|
||||
if (ShouldAppendChatPrefixes)
|
||||
Message = Printf("%s[INFO] %s", cChatColor::Green.c_str(), cChatColor::White.c_str());
|
||||
else
|
||||
Message = Printf("%s", cChatColor::Green.c_str());
|
||||
break;
|
||||
}
|
||||
case mtWarning:
|
||||
{
|
||||
if (ShouldAppendChatPrefixes)
|
||||
Message = Printf("%s[WARN] %s", cChatColor::Rose.c_str(), cChatColor::White.c_str());
|
||||
else
|
||||
Message = Printf("%s", cChatColor::Rose.c_str());
|
||||
break;
|
||||
}
|
||||
case mtFatal:
|
||||
{
|
||||
if (ShouldAppendChatPrefixes)
|
||||
Message = Printf("%s[FATAL] %s", cChatColor::Red.c_str(), cChatColor::White.c_str());
|
||||
else
|
||||
Message = Printf("%s", cChatColor::Red.c_str());
|
||||
break;
|
||||
}
|
||||
case mtDeath:
|
||||
{
|
||||
if (ShouldAppendChatPrefixes)
|
||||
Message = Printf("%s[DEATH] %s", cChatColor::Gray.c_str(), cChatColor::White.c_str());
|
||||
else
|
||||
Message = Printf("%s", cChatColor::Gray.c_str());
|
||||
break;
|
||||
}
|
||||
case mtPrivateMessage:
|
||||
{
|
||||
if (ShouldAppendChatPrefixes)
|
||||
Message = Printf("%s[MSG: %s] %s%s", cChatColor::LightBlue.c_str(), a_AdditionalData.c_str(), cChatColor::White.c_str(), cChatColor::Italic.c_str());
|
||||
else
|
||||
Message = Printf("%s: %s", a_AdditionalData.c_str(), cChatColor::LightBlue.c_str());
|
||||
break;
|
||||
}
|
||||
case mtJoin:
|
||||
{
|
||||
if (ShouldAppendChatPrefixes)
|
||||
Message = Printf("%s[JOIN] %s", cChatColor::Yellow.c_str(), cChatColor::White.c_str());
|
||||
else
|
||||
Message = Printf("%s", cChatColor::Yellow.c_str());
|
||||
break;
|
||||
}
|
||||
case mtLeave:
|
||||
{
|
||||
if (ShouldAppendChatPrefixes)
|
||||
Message = Printf("%s[LEAVE] %s", cChatColor::Yellow.c_str(), cChatColor::White.c_str());
|
||||
else
|
||||
Message = Printf("%s", cChatColor::Yellow.c_str());
|
||||
break;
|
||||
}
|
||||
default: ASSERT(!"Unhandled chat prefix type!"); return;
|
||||
}
|
||||
|
||||
Message.append(a_Message);
|
||||
|
||||
m_Protocol->SendChat(Message);
|
||||
m_Protocol->SendChat(Message.append(a_Message));
|
||||
}
|
||||
|
||||
|
||||
@ -2101,7 +2144,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);
|
||||
}
|
||||
@ -2673,9 +2716,9 @@ void cClientHandle::SocketClosed(void)
|
||||
|
||||
LOGD("Player %s @ %s disconnected", m_Username.c_str(), m_IPString.c_str());
|
||||
|
||||
if (m_Username != "") // Ignore client pings
|
||||
if (!m_Username.empty()) // Ignore client pings
|
||||
{
|
||||
cRoot::Get()->GetPluginManager()->CallHookDisconnect(m_Player, "Player disconnected");
|
||||
cRoot::Get()->GetPluginManager()->CallHookDisconnect(*this, "Player disconnected");
|
||||
}
|
||||
|
||||
Destroy();
|
||||
@ -2685,4 +2728,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -18,6 +18,8 @@
|
||||
#include "ByteBuffer.h"
|
||||
#include "Scoreboard.h"
|
||||
#include "Map.h"
|
||||
#include "Enchantments.h"
|
||||
#include "UI/SlotArea.h"
|
||||
|
||||
|
||||
|
||||
@ -62,8 +64,27 @@ 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
|
||||
|
||||
/** Formats the type of message with the proper color and prefix for sending to the client. **/
|
||||
AString FormatMessageType(bool ShouldAppendChatPrefixes, eMessageType a_ChatPrefix, const AString & a_AdditionalData);
|
||||
|
||||
AString FormatChatPrefix(bool ShouldAppendChatPrefixes, AString a_ChatPrefixS, AString m_Color1, AString m_Color2);
|
||||
|
||||
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);
|
||||
|
||||
@ -230,6 +251,9 @@ public:
|
||||
/** 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 +350,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;
|
||||
@ -359,7 +384,10 @@ private:
|
||||
void UnregisterPluginChannels(const AStringVector & a_ChannelList);
|
||||
|
||||
/** Handles the "MC|AdvCdm" plugin message */
|
||||
void HandleCommandBlockMessage(const char * a_Data, unsigned int a_Length);
|
||||
void HandleCommandBlockMessage(const char * a_Data, size_t a_Length);
|
||||
|
||||
/** Handles the "MC|ItemName" plugin message */
|
||||
void HandleAnvilItemName(const char * a_Data, size_t a_Length);
|
||||
|
||||
// cSocketThreads::cCallback overrides:
|
||||
virtual void DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client
|
||||
|
@ -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)
|
||||
)
|
||||
)
|
||||
{
|
||||
@ -800,7 +802,7 @@ void cCraftingRecipes::HandleFireworks(const cItem * a_CraftingGrid, cCraftingRe
|
||||
break;
|
||||
}
|
||||
case E_ITEM_PAPER: break;
|
||||
default: LOG("Unexpected item in firework rocket a_Recipe, was the crafting file fireworks section changed?"); break;
|
||||
default: LOG("Unexpected item in firework rocket recipe, was the crafting file's fireworks section changed?"); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
@ -835,7 +837,7 @@ void cCraftingRecipes::HandleFireworks(const cItem * a_CraftingGrid, cCraftingRe
|
||||
case E_ITEM_GOLD_NUGGET: a_Recipe->m_Result.m_FireworkItem.m_Type = 2; break;
|
||||
case E_ITEM_FEATHER: a_Recipe->m_Result.m_FireworkItem.m_Type = 4; break;
|
||||
case E_ITEM_HEAD: a_Recipe->m_Result.m_FireworkItem.m_Type = 3; break;
|
||||
default: LOG("Unexpected item in firework star a_Recipe, was the crafting file fireworks section changed?"); break; // ermahgerd BARD ardmins
|
||||
default: LOG("Unexpected item in firework star recipe, was the crafting file's fireworks section changed?"); break; // ermahgerd BARD ardmins
|
||||
}
|
||||
}
|
||||
|
||||
|
509
src/Crypto.cpp
509
src/Crypto.cpp
@ -1,509 +0,0 @@
|
||||
|
||||
// Crypto.cpp
|
||||
|
||||
// Implements classes that wrap the cryptographic code library
|
||||
|
||||
#include "Globals.h"
|
||||
#include "Crypto.h"
|
||||
|
||||
#include "polarssl/pk.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
// Self-test the hash formatting for known values:
|
||||
// sha1(Notch) : 4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48
|
||||
// sha1(jeb_) : -7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1
|
||||
// sha1(simon) : 88e16a1019277b15d58faf0541e11910eb756f6
|
||||
|
||||
class Test
|
||||
{
|
||||
public:
|
||||
Test(void)
|
||||
{
|
||||
AString DigestNotch, DigestJeb, DigestSimon;
|
||||
Byte Digest[20];
|
||||
cSHA1Checksum Checksum;
|
||||
Checksum.Update((const Byte *)"Notch", 5);
|
||||
Checksum.Finalize(Digest);
|
||||
cSHA1Checksum::DigestToJava(Digest, DigestNotch);
|
||||
Checksum.Restart();
|
||||
Checksum.Update((const Byte *)"jeb_", 4);
|
||||
Checksum.Finalize(Digest);
|
||||
cSHA1Checksum::DigestToJava(Digest, DigestJeb);
|
||||
Checksum.Restart();
|
||||
Checksum.Update((const Byte *)"simon", 5);
|
||||
Checksum.Finalize(Digest);
|
||||
cSHA1Checksum::DigestToJava(Digest, DigestSimon);
|
||||
printf("Notch: \"%s\"\n", DigestNotch.c_str());
|
||||
printf("jeb_: \"%s\"\n", DigestJeb.c_str());
|
||||
printf("simon: \"%s\"\n", DigestSimon.c_str());
|
||||
assert(DigestNotch == "4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48");
|
||||
assert(DigestJeb == "-7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1");
|
||||
assert(DigestSimon == "88e16a1019277b15d58faf0541e11910eb756f6");
|
||||
}
|
||||
} test;
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cRSAPrivateKey:
|
||||
|
||||
cRSAPrivateKey::cRSAPrivateKey(void)
|
||||
{
|
||||
rsa_init(&m_Rsa, RSA_PKCS_V15, 0);
|
||||
InitRnd();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cRSAPrivateKey::cRSAPrivateKey(const cRSAPrivateKey & a_Other)
|
||||
{
|
||||
rsa_init(&m_Rsa, RSA_PKCS_V15, 0);
|
||||
rsa_copy(&m_Rsa, &a_Other.m_Rsa);
|
||||
InitRnd();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cRSAPrivateKey::~cRSAPrivateKey()
|
||||
{
|
||||
entropy_free(&m_Entropy);
|
||||
rsa_free(&m_Rsa);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cRSAPrivateKey::InitRnd(void)
|
||||
{
|
||||
entropy_init(&m_Entropy);
|
||||
const unsigned char pers[] = "rsa_genkey";
|
||||
ctr_drbg_init(&m_Ctr_drbg, entropy_func, &m_Entropy, pers, sizeof(pers) - 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cRSAPrivateKey::Generate(unsigned a_KeySizeBits)
|
||||
{
|
||||
if (rsa_gen_key(&m_Rsa, ctr_drbg_random, &m_Ctr_drbg, a_KeySizeBits, 65537) != 0)
|
||||
{
|
||||
// Key generation failed
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AString cRSAPrivateKey::GetPubKeyDER(void)
|
||||
{
|
||||
class cPubKey
|
||||
{
|
||||
public:
|
||||
cPubKey(rsa_context * a_Rsa) :
|
||||
m_IsValid(false)
|
||||
{
|
||||
pk_init(&m_Key);
|
||||
if (pk_init_ctx(&m_Key, pk_info_from_type(POLARSSL_PK_RSA)) != 0)
|
||||
{
|
||||
ASSERT(!"Cannot init PrivKey context");
|
||||
return;
|
||||
}
|
||||
if (rsa_copy(pk_rsa(m_Key), a_Rsa) != 0)
|
||||
{
|
||||
ASSERT(!"Cannot copy PrivKey to PK context");
|
||||
return;
|
||||
}
|
||||
m_IsValid = true;
|
||||
}
|
||||
|
||||
~cPubKey()
|
||||
{
|
||||
if (m_IsValid)
|
||||
{
|
||||
pk_free(&m_Key);
|
||||
}
|
||||
}
|
||||
|
||||
operator pk_context * (void) { return &m_Key; }
|
||||
|
||||
protected:
|
||||
bool m_IsValid;
|
||||
pk_context m_Key;
|
||||
} PkCtx(&m_Rsa);
|
||||
|
||||
unsigned char buf[3000];
|
||||
int res = pk_write_pubkey_der(PkCtx, buf, sizeof(buf));
|
||||
if (res < 0)
|
||||
{
|
||||
return AString();
|
||||
}
|
||||
return AString((const char *)(buf + sizeof(buf) - res), (size_t)res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cRSAPrivateKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength)
|
||||
{
|
||||
if (a_EncryptedLength < m_Rsa.len)
|
||||
{
|
||||
LOGD("%s: Invalid a_EncryptedLength: got %u, exp at least %u",
|
||||
__FUNCTION__, (unsigned)a_EncryptedLength, (unsigned)(m_Rsa.len)
|
||||
);
|
||||
ASSERT(!"Invalid a_DecryptedMaxLength!");
|
||||
return -1;
|
||||
}
|
||||
if (a_DecryptedMaxLength < m_Rsa.len)
|
||||
{
|
||||
LOGD("%s: Invalid a_DecryptedMaxLength: got %u, exp at least %u",
|
||||
__FUNCTION__, (unsigned)a_EncryptedLength, (unsigned)(m_Rsa.len)
|
||||
);
|
||||
ASSERT(!"Invalid a_DecryptedMaxLength!");
|
||||
return -1;
|
||||
}
|
||||
size_t DecryptedLength;
|
||||
int res = rsa_pkcs1_decrypt(
|
||||
&m_Rsa, ctr_drbg_random, &m_Ctr_drbg, RSA_PRIVATE, &DecryptedLength,
|
||||
a_EncryptedData, a_DecryptedData, a_DecryptedMaxLength
|
||||
);
|
||||
if (res != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return (int)DecryptedLength;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cRSAPrivateKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength)
|
||||
{
|
||||
if (a_EncryptedMaxLength < m_Rsa.len)
|
||||
{
|
||||
LOGD("%s: Invalid a_EncryptedMaxLength: got %u, exp at least %u",
|
||||
__FUNCTION__, (unsigned)a_EncryptedMaxLength, (unsigned)(m_Rsa.len)
|
||||
);
|
||||
ASSERT(!"Invalid a_DecryptedMaxLength!");
|
||||
return -1;
|
||||
}
|
||||
if (a_EncryptedMaxLength < m_Rsa.len)
|
||||
{
|
||||
LOGD("%s: Invalid a_PlainLength: got %u, exp at least %u",
|
||||
__FUNCTION__, (unsigned)a_PlainLength, (unsigned)(m_Rsa.len)
|
||||
);
|
||||
ASSERT(!"Invalid a_PlainLength!");
|
||||
return -1;
|
||||
}
|
||||
int res = rsa_pkcs1_encrypt(
|
||||
&m_Rsa, ctr_drbg_random, &m_Ctr_drbg, RSA_PRIVATE,
|
||||
a_PlainLength, a_PlainData, a_EncryptedData
|
||||
);
|
||||
if (res != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return (int)m_Rsa.len;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cPublicKey:
|
||||
|
||||
cPublicKey::cPublicKey(const AString & a_PublicKeyDER)
|
||||
{
|
||||
pk_init(&m_Pk);
|
||||
if (pk_parse_public_key(&m_Pk, (const Byte *)a_PublicKeyDER.data(), a_PublicKeyDER.size()) != 0)
|
||||
{
|
||||
ASSERT(!"Cannot parse PubKey");
|
||||
return;
|
||||
}
|
||||
InitRnd();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cPublicKey::~cPublicKey()
|
||||
{
|
||||
pk_free(&m_Pk);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cPublicKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength)
|
||||
{
|
||||
size_t DecryptedLen = a_DecryptedMaxLength;
|
||||
int res = pk_decrypt(&m_Pk,
|
||||
a_EncryptedData, a_EncryptedLength,
|
||||
a_DecryptedData, &DecryptedLen, a_DecryptedMaxLength,
|
||||
ctr_drbg_random, &m_Ctr_drbg
|
||||
);
|
||||
if (res != 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
return (int)DecryptedLen;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cPublicKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength)
|
||||
{
|
||||
size_t EncryptedLength = a_EncryptedMaxLength;
|
||||
int res = pk_encrypt(&m_Pk,
|
||||
a_PlainData, a_PlainLength, a_EncryptedData, &EncryptedLength, a_EncryptedMaxLength,
|
||||
ctr_drbg_random, &m_Ctr_drbg
|
||||
);
|
||||
if (res != 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
return (int)EncryptedLength;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPublicKey::InitRnd(void)
|
||||
{
|
||||
entropy_init(&m_Entropy);
|
||||
const unsigned char pers[] = "rsa_genkey";
|
||||
ctr_drbg_init(&m_Ctr_drbg, entropy_func, &m_Entropy, pers, sizeof(pers) - 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cAESCFBDecryptor:
|
||||
|
||||
cAESCFBDecryptor::cAESCFBDecryptor(void) :
|
||||
m_IVOffset(0),
|
||||
m_IsValid(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cAESCFBDecryptor::~cAESCFBDecryptor()
|
||||
{
|
||||
// Clear the leftover in-memory data, so that they can't be accessed by a backdoor
|
||||
memset(&m_Aes, 0, sizeof(m_Aes));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cAESCFBDecryptor::Init(const Byte a_Key[16], const Byte a_IV[16])
|
||||
{
|
||||
ASSERT(!IsValid()); // Cannot Init twice
|
||||
|
||||
memcpy(m_IV, a_IV, 16);
|
||||
aes_setkey_enc(&m_Aes, a_Key, 128);
|
||||
m_IsValid = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cAESCFBDecryptor::ProcessData(Byte * a_DecryptedOut, const Byte * a_EncryptedIn, size_t a_Length)
|
||||
{
|
||||
ASSERT(IsValid()); // Must Init() first
|
||||
|
||||
// PolarSSL doesn't support AES-CFB8, need to implement it manually:
|
||||
for (size_t i = 0; i < a_Length; i++)
|
||||
{
|
||||
Byte Buffer[sizeof(m_IV)];
|
||||
aes_crypt_ecb(&m_Aes, AES_ENCRYPT, m_IV, Buffer);
|
||||
for (size_t idx = 0; idx < sizeof(m_IV) - 1; idx++)
|
||||
{
|
||||
m_IV[idx] = m_IV[idx + 1];
|
||||
}
|
||||
m_IV[sizeof(m_IV) - 1] = a_EncryptedIn[i];
|
||||
a_DecryptedOut[i] = a_EncryptedIn[i] ^ Buffer[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cAESCFBEncryptor:
|
||||
|
||||
cAESCFBEncryptor::cAESCFBEncryptor(void) :
|
||||
m_IVOffset(0),
|
||||
m_IsValid(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cAESCFBEncryptor::~cAESCFBEncryptor()
|
||||
{
|
||||
// Clear the leftover in-memory data, so that they can't be accessed by a backdoor
|
||||
memset(&m_Aes, 0, sizeof(m_Aes));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cAESCFBEncryptor::Init(const Byte a_Key[16], const Byte a_IV[16])
|
||||
{
|
||||
ASSERT(!IsValid()); // Cannot Init twice
|
||||
ASSERT(m_IVOffset == 0);
|
||||
|
||||
memcpy(m_IV, a_IV, 16);
|
||||
aes_setkey_enc(&m_Aes, a_Key, 128);
|
||||
m_IsValid = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cAESCFBEncryptor::ProcessData(Byte * a_EncryptedOut, const Byte * a_PlainIn, size_t a_Length)
|
||||
{
|
||||
ASSERT(IsValid()); // Must Init() first
|
||||
|
||||
// PolarSSL doesn't do AES-CFB8, so we need to implement it ourselves:
|
||||
for (size_t i = 0; i < a_Length; i++)
|
||||
{
|
||||
Byte Buffer[sizeof(m_IV)];
|
||||
aes_crypt_ecb(&m_Aes, AES_ENCRYPT, m_IV, Buffer);
|
||||
for (size_t idx = 0; idx < sizeof(m_IV) - 1; idx++)
|
||||
{
|
||||
m_IV[idx] = m_IV[idx + 1];
|
||||
}
|
||||
a_EncryptedOut[i] = a_PlainIn[i] ^ Buffer[0];
|
||||
m_IV[sizeof(m_IV) - 1] = a_EncryptedOut[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cSHA1Checksum:
|
||||
|
||||
cSHA1Checksum::cSHA1Checksum(void) :
|
||||
m_DoesAcceptInput(true)
|
||||
{
|
||||
sha1_starts(&m_Sha1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSHA1Checksum::Update(const Byte * a_Data, size_t a_Length)
|
||||
{
|
||||
ASSERT(m_DoesAcceptInput); // Not Finalize()-d yet, or Restart()-ed
|
||||
|
||||
sha1_update(&m_Sha1, a_Data, a_Length);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSHA1Checksum::Finalize(cSHA1Checksum::Checksum & a_Output)
|
||||
{
|
||||
ASSERT(m_DoesAcceptInput); // Not Finalize()-d yet, or Restart()-ed
|
||||
|
||||
sha1_finish(&m_Sha1, a_Output);
|
||||
m_DoesAcceptInput = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSHA1Checksum::DigestToJava(const Checksum & a_Digest, AString & a_Out)
|
||||
{
|
||||
Checksum Digest;
|
||||
memcpy(Digest, a_Digest, sizeof(Digest));
|
||||
|
||||
bool IsNegative = (Digest[0] >= 0x80);
|
||||
if (IsNegative)
|
||||
{
|
||||
// Two's complement:
|
||||
bool carry = true; // Add one to the whole number
|
||||
for (int i = 19; i >= 0; i--)
|
||||
{
|
||||
Digest[i] = ~Digest[i];
|
||||
if (carry)
|
||||
{
|
||||
carry = (Digest[i] == 0xff);
|
||||
Digest[i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
a_Out.clear();
|
||||
a_Out.reserve(40);
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
AppendPrintf(a_Out, "%02x", Digest[i]);
|
||||
}
|
||||
while ((a_Out.length() > 0) && (a_Out[0] == '0'))
|
||||
{
|
||||
a_Out.erase(0, 1);
|
||||
}
|
||||
if (IsNegative)
|
||||
{
|
||||
a_Out.insert(0, "-");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSHA1Checksum::Restart(void)
|
||||
{
|
||||
sha1_starts(&m_Sha1);
|
||||
m_DoesAcceptInput = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
198
src/Crypto.h
198
src/Crypto.h
@ -1,198 +0,0 @@
|
||||
|
||||
// Crypto.h
|
||||
|
||||
// Declares classes that wrap the cryptographic code library
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "polarssl/rsa.h"
|
||||
#include "polarssl/aes.h"
|
||||
#include "polarssl/entropy.h"
|
||||
#include "polarssl/ctr_drbg.h"
|
||||
#include "polarssl/sha1.h"
|
||||
#include "polarssl/pk.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Encapsulates an RSA private key used in PKI cryptography */
|
||||
class cRSAPrivateKey
|
||||
{
|
||||
public:
|
||||
/** Creates a new empty object, the key is not assigned */
|
||||
cRSAPrivateKey(void);
|
||||
|
||||
/** Deep-copies the key from a_Other */
|
||||
cRSAPrivateKey(const cRSAPrivateKey & a_Other);
|
||||
|
||||
~cRSAPrivateKey();
|
||||
|
||||
/** Generates a new key within this object, with the specified size in bits.
|
||||
Returns true on success, false on failure. */
|
||||
bool Generate(unsigned a_KeySizeBits = 1024);
|
||||
|
||||
/** Returns the public key part encoded in ASN1 DER encoding */
|
||||
AString GetPubKeyDER(void);
|
||||
|
||||
/** Decrypts the data using RSAES-PKCS#1 algorithm.
|
||||
Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large.
|
||||
Returns the number of bytes decrypted, or negative number for error. */
|
||||
int Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength);
|
||||
|
||||
/** Encrypts the data using RSAES-PKCS#1 algorithm.
|
||||
Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large.
|
||||
Returns the number of bytes decrypted, or negative number for error. */
|
||||
int Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength);
|
||||
|
||||
protected:
|
||||
rsa_context m_Rsa;
|
||||
entropy_context m_Entropy;
|
||||
ctr_drbg_context m_Ctr_drbg;
|
||||
|
||||
/** Initializes the m_Entropy and m_Ctr_drbg contexts
|
||||
Common part of this object's construction, called from all constructors. */
|
||||
void InitRnd(void);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cPublicKey
|
||||
{
|
||||
public:
|
||||
cPublicKey(const AString & a_PublicKeyDER);
|
||||
~cPublicKey();
|
||||
|
||||
/** Decrypts the data using the stored public key
|
||||
Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large.
|
||||
Returns the number of bytes decrypted, or negative number for error. */
|
||||
int Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength);
|
||||
|
||||
/** Encrypts the data using the stored public key
|
||||
Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large.
|
||||
Returns the number of bytes decrypted, or negative number for error. */
|
||||
int Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength);
|
||||
|
||||
protected:
|
||||
pk_context m_Pk;
|
||||
entropy_context m_Entropy;
|
||||
ctr_drbg_context m_Ctr_drbg;
|
||||
|
||||
/** Initializes the m_Entropy and m_Ctr_drbg contexts
|
||||
Common part of this object's construction, called from all constructors. */
|
||||
void InitRnd(void);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Decrypts data using the AES / CFB (128) algorithm */
|
||||
class cAESCFBDecryptor
|
||||
{
|
||||
public:
|
||||
Byte test;
|
||||
|
||||
cAESCFBDecryptor(void);
|
||||
~cAESCFBDecryptor();
|
||||
|
||||
/** Initializes the decryptor with the specified Key / IV */
|
||||
void Init(const Byte a_Key[16], const Byte a_IV[16]);
|
||||
|
||||
/** Decrypts a_Length bytes of the encrypted data; produces a_Length output bytes */
|
||||
void ProcessData(Byte * a_DecryptedOut, const Byte * a_EncryptedIn, size_t a_Length);
|
||||
|
||||
/** Returns true if the object has been initialized with the Key / IV */
|
||||
bool IsValid(void) const { return m_IsValid; }
|
||||
|
||||
protected:
|
||||
aes_context m_Aes;
|
||||
|
||||
/** The InitialVector, used by the CFB mode decryption */
|
||||
Byte m_IV[16];
|
||||
|
||||
/** Current offset in the m_IV, used by the CFB mode decryption */
|
||||
size_t m_IVOffset;
|
||||
|
||||
/** Indicates whether the object has been initialized with the Key / IV */
|
||||
bool m_IsValid;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Encrypts data using the AES / CFB (128) algorithm */
|
||||
class cAESCFBEncryptor
|
||||
{
|
||||
public:
|
||||
cAESCFBEncryptor(void);
|
||||
~cAESCFBEncryptor();
|
||||
|
||||
/** Initializes the decryptor with the specified Key / IV */
|
||||
void Init(const Byte a_Key[16], const Byte a_IV[16]);
|
||||
|
||||
/** Encrypts a_Length bytes of the plain data; produces a_Length output bytes */
|
||||
void ProcessData(Byte * a_EncryptedOut, const Byte * a_PlainIn, size_t a_Length);
|
||||
|
||||
/** Returns true if the object has been initialized with the Key / IV */
|
||||
bool IsValid(void) const { return m_IsValid; }
|
||||
|
||||
protected:
|
||||
aes_context m_Aes;
|
||||
|
||||
/** The InitialVector, used by the CFB mode encryption */
|
||||
Byte m_IV[16];
|
||||
|
||||
/** Current offset in the m_IV, used by the CFB mode encryption */
|
||||
size_t m_IVOffset;
|
||||
|
||||
/** Indicates whether the object has been initialized with the Key / IV */
|
||||
bool m_IsValid;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Calculates a SHA1 checksum for data stream */
|
||||
class cSHA1Checksum
|
||||
{
|
||||
public:
|
||||
typedef Byte Checksum[20]; // The type used for storing the checksum
|
||||
|
||||
cSHA1Checksum(void);
|
||||
|
||||
/** Adds the specified data to the checksum */
|
||||
void Update(const Byte * a_Data, size_t a_Length);
|
||||
|
||||
/** Calculates and returns the final checksum */
|
||||
void Finalize(Checksum & a_Output);
|
||||
|
||||
/** Returns true if the object is accepts more input data, false if Finalize()-d (need to Restart()) */
|
||||
bool DoesAcceptInput(void) const { return m_DoesAcceptInput; }
|
||||
|
||||
/** Converts a raw 160-bit SHA1 digest into a Java Hex representation
|
||||
According to http://wiki.vg/wiki/index.php?title=Protocol_Encryption&oldid=2802
|
||||
*/
|
||||
static void DigestToJava(const Checksum & a_Digest, AString & a_JavaOut);
|
||||
|
||||
/** Clears the current context and start a new checksum calculation */
|
||||
void Restart(void);
|
||||
|
||||
protected:
|
||||
/** True if the object is accepts more input data, false if Finalize()-d (need to Restart()) */
|
||||
bool m_DoesAcceptInput;
|
||||
|
||||
sha1_context m_Sha1;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
@ -109,21 +109,24 @@ void cDeadlockDetect::CheckWorldAge(const AString & a_WorldName, Int64 a_Age)
|
||||
WorldAges::iterator itr = m_WorldAges.find(a_WorldName);
|
||||
if (itr == m_WorldAges.end())
|
||||
{
|
||||
ASSERT(!"Unknown world in cDeadlockDetect");
|
||||
SetWorldAge(a_WorldName, a_Age);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
@ -74,6 +83,15 @@ void cEnchantments::AddFromString(const AString & a_StringSpec)
|
||||
|
||||
|
||||
|
||||
size_t cEnchantments::Count(void)
|
||||
{
|
||||
return m_Enchantments.size();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AString cEnchantments::ToString(void) const
|
||||
{
|
||||
// Serialize all the enchantments into a string
|
||||
@ -150,7 +168,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 +199,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 +240,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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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,90 @@ 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
|
||||
/** Get the count of enchantments */
|
||||
size_t Count(void);
|
||||
|
||||
/** 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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
193
src/Entities/ArrowEntity.cpp
Normal file
193
src/Entities/ArrowEntity.cpp
Normal file
@ -0,0 +1,193 @@
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "Player.h"
|
||||
#include "ArrowEntity.h"
|
||||
#include "../Chunk.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
|
||||
super(pkArrow, a_Creator, a_X, a_Y, a_Z, 0.5, 0.5),
|
||||
m_PickupState(psNoPickup),
|
||||
m_DamageCoeff(2),
|
||||
m_IsCritical(false),
|
||||
m_Timer(0),
|
||||
m_HitGroundTimer(0),
|
||||
m_bIsCollected(false),
|
||||
m_HitBlockPos(Vector3i(0, 0, 0))
|
||||
{
|
||||
SetSpeed(a_Speed);
|
||||
SetMass(0.1);
|
||||
SetYawFromSpeed();
|
||||
SetPitchFromSpeed();
|
||||
LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}",
|
||||
m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ(),
|
||||
GetYaw(), GetPitch()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cArrowEntity::cArrowEntity(cPlayer & a_Player, double a_Force) :
|
||||
super(pkArrow, &a_Player, a_Player.GetThrowStartPos(), a_Player.GetThrowSpeed(a_Force * 1.5 * 20), 0.5, 0.5),
|
||||
m_PickupState(psInSurvivalOrCreative),
|
||||
m_DamageCoeff(2),
|
||||
m_IsCritical((a_Force >= 1)),
|
||||
m_Timer(0),
|
||||
m_HitGroundTimer(0),
|
||||
m_HasTeleported(false),
|
||||
m_bIsCollected(false),
|
||||
m_HitBlockPos(0, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cArrowEntity::CanPickup(const cPlayer & a_Player) const
|
||||
{
|
||||
switch (m_PickupState)
|
||||
{
|
||||
case psNoPickup: return false;
|
||||
case psInSurvivalOrCreative: return (a_Player.IsGameModeSurvival() || a_Player.IsGameModeCreative());
|
||||
case psInCreative: return a_Player.IsGameModeCreative();
|
||||
}
|
||||
ASSERT(!"Unhandled pickup state");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
|
||||
{
|
||||
if (a_HitFace == BLOCK_FACE_NONE) { return; }
|
||||
|
||||
super::OnHitSolidBlock(a_HitPos, a_HitFace);
|
||||
int a_X = (int)a_HitPos.x, a_Y = (int)a_HitPos.y, a_Z = (int)a_HitPos.z;
|
||||
|
||||
switch (a_HitFace)
|
||||
{
|
||||
case BLOCK_FACE_XM: // Strangely, bounding boxes / block tracers return the actual block for these two directions, so AddFace not needed
|
||||
case BLOCK_FACE_YM:
|
||||
{
|
||||
break;
|
||||
}
|
||||
default: AddFaceDirection(a_X, a_Y, a_Z, a_HitFace, true);
|
||||
}
|
||||
|
||||
m_HitBlockPos = Vector3i(a_X, a_Y, a_Z);
|
||||
|
||||
// Broadcast arrow hit sound
|
||||
m_World->BroadcastSoundEffect("random.bowhit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
|
||||
{
|
||||
if (!a_EntityHit.IsMob() && !a_EntityHit.IsMinecart() && !a_EntityHit.IsPlayer() && !a_EntityHit.IsBoat())
|
||||
{
|
||||
// Not an entity that interacts with an arrow
|
||||
return;
|
||||
}
|
||||
|
||||
int Damage = (int)(GetSpeed().Length() / 20 * m_DamageCoeff + 0.5);
|
||||
if (m_IsCritical)
|
||||
{
|
||||
Damage += m_World->GetTickRandomNumber(Damage / 2 + 2);
|
||||
}
|
||||
a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, 1);
|
||||
|
||||
// Broadcast successful hit sound
|
||||
m_World->BroadcastSoundEffect("random.successful_hit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
|
||||
|
||||
Destroy();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cArrowEntity::CollectedBy(cPlayer * a_Dest)
|
||||
{
|
||||
if ((m_IsInGround) && (!m_bIsCollected) && (CanPickup(*a_Dest)))
|
||||
{
|
||||
int NumAdded = a_Dest->GetInventory().AddItem(E_ITEM_ARROW);
|
||||
if (NumAdded > 0) // Only play effects if there was space in inventory
|
||||
{
|
||||
m_World->BroadcastCollectPickup((const cPickup &)*this, *a_Dest);
|
||||
// Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;)
|
||||
m_World->BroadcastSoundEffect("random.pop", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
|
||||
m_bIsCollected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cArrowEntity::Tick(float a_Dt, cChunk & a_Chunk)
|
||||
{
|
||||
super::Tick(a_Dt, a_Chunk);
|
||||
m_Timer += a_Dt;
|
||||
|
||||
if (m_bIsCollected)
|
||||
{
|
||||
if (m_Timer > 500.f) // 0.5 seconds
|
||||
{
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (m_Timer > 1000 * 60 * 5) // 5 minutes
|
||||
{
|
||||
Destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_IsInGround)
|
||||
{
|
||||
// When an arrow hits, the client doesn't think its in the ground and keeps on moving, IF BroadcastMovementUpdate() and TeleportEntity was called during flight, AT ALL
|
||||
// Fix is to simply not sync with the client and send a teleport to confirm pos after arrow has stabilised (around 1 sec after landing)
|
||||
// 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_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_HasTeleported = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_HitGroundTimer += a_Dt;
|
||||
}
|
||||
}
|
||||
|
||||
int RelPosX = m_HitBlockPos.x - a_Chunk.GetPosX() * cChunkDef::Width;
|
||||
int RelPosZ = m_HitBlockPos.z - a_Chunk.GetPosZ() * cChunkDef::Width;
|
||||
cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ);
|
||||
|
||||
if (Chunk == NULL)
|
||||
{
|
||||
// Inside an unloaded chunk, abort
|
||||
return;
|
||||
}
|
||||
|
||||
if (Chunk->GetBlock(RelPosX, m_HitBlockPos.y, RelPosZ) == E_BLOCK_AIR) // Block attached to was destroyed?
|
||||
{
|
||||
m_IsInGround = false; // Yes, begin simulating physics again
|
||||
}
|
||||
}
|
||||
}
|
96
src/Entities/ArrowEntity.h
Normal file
96
src/Entities/ArrowEntity.h
Normal file
@ -0,0 +1,96 @@
|
||||
//
|
||||
// ArrowEntity.h
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ProjectileEntity.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// tolua_begin
|
||||
|
||||
class cArrowEntity :
|
||||
public cProjectileEntity
|
||||
{
|
||||
typedef cProjectileEntity super;
|
||||
|
||||
public:
|
||||
/// Determines when the arrow can be picked up (depending on player gamemode). Corresponds to the MCA file "pickup" field
|
||||
enum ePickupState
|
||||
{
|
||||
psNoPickup = 0,
|
||||
psInSurvivalOrCreative = 1,
|
||||
psInCreative = 2,
|
||||
} ;
|
||||
|
||||
// tolua_end
|
||||
|
||||
CLASS_PROTODEF(cArrowEntity);
|
||||
|
||||
/// Creates a new arrow with psNoPickup state and default damage modifier coeff
|
||||
cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
|
||||
|
||||
/// Creates a new arrow as shot by a player, initializes it from the player object
|
||||
cArrowEntity(cPlayer & a_Player, double a_Force);
|
||||
|
||||
// tolua_begin
|
||||
|
||||
/// Returns whether the arrow can be picked up by players
|
||||
ePickupState GetPickupState(void) const { return m_PickupState; }
|
||||
|
||||
/// Sets a new pickup state
|
||||
void SetPickupState(ePickupState a_PickupState) { m_PickupState = a_PickupState; }
|
||||
|
||||
/// Returns the damage modifier coeff.
|
||||
double GetDamageCoeff(void) const { return m_DamageCoeff; }
|
||||
|
||||
/// Sets the damage modifier coeff
|
||||
void SetDamageCoeff(double a_DamageCoeff) { m_DamageCoeff = a_DamageCoeff; }
|
||||
|
||||
/// Returns true if the specified player can pick the arrow up
|
||||
bool CanPickup(const cPlayer & a_Player) const;
|
||||
|
||||
/// Returns true if the arrow is set as critical
|
||||
bool IsCritical(void) const { return m_IsCritical; }
|
||||
|
||||
/// Sets the IsCritical flag
|
||||
void SetIsCritical(bool a_IsCritical) { m_IsCritical = a_IsCritical; }
|
||||
|
||||
// tolua_end
|
||||
|
||||
protected:
|
||||
|
||||
/// Determines when the arrow can be picked up by players
|
||||
ePickupState m_PickupState;
|
||||
|
||||
/// The coefficient applied to the damage that the arrow will deal, based on the bow enchantment. 2.0 for normal arrow
|
||||
double m_DamageCoeff;
|
||||
|
||||
/// If true, the arrow deals more damage
|
||||
bool m_IsCritical;
|
||||
|
||||
/// Timer for pickup collection animation or five minute timeout
|
||||
float m_Timer;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// Stores the block position that arrow is lodged into, sets m_IsInGround to false if it becomes air
|
||||
Vector3i m_HitBlockPos;
|
||||
|
||||
// cProjectileEntity overrides:
|
||||
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
|
||||
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
|
||||
virtual void CollectedBy(cPlayer * a_Player) override;
|
||||
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
|
||||
|
||||
}; // tolua_export
|
@ -33,9 +33,12 @@ void cBoat::SpawnOn(cClientHandle & a_ClientHandle)
|
||||
|
||||
|
||||
|
||||
void cBoat::DoTakeDamage(TakeDamageInfo & TDI)
|
||||
bool cBoat::DoTakeDamage(TakeDamageInfo & TDI)
|
||||
{
|
||||
super::DoTakeDamage(TDI);
|
||||
if (!super::DoTakeDamage(TDI))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GetHealth() == 0)
|
||||
{
|
||||
@ -50,6 +53,7 @@ void cBoat::DoTakeDamage(TakeDamageInfo & TDI)
|
||||
}
|
||||
Destroy(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -26,7 +26,7 @@ public:
|
||||
// cEntity overrides:
|
||||
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
|
||||
virtual void OnRightClicked(cPlayer & a_Player) override;
|
||||
virtual void DoTakeDamage(TakeDamageInfo & TDI) override;
|
||||
virtual bool DoTakeDamage(TakeDamageInfo & TDI) override;
|
||||
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
|
||||
virtual void HandleSpeedFromAttachee(float a_Forward, float a_Sideways) override;
|
||||
|
||||
|
@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../")
|
||||
|
||||
file(GLOB SOURCE
|
||||
"*.cpp"
|
||||
"*.h"
|
||||
)
|
||||
|
||||
add_library(Entities ${SOURCE})
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "Entity.h"
|
||||
@ -10,7 +11,6 @@
|
||||
#include "../Simulator/FluidSimulator.h"
|
||||
#include "../Bindings/PluginManager.h"
|
||||
#include "../Tracer.h"
|
||||
#include "Minecart.h"
|
||||
#include "Player.h"
|
||||
|
||||
|
||||
@ -32,19 +32,14 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
|
||||
, m_Attachee(NULL)
|
||||
, m_bDirtyHead(true)
|
||||
, m_bDirtyOrientation(true)
|
||||
, m_bDirtyPosition(true)
|
||||
, m_bDirtySpeed(true)
|
||||
, m_bOnGround( false )
|
||||
, m_Gravity( -9.81f )
|
||||
, m_LastPosX( 0.0 )
|
||||
, m_LastPosY( 0.0 )
|
||||
, m_LastPosZ( 0.0 )
|
||||
, m_TimeLastTeleportPacket(0)
|
||||
, m_TimeLastMoveReltPacket(0)
|
||||
, m_TimeLastSpeedPacket(0)
|
||||
, m_bHasSentNoSpeed(true)
|
||||
, m_bOnGround(false)
|
||||
, m_Gravity(-9.81f)
|
||||
, m_LastPos(a_X, a_Y, a_Z)
|
||||
, m_IsInitialized(false)
|
||||
, m_EntityType(a_EntityType)
|
||||
, m_World(NULL)
|
||||
, m_IsFireproof(false)
|
||||
, m_TicksSinceLastBurnDamage(0)
|
||||
, m_TicksSinceLastLavaDamage(0)
|
||||
, m_TicksSinceLastFireDamage(0)
|
||||
@ -52,13 +47,16 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
|
||||
, m_TicksSinceLastVoidDamage(0)
|
||||
, m_IsSwimming(false)
|
||||
, m_IsSubmerged(false)
|
||||
, m_HeadYaw( 0.0 )
|
||||
, m_AirLevel(0)
|
||||
, m_AirTickTimer(0)
|
||||
, m_HeadYaw(0.0)
|
||||
, m_Rot(0.0, 0.0, 0.0)
|
||||
, m_Pos(a_X, a_Y, a_Z)
|
||||
, m_WaterSpeed(0, 0, 0)
|
||||
, m_Mass (0.001) // Default 1g
|
||||
, m_Width(a_Width)
|
||||
, m_Height(a_Height)
|
||||
, m_InvulnerableTicks(0)
|
||||
{
|
||||
cCSLock Lock(m_CSCount);
|
||||
m_EntityCount++;
|
||||
@ -293,17 +291,23 @@ void cEntity::SetPitchFromSpeed(void)
|
||||
|
||||
|
||||
|
||||
void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
|
||||
bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
|
||||
{
|
||||
if (cRoot::Get()->GetPluginManager()->CallHookTakeDamage(*this, a_TDI))
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_Health <= 0)
|
||||
{
|
||||
// Can't take damage if already dead
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_InvulnerableTicks > 0)
|
||||
{
|
||||
// Entity is invulnerable
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((a_TDI.Attacker != NULL) && (a_TDI.Attacker->IsPlayer()))
|
||||
@ -325,17 +329,49 @@ 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);
|
||||
|
||||
m_InvulnerableTicks = 10;
|
||||
|
||||
if (m_Health <= 0)
|
||||
{
|
||||
KilledBy(a_TDI.Attacker);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -380,11 +416,8 @@ int cEntity::GetRawDamageAgainst(const cEntity & a_Receiver)
|
||||
|
||||
|
||||
|
||||
int cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_Damage)
|
||||
bool cEntity::ArmorCoversAgainst(eDamageType a_DamageType)
|
||||
{
|
||||
// Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover
|
||||
|
||||
// Filter out damage types that are not protected by armor:
|
||||
// Ref.: http://www.minecraftwiki.net/wiki/Armor#Effects as of 2012_12_20
|
||||
switch (a_DamageType)
|
||||
{
|
||||
@ -399,9 +432,34 @@ int cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_Dama
|
||||
case dtLightning:
|
||||
case dtPlugin:
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
case dtAttack:
|
||||
case dtArrowAttack:
|
||||
case dtCactusContact:
|
||||
case dtLavaContact:
|
||||
case dtFireContact:
|
||||
case dtEnderPearl:
|
||||
case dtExplosion:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
ASSERT(!"Invalid damage type!");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_Damage)
|
||||
{
|
||||
// Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover
|
||||
|
||||
// Filter out damage types that are not protected by armor:
|
||||
if (!ArmorCoversAgainst(a_DamageType)) return 0;
|
||||
|
||||
// Add up all armor points:
|
||||
// Ref.: http://www.minecraftwiki.net/wiki/Armor#Defense_points as of 2012_12_20
|
||||
@ -479,7 +537,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);
|
||||
}
|
||||
|
||||
|
||||
@ -510,6 +568,11 @@ void cEntity::SetHealth(int a_Health)
|
||||
|
||||
void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
|
||||
{
|
||||
if (m_InvulnerableTicks > 0)
|
||||
{
|
||||
m_InvulnerableTicks--;
|
||||
}
|
||||
|
||||
if (m_AttachedTo != NULL)
|
||||
{
|
||||
if ((m_Pos - m_AttachedTo->GetPosition()).Length() > 0.5)
|
||||
@ -519,37 +582,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 +621,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 +674,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;
|
||||
@ -730,30 +788,43 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
|
||||
|
||||
NextSpeed += m_WaterSpeed;
|
||||
|
||||
if( NextSpeed.SqrLength() > 0.f )
|
||||
if (NextSpeed.SqrLength() > 0.f)
|
||||
{
|
||||
cTracer Tracer( GetWorld() );
|
||||
bool HasHit = Tracer.Trace( NextPos, NextSpeed, 2 );
|
||||
if (HasHit) // Oh noez! we hit something
|
||||
cTracer Tracer(GetWorld());
|
||||
// Distance traced is an integer, so we round up from the distance we should go (Speed * Delta), else we will encounter collision detection failurse
|
||||
int DistanceToTrace = (int)(ceil((NextSpeed * a_Dt).SqrLength()) * 2);
|
||||
bool HasHit = Tracer.Trace(NextPos, NextSpeed, DistanceToTrace);
|
||||
|
||||
if (HasHit)
|
||||
{
|
||||
// Set to hit position
|
||||
// Oh noez! We hit something: verify that the (hit position - current) was smaller or equal to the (position that we should travel without obstacles - current)
|
||||
// This is because previously, we traced with a length that was rounded up (due to integer limitations), and in the case that something was hit, we don't want to overshoot our projected movement
|
||||
if ((Tracer.RealHit - NextPos).SqrLength() <= (NextSpeed * a_Dt).SqrLength())
|
||||
{
|
||||
// Block hit was within our projected path
|
||||
// Begin by stopping movement in the direction that we hit something. The Normal is the line perpendicular to a 2D face and in this case, stores what block face was hit through either -1 or 1.
|
||||
// For example: HitNormal.y = -1 : BLOCK_FACE_YM; HitNormal.y = 1 : BLOCK_FACE_YP
|
||||
if (Tracer.HitNormal.x != 0.f) NextSpeed.x = 0.f;
|
||||
if (Tracer.HitNormal.y != 0.f) NextSpeed.y = 0.f;
|
||||
if (Tracer.HitNormal.z != 0.f) NextSpeed.z = 0.f;
|
||||
|
||||
if (Tracer.HitNormal.y > 0) // means on ground
|
||||
if (Tracer.HitNormal.y == 1) // Hit BLOCK_FACE_YP, we are on the ground
|
||||
{
|
||||
m_bOnGround = true;
|
||||
}
|
||||
NextPos.Set(Tracer.RealHit.x,Tracer.RealHit.y,Tracer.RealHit.z);
|
||||
NextPos.x += Tracer.HitNormal.x * 0.3f;
|
||||
NextPos.y += Tracer.HitNormal.y * 0.05f; // Any larger produces entity vibration-upon-the-spot
|
||||
NextPos.z += Tracer.HitNormal.z * 0.3f;
|
||||
|
||||
// Now, set our position to the hit block (i.e. move part way along our intended trajectory)
|
||||
NextPos.Set(Tracer.RealHit.x, Tracer.RealHit.y, Tracer.RealHit.z);
|
||||
NextPos.x += Tracer.HitNormal.x * 0.1;
|
||||
NextPos.y += Tracer.HitNormal.y * 0.05;
|
||||
NextPos.z += Tracer.HitNormal.z * 0.1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have hit a block but overshot our intended trajectory, move normally, safe in the warm cocoon of knowledge that we won't appear to teleport forwards on clients,
|
||||
// and that this piece of software will come to be hailed as the epitome of performance and functionality in C++, never before seen, and of such a like that will never
|
||||
// be henceforth seen again in the time of programmers and man alike
|
||||
// </&sensationalist>
|
||||
NextPos += (NextSpeed * a_Dt);
|
||||
}
|
||||
}
|
||||
@ -764,20 +835,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 +847,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 +862,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 +876,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 +933,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 +954,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;
|
||||
}
|
||||
}
|
||||
@ -941,9 +1017,9 @@ void cEntity::SetSwimState(cChunk & a_Chunk)
|
||||
{
|
||||
// This sometimes happens on Linux machines
|
||||
// Ref.: http://forum.mc-server.org/showthread.php?tid=1244
|
||||
LOGD("SetSwimState failure: RelX = %d, RelZ = %d, LastPos = {%.02f, %.02f}, Pos = %.02f, %.02f}",
|
||||
RelX, RelY, m_LastPosX, m_LastPosZ, GetPosX(), GetPosZ()
|
||||
);
|
||||
LOGD("SetSwimState failure: RelX = %d, RelZ = %d, Pos = %.02f, %.02f}",
|
||||
RelX, RelY, GetPosX(), GetPosZ()
|
||||
);
|
||||
m_IsSwimming = false;
|
||||
m_IsSubmerged = false;
|
||||
return;
|
||||
@ -981,13 +1057,13 @@ void cEntity::HandleAir(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
m_AirTickTimer -= 1;
|
||||
m_AirTickTimer--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reduce air supply
|
||||
m_AirLevel -= 1;
|
||||
m_AirLevel--;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -1040,6 +1116,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,72 +1185,70 @@ 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))
|
||||
// Process packet sending every two ticks
|
||||
if (GetWorld()->GetWorldAge() % 2 == 0)
|
||||
{
|
||||
m_World->BroadcastEntityVelocity(*this,a_Exclude);
|
||||
m_bDirtySpeed = false;
|
||||
m_TimeLastSpeedPacket = m_World->GetWorldAge();
|
||||
}
|
||||
|
||||
//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));
|
||||
int DiffY = (int) (floor(GetPosY() * 32.0) - floor(m_LastPosY * 32.0));
|
||||
int DiffZ = (int) (floor(GetPosZ() * 32.0) - floor(m_LastPosZ * 32.0));
|
||||
Int64 DiffTeleportPacket = m_World->GetWorldAge() - m_TimeLastTeleportPacket;
|
||||
// 4 blocks is max Relative So if the Diff is greater than 127 or. Send an absolute position every 20 seconds
|
||||
if (DiffTeleportPacket >= 400 ||
|
||||
((DiffX > 127) || (DiffX < -128) ||
|
||||
(DiffY > 127) || (DiffY < -128) ||
|
||||
(DiffZ > 127) || (DiffZ < -128)))
|
||||
double SpeedSqr = GetSpeed().SqrLength();
|
||||
if (SpeedSqr == 0.0)
|
||||
{
|
||||
//
|
||||
m_World->BroadcastTeleportEntity(*this,a_Exclude);
|
||||
m_TimeLastTeleportPacket = m_World->GetWorldAge();
|
||||
m_TimeLastMoveReltPacket = m_TimeLastTeleportPacket; //Must synchronize.
|
||||
m_LastPosX = GetPosX();
|
||||
m_LastPosY = GetPosY();
|
||||
m_LastPosZ = GetPosZ();
|
||||
m_bDirtyPosition = false;
|
||||
m_bDirtyOrientation = false;
|
||||
// Speed is zero, send this to clients once only as well as an absolute position
|
||||
if (!m_bHasSentNoSpeed)
|
||||
{
|
||||
m_World->BroadcastEntityVelocity(*this, a_Exclude);
|
||||
m_World->BroadcastTeleportEntity(*this, a_Exclude);
|
||||
m_bHasSentNoSpeed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Int64 DiffMoveRelPacket = m_World->GetWorldAge() - m_TimeLastMoveReltPacket;
|
||||
//if the change is big enough.
|
||||
if ((abs(DiffX) >= 4 || abs(DiffY) >= 4 || abs(DiffZ) >= 4 || DiffMoveRelPacket >= 60) && m_bDirtyPosition)
|
||||
// Movin'
|
||||
m_World->BroadcastEntityVelocity(*this, a_Exclude);
|
||||
m_bHasSentNoSpeed = false;
|
||||
}
|
||||
|
||||
// TODO: Pickups move disgracefully if relative move packets are sent as opposed to just velocity. Have a system to send relmove only when SetPosXXX() is called with a large difference in position
|
||||
int DiffX = (int)(floor(GetPosX() * 32.0) - floor(m_LastPos.x * 32.0));
|
||||
int DiffY = (int)(floor(GetPosY() * 32.0) - floor(m_LastPos.y * 32.0));
|
||||
int DiffZ = (int)(floor(GetPosZ() * 32.0) - floor(m_LastPos.z * 32.0));
|
||||
|
||||
if ((DiffX != 0) || (DiffY != 0) || (DiffZ != 0)) // Have we moved?
|
||||
{
|
||||
if ((abs(DiffX) <= 127) && (abs(DiffY) <= 127) && (abs(DiffZ) <= 127)) // Limitations of a Byte
|
||||
{
|
||||
// Difference within Byte limitations, use a relative move packet
|
||||
if (m_bDirtyOrientation)
|
||||
{
|
||||
m_World->BroadcastEntityRelMoveLook(*this, (char)DiffX, (char)DiffY, (char)DiffZ,a_Exclude);
|
||||
m_World->BroadcastEntityRelMoveLook(*this, (char)DiffX, (char)DiffY, (char)DiffZ, a_Exclude);
|
||||
m_bDirtyOrientation = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_World->BroadcastEntityRelMove(*this, (char)DiffX, (char)DiffY, (char)DiffZ,a_Exclude);
|
||||
m_World->BroadcastEntityRelMove(*this, (char)DiffX, (char)DiffY, (char)DiffZ, a_Exclude);
|
||||
}
|
||||
m_LastPosX = GetPosX();
|
||||
m_LastPosY = GetPosY();
|
||||
m_LastPosZ = GetPosZ();
|
||||
m_bDirtyPosition = false;
|
||||
m_TimeLastMoveReltPacket = m_World->GetWorldAge();
|
||||
// Clients seem to store two positions, one for the velocity packet and one for the teleport/relmove packet
|
||||
// The latter is only changed with a relmove/teleport, and m_LastPos stores this position
|
||||
m_LastPos = GetPosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_bDirtyOrientation)
|
||||
{
|
||||
m_World->BroadcastEntityLook(*this,a_Exclude);
|
||||
m_bDirtyOrientation = false;
|
||||
}
|
||||
}
|
||||
// Too big a movement, do a teleport
|
||||
m_World->BroadcastTeleportEntity(*this, a_Exclude);
|
||||
m_LastPos = GetPosition(); // See above
|
||||
m_bDirtyOrientation = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_bDirtyHead)
|
||||
{
|
||||
m_World->BroadcastEntityHeadLook(*this,a_Exclude);
|
||||
m_World->BroadcastEntityHeadLook(*this, a_Exclude);
|
||||
m_bDirtyHead = false;
|
||||
}
|
||||
if (m_bDirtyOrientation)
|
||||
{
|
||||
// Send individual update in case above (sending with rel-move packet) wasn't done
|
||||
GetWorld()->BroadcastEntityLook(*this, a_Exclude);
|
||||
m_bDirtyOrientation = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1304,7 +1388,7 @@ void cEntity::SetRoll(double a_Roll)
|
||||
void cEntity::SetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ)
|
||||
{
|
||||
m_Speed.Set(a_SpeedX, a_SpeedY, a_SpeedZ);
|
||||
m_bDirtySpeed = true;
|
||||
|
||||
WrapSpeed();
|
||||
}
|
||||
|
||||
@ -1314,7 +1398,7 @@ void cEntity::SetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ)
|
||||
void cEntity::SetSpeedX(double a_SpeedX)
|
||||
{
|
||||
m_Speed.x = a_SpeedX;
|
||||
m_bDirtySpeed = true;
|
||||
|
||||
WrapSpeed();
|
||||
}
|
||||
|
||||
@ -1324,7 +1408,7 @@ void cEntity::SetSpeedX(double a_SpeedX)
|
||||
void cEntity::SetSpeedY(double a_SpeedY)
|
||||
{
|
||||
m_Speed.y = a_SpeedY;
|
||||
m_bDirtySpeed = true;
|
||||
|
||||
WrapSpeed();
|
||||
}
|
||||
|
||||
@ -1334,7 +1418,7 @@ void cEntity::SetSpeedY(double a_SpeedY)
|
||||
void cEntity::SetSpeedZ(double a_SpeedZ)
|
||||
{
|
||||
m_Speed.z = a_SpeedZ;
|
||||
m_bDirtySpeed = true;
|
||||
|
||||
WrapSpeed();
|
||||
}
|
||||
|
||||
@ -1354,7 +1438,7 @@ void cEntity::SetWidth(double a_Width)
|
||||
void cEntity::AddPosX(double a_AddPosX)
|
||||
{
|
||||
m_Pos.x += a_AddPosX;
|
||||
m_bDirtyPosition = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1363,7 +1447,7 @@ void cEntity::AddPosX(double a_AddPosX)
|
||||
void cEntity::AddPosY(double a_AddPosY)
|
||||
{
|
||||
m_Pos.y += a_AddPosY;
|
||||
m_bDirtyPosition = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1372,7 +1456,7 @@ void cEntity::AddPosY(double a_AddPosY)
|
||||
void cEntity::AddPosZ(double a_AddPosZ)
|
||||
{
|
||||
m_Pos.z += a_AddPosZ;
|
||||
m_bDirtyPosition = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1383,7 +1467,7 @@ void cEntity::AddPosition(double a_AddPosX, double a_AddPosY, double a_AddPosZ)
|
||||
m_Pos.x += a_AddPosX;
|
||||
m_Pos.y += a_AddPosY;
|
||||
m_Pos.z += a_AddPosZ;
|
||||
m_bDirtyPosition = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1393,8 +1477,7 @@ void cEntity::AddSpeed(double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeed
|
||||
{
|
||||
m_Speed.x += a_AddSpeedX;
|
||||
m_Speed.y += a_AddSpeedY;
|
||||
m_Speed.z += a_AddSpeedZ;
|
||||
m_bDirtySpeed = true;
|
||||
m_Speed.z += a_AddSpeedZ;
|
||||
WrapSpeed();
|
||||
}
|
||||
|
||||
@ -1404,8 +1487,7 @@ void cEntity::AddSpeed(double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeed
|
||||
|
||||
void cEntity::AddSpeedX(double a_AddSpeedX)
|
||||
{
|
||||
m_Speed.x += a_AddSpeedX;
|
||||
m_bDirtySpeed = true;
|
||||
m_Speed.x += a_AddSpeedX;
|
||||
WrapSpeed();
|
||||
}
|
||||
|
||||
@ -1415,8 +1497,7 @@ void cEntity::AddSpeedX(double a_AddSpeedX)
|
||||
|
||||
void cEntity::AddSpeedY(double a_AddSpeedY)
|
||||
{
|
||||
m_Speed.y += a_AddSpeedY;
|
||||
m_bDirtySpeed = true;
|
||||
m_Speed.y += a_AddSpeedY;
|
||||
WrapSpeed();
|
||||
}
|
||||
|
||||
@ -1426,8 +1507,7 @@ void cEntity::AddSpeedY(double a_AddSpeedY)
|
||||
|
||||
void cEntity::AddSpeedZ(double a_AddSpeedZ)
|
||||
{
|
||||
m_Speed.z += a_AddSpeedZ;
|
||||
m_bDirtySpeed = true;
|
||||
m_Speed.z += a_AddSpeedZ;
|
||||
WrapSpeed();
|
||||
}
|
||||
|
||||
@ -1482,8 +1562,7 @@ Vector3d cEntity::GetLookVector(void) const
|
||||
// Set position
|
||||
void cEntity::SetPosition(double a_PosX, double a_PosY, double a_PosZ)
|
||||
{
|
||||
m_Pos.Set(a_PosX, a_PosY, a_PosZ);
|
||||
m_bDirtyPosition = true;
|
||||
m_Pos.Set(a_PosX, a_PosY, a_PosZ);
|
||||
}
|
||||
|
||||
|
||||
@ -1492,8 +1571,7 @@ void cEntity::SetPosition(double a_PosX, double a_PosY, double a_PosZ)
|
||||
|
||||
void cEntity::SetPosX(double a_PosX)
|
||||
{
|
||||
m_Pos.x = a_PosX;
|
||||
m_bDirtyPosition = true;
|
||||
m_Pos.x = a_PosX;
|
||||
}
|
||||
|
||||
|
||||
@ -1502,8 +1580,7 @@ void cEntity::SetPosX(double a_PosX)
|
||||
|
||||
void cEntity::SetPosY(double a_PosY)
|
||||
{
|
||||
m_Pos.y = a_PosY;
|
||||
m_bDirtyPosition = true;
|
||||
m_Pos.y = a_PosY;
|
||||
}
|
||||
|
||||
|
||||
@ -1513,7 +1590,6 @@ void cEntity::SetPosY(double a_PosY)
|
||||
void cEntity::SetPosZ(double a_PosZ)
|
||||
{
|
||||
m_Pos.z = a_PosZ;
|
||||
m_bDirtyPosition = true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
@ -240,14 +262,19 @@ public:
|
||||
|
||||
// tolua_end
|
||||
|
||||
/// Makes this entity take damage specified in the a_TDI. The TDI is sent through plugins first, then applied
|
||||
virtual void DoTakeDamage(TakeDamageInfo & a_TDI);
|
||||
/** Makes this entity take damage specified in the a_TDI.
|
||||
The TDI is sent through plugins first, then applied.
|
||||
If it returns false, the entity hasn't receive any damage. */
|
||||
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI);
|
||||
|
||||
// tolua_begin
|
||||
|
||||
/// Returns the hitpoints that this pawn can deal to a_Receiver using its equipped items
|
||||
virtual int GetRawDamageAgainst(const cEntity & a_Receiver);
|
||||
|
||||
/** Returns whether armor will protect against the passed damage type **/
|
||||
virtual bool ArmorCoversAgainst(eDamageType a_DamageType);
|
||||
|
||||
/// Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover
|
||||
virtual int GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_RawDamage);
|
||||
|
||||
@ -307,6 +334,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);
|
||||
|
||||
@ -364,6 +396,12 @@ public:
|
||||
virtual bool IsSubmerged(void) const{ return m_IsSubmerged; }
|
||||
/** Gets remaining air of a monster */
|
||||
int GetAirLevel(void) const { return m_AirLevel; }
|
||||
|
||||
/** Gets the invulnerable ticks from the entity */
|
||||
int GetInvulnerableTicks(void) const { return m_InvulnerableTicks; }
|
||||
|
||||
/** Set the invulnerable ticks from the entity */
|
||||
void SetInvulnerableTicks(int a_InvulnerableTicks) { m_InvulnerableTicks = a_InvulnerableTicks; }
|
||||
|
||||
// tolua_end
|
||||
|
||||
@ -392,27 +430,37 @@ protected:
|
||||
/// The entity which is attached to this entity (rider), NULL if none
|
||||
cEntity * m_Attachee;
|
||||
|
||||
// Flags that signal that we haven't updated the clients with the latest.
|
||||
bool m_bDirtyHead;
|
||||
bool m_bDirtyOrientation;
|
||||
bool m_bDirtyPosition;
|
||||
bool m_bDirtySpeed;
|
||||
|
||||
bool m_bOnGround;
|
||||
float m_Gravity;
|
||||
/** Stores whether head yaw has been set manually */
|
||||
bool m_bDirtyHead;
|
||||
|
||||
// Last Position.
|
||||
double m_LastPosX, m_LastPosY, m_LastPosZ;
|
||||
/** Stores whether our yaw/pitch/roll (body orientation) has been set manually */
|
||||
bool m_bDirtyOrientation;
|
||||
|
||||
/** Stores whether we have sent a Velocity packet with a speed of zero (no speed) to the client
|
||||
Ensures that said packet is sent only once */
|
||||
bool m_bHasSentNoSpeed;
|
||||
|
||||
// This variables keep track of the last time a packet was sent
|
||||
Int64 m_TimeLastTeleportPacket, m_TimeLastMoveReltPacket, m_TimeLastSpeedPacket; // In ticks
|
||||
/** Stores if the entity is on the ground */
|
||||
bool m_bOnGround;
|
||||
|
||||
/** Stores gravity that is applied to an entity every tick
|
||||
For realistic effects, this should be negative. For spaaaaaaace, this can be zero or even positive */
|
||||
float m_Gravity;
|
||||
|
||||
/** Last position sent to client via the Relative Move or Teleport packets (not Velocity)
|
||||
Only updated if cEntity::BroadcastMovementUpdate() is called! */
|
||||
Vector3d m_LastPos;
|
||||
|
||||
bool m_IsInitialized; // Is set to true when it's initialized, until it's destroyed (Initialize() till Destroy() )
|
||||
/** True when entity is initialised (Initialize()) and false when destroyed pending deletion (Destroy()) */
|
||||
bool m_IsInitialized;
|
||||
|
||||
eEntityType m_EntityType;
|
||||
|
||||
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;
|
||||
|
||||
@ -428,12 +476,14 @@ protected:
|
||||
/// Time, in ticks, since the last damage dealt by the void. Reset to zero when moving out of the void.
|
||||
int m_TicksSinceLastVoidDamage;
|
||||
|
||||
|
||||
virtual void Destroyed(void) {} // Called after the entity has been destroyed
|
||||
|
||||
void SetWorld(cWorld * a_World) { m_World = a_World; }
|
||||
|
||||
/** Called in each tick to handle air-related processing i.e. drowning */
|
||||
virtual void HandleAir();
|
||||
|
||||
/** Called once per tick to set IsSwimming and IsSubmerged */
|
||||
virtual void SetSwimState(cChunk & a_Chunk);
|
||||
|
||||
@ -445,29 +495,33 @@ protected:
|
||||
int m_AirTickTimer;
|
||||
|
||||
private:
|
||||
// Measured in degrees, [-180, +180)
|
||||
/** Measured in degrees, [-180, +180) */
|
||||
double m_HeadYaw;
|
||||
|
||||
// Measured in meter/second (m/s)
|
||||
/** Measured in meter/second (m/s) */
|
||||
Vector3d m_Speed;
|
||||
|
||||
// Measured in degrees, [-180, +180)
|
||||
/** Measured in degrees, [-180, +180) */
|
||||
Vector3d m_Rot;
|
||||
|
||||
/// Position of the entity's XZ center and Y bottom
|
||||
/** Position of the entity's XZ center and Y bottom */
|
||||
Vector3d m_Pos;
|
||||
|
||||
// Measured in meter / second
|
||||
/** Measured in meter / second */
|
||||
Vector3d m_WaterSpeed;
|
||||
|
||||
// Measured in Kilograms (Kg)
|
||||
/** Measured in Kilograms (Kg) */
|
||||
double m_Mass;
|
||||
|
||||
/// Width of the entity, in the XZ plane. Since entities are represented as cylinders, this is more of a diameter.
|
||||
/** Width of the entity, in the XZ plane. Since entities are represented as cylinders, this is more of a diameter. */
|
||||
double m_Width;
|
||||
|
||||
/// Height of the entity (Y axis)
|
||||
/** Height of the entity (Y axis) */
|
||||
double m_Height;
|
||||
|
||||
/** If a player hit a entity, the entity receive a invulnerable of 10 ticks.
|
||||
While this ticks, a player can't hit this entity. */
|
||||
int m_InvulnerableTicks;
|
||||
} ; // tolua_export
|
||||
|
||||
typedef std::list<cEntity *> cEntityList;
|
||||
|
27
src/Entities/ExpBottleEntity.cpp
Normal file
27
src/Entities/ExpBottleEntity.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "ExpBottleEntity.h"
|
||||
#include "../World.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cExpBottleEntity::cExpBottleEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
|
||||
super(pkExpBottle, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
|
||||
{
|
||||
SetSpeed(a_Speed);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cExpBottleEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
|
||||
{
|
||||
// Spawn an experience orb with a reward between 3 and 11.
|
||||
m_World->BroadcastSoundParticleEffect(2002, POSX_TOINT, POSY_TOINT, POSZ_TOINT, 0);
|
||||
m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), 3 + m_World->GetTickRandomNumber(8));
|
||||
|
||||
Destroy();
|
||||
}
|
33
src/Entities/ExpBottleEntity.h
Normal file
33
src/Entities/ExpBottleEntity.h
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// ExpBottleEntity.h
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ProjectileEntity.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// tolua_begin
|
||||
|
||||
class cExpBottleEntity :
|
||||
public cProjectileEntity
|
||||
{
|
||||
typedef cProjectileEntity super;
|
||||
|
||||
public:
|
||||
|
||||
// tolua_end
|
||||
|
||||
CLASS_PROTODEF(cExpBottleEntity);
|
||||
|
||||
cExpBottleEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
|
||||
|
||||
protected:
|
||||
|
||||
// cProjectileEntity overrides:
|
||||
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
|
||||
|
||||
}; // tolua_export
|
@ -34,8 +34,6 @@ cExpOrb::cExpOrb(const Vector3d & a_Pos, int a_Reward)
|
||||
void cExpOrb::SpawnOn(cClientHandle & a_Client)
|
||||
{
|
||||
a_Client.SendExperienceOrb(*this);
|
||||
m_bDirtyPosition = false;
|
||||
m_bDirtySpeed = false;
|
||||
m_bDirtyOrientation = false;
|
||||
m_bDirtyHead = false;
|
||||
}
|
||||
|
@ -87,7 +87,8 @@ void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk)
|
||||
AddSpeedY(MilliDt * -9.8f);
|
||||
AddPosition(GetSpeed() * MilliDt);
|
||||
|
||||
if ((GetSpeedX() != 0) || (GetSpeedZ() != 0))
|
||||
// If not static (one billionth precision) broadcast movement
|
||||
if ((fabs(GetSpeedX()) > std::numeric_limits<double>::epsilon()) || (fabs(GetSpeedZ()) > std::numeric_limits<double>::epsilon()))
|
||||
{
|
||||
BroadcastMovementUpdate();
|
||||
}
|
||||
|
50
src/Entities/FireChargeEntity.cpp
Normal file
50
src/Entities/FireChargeEntity.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "FireChargeEntity.h"
|
||||
#include "../World.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cFireChargeEntity::cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
|
||||
super(pkFireCharge, a_Creator, a_X, a_Y, a_Z, 0.3125, 0.3125)
|
||||
{
|
||||
SetSpeed(a_Speed);
|
||||
SetGravity(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cFireChargeEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
{
|
||||
if (m_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR)
|
||||
{
|
||||
m_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cFireChargeEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
|
||||
{
|
||||
Destroy();
|
||||
Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cFireChargeEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
|
||||
{
|
||||
Destroy();
|
||||
Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z));
|
||||
|
||||
// TODO: Some entities are immune to hits
|
||||
a_EntityHit.StartBurning(5 * 20); // 5 seconds of burning
|
||||
}
|
36
src/Entities/FireChargeEntity.h
Normal file
36
src/Entities/FireChargeEntity.h
Normal file
@ -0,0 +1,36 @@
|
||||
//
|
||||
// FireChargeEntity.h
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ProjectileEntity.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// tolua_begin
|
||||
|
||||
class cFireChargeEntity :
|
||||
public cProjectileEntity
|
||||
{
|
||||
typedef cProjectileEntity super;
|
||||
|
||||
public:
|
||||
|
||||
// tolua_end
|
||||
|
||||
CLASS_PROTODEF(cFireChargeEntity);
|
||||
|
||||
cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
|
||||
|
||||
protected:
|
||||
|
||||
void Explode(int a_BlockX, int a_BlockY, int a_BlockZ);
|
||||
|
||||
// cProjectileEntity overrides:
|
||||
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
|
||||
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
|
||||
|
||||
} ; // tolua_export
|
73
src/Entities/FireworkEntity.cpp
Normal file
73
src/Entities/FireworkEntity.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "FireworkEntity.h"
|
||||
#include "../World.h"
|
||||
#include "../Chunk.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item) :
|
||||
super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25),
|
||||
m_ExplodeTimer(0),
|
||||
m_FireworkItem(a_Item)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
|
||||
{
|
||||
int RelX = POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width;
|
||||
int RelZ = POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width;
|
||||
int PosY = POSY_TOINT;
|
||||
|
||||
if ((PosY < 0) || (PosY >= cChunkDef::Height))
|
||||
{
|
||||
goto setspeed;
|
||||
}
|
||||
|
||||
if (m_IsInGround)
|
||||
{
|
||||
if (a_Chunk.GetBlock(RelX, POSY_TOINT + 1, RelZ) == E_BLOCK_AIR)
|
||||
{
|
||||
m_IsInGround = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a_Chunk.GetBlock(RelX, POSY_TOINT + 1, RelZ) != E_BLOCK_AIR)
|
||||
{
|
||||
OnHitSolidBlock(GetPosition(), BLOCK_FACE_YM);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setspeed:
|
||||
AddSpeedY(1);
|
||||
AddPosition(GetSpeed() * (a_Dt / 1000));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cFireworkEntity::Tick(float a_Dt, cChunk & a_Chunk)
|
||||
{
|
||||
super::Tick(a_Dt, a_Chunk);
|
||||
|
||||
if (m_ExplodeTimer == m_FireworkItem.m_FireworkItem.m_FlightTimeInTicks)
|
||||
{
|
||||
m_World->BroadcastEntityStatus(*this, esFireworkExploding);
|
||||
Destroy();
|
||||
}
|
||||
|
||||
m_ExplodeTimer++;
|
||||
}
|
40
src/Entities/FireworkEntity.h
Normal file
40
src/Entities/FireworkEntity.h
Normal file
@ -0,0 +1,40 @@
|
||||
//
|
||||
// FireworkEntity.h
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ProjectileEntity.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// tolua_begin
|
||||
|
||||
class cFireworkEntity :
|
||||
public cProjectileEntity
|
||||
{
|
||||
typedef cProjectileEntity super;
|
||||
|
||||
public:
|
||||
|
||||
// tolua_end
|
||||
|
||||
CLASS_PROTODEF(cFireworkEntity);
|
||||
|
||||
cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item);
|
||||
const cItem & GetItem(void) const { return m_FireworkItem; }
|
||||
|
||||
protected:
|
||||
|
||||
// cProjectileEntity overrides:
|
||||
virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
|
||||
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
|
||||
|
||||
private:
|
||||
|
||||
int m_ExplodeTimer;
|
||||
cItem m_FireworkItem;
|
||||
|
||||
}; // tolua_export
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user