1
0

Merge remote-tracking branch 'upstream/master'

Conflicts:
	source/Bindings.cpp
	source/Entities/Entity.cpp

Updated from upsteam
This commit is contained in:
Tiger Wang 2013-09-02 14:15:28 +01:00
commit 20b64e18e4
83 changed files with 4923 additions and 1573 deletions

1
.gitignore vendored
View File

@ -11,3 +11,4 @@ cloc.xsl
*.user
*.suo
/EveryNight.cmd
*.sublime-*

2
MCServer/.gitignore vendored
View File

@ -10,7 +10,7 @@ schematics
*.schematic
*.ilk
*.pdb
memdump.xml
memdump*
*.grab
ProtectionAreas.sqlite
helgrind.log

View File

@ -13,8 +13,7 @@ function Initialize(Plugin)
Plugin:SetName("ChatLog")
Plugin:SetVersion(3)
PluginManager = cRoot:Get():GetPluginManager()
PluginManager:AddHook(Plugin, cPluginManager.HOOK_CHAT)
cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChat)
LOG("Initialized " .. Plugin:GetName() .. " v." .. Plugin:GetVersion())
return true

View File

@ -26,8 +26,7 @@ function Initialize(Plugin)
PLUGIN:SetName("ChunkWorx")
PLUGIN:SetVersion(6)
PluginManager = cRoot:Get():GetPluginManager()
PluginManager:AddHook(PLUGIN, cPluginManager.HOOK_TICK)
cPluginManager.AddHook(cPluginManager.HOOK_TICK, OnTick)
Plugin:AddWebTab("(Re)Generation", HandleRequest_Generation)

@ -1 +1 @@
Subproject commit e3a45f34303331be77aceacf2ba53e503ad7284f
Subproject commit c8ef7e9f8ed2bc1ffdbb3756c2024536bf004698

View File

@ -16,15 +16,21 @@ function Initialize(Plugin)
Plugin:SetName("Debuggers")
Plugin:SetVersion(1)
PluginManager = cRoot:Get():GetPluginManager()
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_USING_BLOCK);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_USING_ITEM);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_TAKE_DAMAGE);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_TICK);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_CHAT);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_RIGHT_CLICKING_ENTITY);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_WORLD_TICK);
--[[
-- Test multiple hook handlers:
cPluginManager.AddHook(cPluginManager.HOOK_TICK, OnTick1);
cPluginManager.AddHook(cPluginManager.HOOK_TICK, OnTick2);
--]]
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_USING_BLOCK, OnPlayerUsingBlock);
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_USING_ITEM, OnPlayerUsingItem);
cPluginManager.AddHook(cPluginManager.HOOK_TAKE_DAMAGE, OnTakeDamage);
cPluginManager.AddHook(cPluginManager.HOOK_TICK, OnTick);
cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChat);
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_RIGHT_CLICKING_ENTITY, OnPlayerRightClickingEntity);
cPluginManager.AddHook(cPluginManager.HOOK_WORLD_TICK, OnWorldTick);
PluginManager = cRoot:Get():GetPluginManager();
PluginManager:BindCommand("/le", "debuggers", HandleListEntitiesCmd, "- Shows a list of all the loaded entities");
PluginManager:BindCommand("/ke", "debuggers", HandleKillEntitiesCmd, "- Kills all the loaded entities");
PluginManager:BindCommand("/wool", "debuggers", HandleWoolCmd, "- Sets all your armor to blue wool");
@ -39,6 +45,7 @@ function Initialize(Plugin)
PluginManager:BindCommand("/spidey", "debuggers", HandleSpideyCmd, "- Shoots a line of web blocks until it hits non-air");
PluginManager:BindCommand("/ench", "debuggers", HandleEnchCmd, "- Provides an instant dummy enchantment window");
PluginManager:BindCommand("/fs", "debuggers", HandleFoodStatsCmd, "- Turns regular foodstats message on or off");
PluginManager:BindCommand("/arr", "debuggers", HandleArrowCmd, "- Creates an arrow going away from the player");
-- Enable the following line for BlockArea / Generator interface testing:
-- PluginManager:AddHook(Plugin, cPluginManager.HOOK_CHUNK_GENERATED);
@ -415,6 +422,24 @@ end
function OnTick1()
-- For testing multiple hook handlers per plugin
LOGINFO("Tick1");
end
function OnTick2()
-- For testing multiple hook handlers per plugin
LOGINFO("Tick2");
end
--- When set to a positive number, the following OnTick() will perform GC and decrease until 0 again
GCOnTick = 0;
@ -770,6 +795,7 @@ function HandleEnchCmd(a_Split, a_Player)
Wnd:SetProperty(0, 10);
Wnd:SetProperty(1, 15);
Wnd:SetProperty(2, 25);
return true;
end
@ -778,6 +804,22 @@ end
function HandleFoodStatsCmd(a_Split, a_Player)
g_ShowFoodStats = not(g_ShowFoodStats);
return true;
end
function HandleArrowCmd(a_Split, a_Player)
local World = a_Player:GetWorld();
local Pos = a_Player:GetEyePosition();
local Speed = a_Player:GetLookVector();
Speed:Normalize();
Pos = Pos + Speed;
World:CreateProjectile(Pos.x, Pos.y, Pos.z, cProjectileEntity.pkArrow, a_Player, Speed * 10);
return true;
end

View File

@ -9,7 +9,6 @@
-- Global variables
PLUGIN = {} -- Reference to own plugin object
MOVER_SIZE_X = 4;
MOVER_SIZE_Y = 4;
MOVER_SIZE_Z = 4;
@ -19,13 +18,10 @@ MOVER_SIZE_Z = 4;
function Initialize(Plugin)
PLUGIN = Plugin;
Plugin:SetName("DiamondMover");
Plugin:SetVersion(1);
PluginManager = cRoot:Get():GetPluginManager();
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_USED_ITEM);
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_USED_ITEM, OnPlayerUsedItem);
return true;
end
@ -80,4 +76,8 @@ function OnPlayerUsedItem(Player, BlockX, BlockY, BlockZ, BlockFace, CursorX, Cu
Area:Write(Player:GetWorld(), BlockX - MOVER_SIZE_X, BlockY - MOVER_SIZE_Y, BlockZ - MOVER_SIZE_Z);
return false;
end
end
end

View File

@ -13,47 +13,47 @@ function Initialize(Plugin)
Plugin:SetVersion(1);
PluginManager = cPluginManager:Get();
PluginManager:AddHook(Plugin, cPluginManager.HOOK_BLOCK_TO_PICKUPS);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_CHAT);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_CHUNK_AVAILABLE);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_CHUNK_GENERATED);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_CHUNK_GENERATING);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_CHUNK_UNLOADED);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_CHUNK_UNLOADING);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_COLLECTING_PICKUP);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_CRAFTING_NO_RECIPE);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_DISCONNECT);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_EXECUTE_COMMAND);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_HANDSHAKE);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_KILLING);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_LOGIN);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_BREAKING_BLOCK);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_BROKEN_BLOCK);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_EATING);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_JOINED);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_LEFT_CLICK);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_MOVING);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_PLACED_BLOCK);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_PLACING_BLOCK);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_RIGHT_CLICK);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_SHOOTING);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_SPAWNED);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_TOSSING_ITEM);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_USED_BLOCK);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_USED_ITEM);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_USING_BLOCK);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_USING_ITEM);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_POST_CRAFTING);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PRE_CRAFTING);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_SPAWNED_ENTITY);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_SPAWNED_MONSTER);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_SPAWNING_ENTITY);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_SPAWNING_MONSTER);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_TAKE_DAMAGE);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_UPDATED_SIGN);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_UPDATING_SIGN);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_WEATHER_CHANGED);
PluginManager:AddHook(Plugin, cPluginManager.HOOK_WEATHER_CHANGING);
cPluginManager.AddHook(cPluginManager.HOOK_BLOCK_TO_PICKUPS, OnBlockToPickups);
cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChat);
cPluginManager.AddHook(cPluginManager.HOOK_CHUNK_AVAILABLE, OnChunkAvailable);
cPluginManager.AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated);
cPluginManager.AddHook(cPluginManager.HOOK_CHUNK_GENERATING, OnChunkGenerating);
cPluginManager.AddHook(cPluginManager.HOOK_CHUNK_UNLOADED, OnChunkUnloaded);
cPluginManager.AddHook(cPluginManager.HOOK_CHUNK_UNLOADING, OnChunkUnloading);
cPluginManager.AddHook(cPluginManager.HOOK_COLLECTING_PICKUP, OnCollectingPickup);
cPluginManager.AddHook(cPluginManager.HOOK_CRAFTING_NO_RECIPE, OnCraftingNoRecipe);
cPluginManager.AddHook(cPluginManager.HOOK_DISCONNECT, OnDisconnect);
cPluginManager.AddHook(cPluginManager.HOOK_EXECUTE_COMMAND, OnExecuteCommand);
cPluginManager.AddHook(cPluginManager.HOOK_HANDSHAKE, OnHandshake);
cPluginManager.AddHook(cPluginManager.HOOK_KILLING, OnKilling);
cPluginManager.AddHook(cPluginManager.HOOK_LOGIN, OnLogin);
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_BREAKING_BLOCK, OnPlayerBreakingBlock);
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_BROKEN_BLOCK, OnPlayerBrokenBlock);
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_EATING, OnPlayerEating);
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_JOINED, OnPlayerJoined);
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_LEFT_CLICK, OnPlayerLeftClick);
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_MOVING, OnPlayerMoving);
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_PLACED_BLOCK, OnPlayerPlacedBlock);
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_PLACING_BLOCK, OnPlayerPlacingBlock);
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_RIGHT_CLICK, OnPlayerRightClick);
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_SHOOTING, OnPlayerShooting);
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_SPAWNED, OnPlayerSpawned);
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_TOSSING_ITEM, OnPlayerTossingItem);
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_USED_BLOCK, OnPlayerUsedBlock);
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_USED_ITEM, OnPlayerUsedItem);
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_USING_BLOCK, OnPlayerUsingBlock);
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_USING_ITEM, OnPlayerUsingItem);
cPluginManager.AddHook(cPluginManager.HOOK_POST_CRAFTING, OnPostCrafting);
cPluginManager.AddHook(cPluginManager.HOOK_PRE_CRAFTING, OnPreCrafting);
cPluginManager.AddHook(cPluginManager.HOOK_SPAWNED_ENTITY, OnSpawnedEntity);
cPluginManager.AddHook(cPluginManager.HOOK_SPAWNED_MONSTER, OnSpawnedMonster);
cPluginManager.AddHook(cPluginManager.HOOK_SPAWNING_ENTITY, OnSpawningEntity);
cPluginManager.AddHook(cPluginManager.HOOK_SPAWNING_MONSTER, OnSpawningMonster);
cPluginManager.AddHook(cPluginManager.HOOK_TAKE_DAMAGE, OnTakeDamage);
cPluginManager.AddHook(cPluginManager.HOOK_UPDATED_SIGN, OnUpdatedSign);
cPluginManager.AddHook(cPluginManager.HOOK_UPDATING_SIGN, OnUpdatingSign);
cPluginManager.AddHook(cPluginManager.HOOK_WEATHER_CHANGED, OnWeatherChanged);
cPluginManager.AddHook(cPluginManager.HOOK_WEATHER_CHANGING, OnWeatherChanging);
LOGINFO("HookNotify plugin is installed, beware, the log output may be quite large!");
LOGINFO("You want this plugin enabled only when developing another plugin, not for regular gameplay.");

View File

@ -7,9 +7,8 @@ function Initialize( Plugin )
Plugin:SetName( "MagicCarpet" )
Plugin:SetVersion( 1 )
PluginManager = cRoot:Get():GetPluginManager()
PluginManager:AddHook(Plugin, cPluginManager.HOOK_PLAYER_MOVING)
PluginManager:AddHook(Plugin, cPluginManager.HOOK_DISCONNECT)
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_MOVING, OnPlayerMoving)
cPluginManager.AddHook(cPluginManager.HOOK_DISCONNECT, OnDisconnect)
PluginManager:BindCommand("/mc", "magiccarpet", HandleCarpetCommand, " - Spawns a magical carpet");

@ -1 +1 @@
Subproject commit 3019c7b396221b987cd3f89d422276f764834ffe
Subproject commit bef8ff2a883e98db94f842f9db3d256a039b1fcd

View File

@ -1,5 +1,9 @@
.xls
Statistics.txt
*.bmp
Debug/
Release/
Profiling
*.png
world/
*.html

View File

@ -8,6 +8,7 @@
#include "Statistics.h"
#include "BiomeMap.h"
#include "HeightMap.h"
#include "HeightBiomeMap.h"
#include "ChunkExtract.h"
#include "SpringStats.h"
@ -26,6 +27,7 @@ int main(int argc, char * argv[])
LOG(" 2 - height map");
LOG(" 3 - extract chunks");
LOG(" 4 - count lava- and water- springs");
LOG(" 5 - biome and height map");
LOG("\nNo method number present, aborting.");
return -1;
}
@ -48,6 +50,7 @@ int main(int argc, char * argv[])
case 2: Factory = new cHeightMapFactory; break;
case 3: Factory = new cChunkExtractFactory(WorldFolder); break;
case 4: Factory = new cSpringStatsFactory; break;
case 5: Factory = new cHeightBiomeMapFactory; break;
default:
{
LOG("Unknown method \"%s\", aborting.", argv[1]);

View File

@ -15,6 +15,7 @@ Possible usage:
- count the per-chunk density of specific blocks
- count the per-chunk density of dungeons, by measuring the number of zombie/skeleton/regularspider spawners
- count the per-chunk-per-biome density of trees, by measuring the number of dirt-log vertical transitions, correlating to biome data
- draw a vertical map of the world based on a specific measured value (biome, elevation, ...)
This project is Windows-only, although it shouldn't be too difficult to make it portable.

View File

@ -313,6 +313,14 @@
RelativePath=".\Globals.h"
>
</File>
<File
RelativePath=".\HeightBiomeMap.cpp"
>
</File>
<File
RelativePath=".\HeightBiomeMap.h"
>
</File>
<File
RelativePath=".\HeightMap.cpp"
>
@ -321,6 +329,14 @@
RelativePath=".\HeightMap.h"
>
</File>
<File
RelativePath=".\ImageComposingCallback.cpp"
>
</File>
<File
RelativePath=".\ImageComposingCallback.h"
>
</File>
<File
RelativePath=".\Processor.cpp"
>

View File

@ -22,44 +22,53 @@ class cParsedNBT;
/** The base class for all chunk-processor callbacks, declares the interface.
The processor calls each virtual function in the order they are declared here with the specified args.
If the function returns true, the processor moves on to next chunk and starts calling the callbacks again from start with
the new chunk data.
If the function returns true, the processor doesn't process the data item, moves on to the next chunk
and starts calling the callbacks again from start with the new chunk data.
So if a statistics collector doesn't need data decompression at all, it can stop the processor from doing so early-enough
and still get meaningful data.
A callback is guaranteed to run in a single thread and always the same thread.
A callback is guaranteed to run in a single thread and always the same thread for the same chunk.
A callback is guaranteed to run on all chunks in a region and one region is guaranteed to be handled by only callback.
*/
class cCallback abstract
{
public:
enum
{
CALLBACK_CONTINUE = false,
CALLBACK_ABORT = true,
} ;
virtual ~cCallback() {} // Force a virtual destructor in each descendant
/// Called when a new region file is about to be opened; by default allow the region
virtual bool OnNewRegion(int a_RegionX, int a_RegionZ) { return CALLBACK_CONTINUE; }
/// Called to inform the stats module of the chunk coords for newly processing chunk
virtual bool OnNewChunk(int a_ChunkX, int a_ChunkZ) = 0;
/// Called to inform about the chunk's data offset in the file (chunk mini-header), the number of sectors it uses and the timestamp field value
virtual bool OnHeader(int a_FileOffset, unsigned char a_NumSectors, int a_Timestamp) { return true; }
virtual bool OnHeader(int a_FileOffset, unsigned char a_NumSectors, int a_Timestamp) { return CALLBACK_ABORT; }
/// Called to inform of the compressed chunk data size and position in the file (offset from file start to the actual data)
virtual bool OnCompressedDataSizePos(int a_CompressedDataSize, int a_DataOffset, char a_CompressionMethod) { return true; }
virtual bool OnCompressedDataSizePos(int a_CompressedDataSize, int a_DataOffset, char a_CompressionMethod) { return CALLBACK_ABORT; }
/// Just in case you wanted to process the NBT yourself ;)
virtual bool OnDecompressedData(const char * a_DecompressedNBT, int a_DataSize) { return true; }
virtual bool OnDecompressedData(const char * a_DecompressedNBT, int a_DataSize) { return CALLBACK_ABORT; }
/// The chunk's NBT should specify chunk coords, these are sent here:
virtual bool OnRealCoords(int a_ChunkX, int a_ChunkZ) { return true; }
virtual bool OnRealCoords(int a_ChunkX, int a_ChunkZ) { return CALLBACK_ABORT; }
/// The chunk contains a LastUpdate value specifying the last tick in which it was saved.
virtual bool OnLastUpdate(Int64 a_LastUpdate) { return true; }
virtual bool OnLastUpdate(Int64 a_LastUpdate) { return CALLBACK_ABORT; }
virtual bool OnTerrainPopulated(bool a_Populated) { return true; }
virtual bool OnTerrainPopulated(bool a_Populated) { return CALLBACK_ABORT; }
virtual bool OnBiomes(const unsigned char * a_BiomeData) { return true; }
virtual bool OnBiomes(const unsigned char * a_BiomeData) { return CALLBACK_ABORT; }
/** Called when a heightmap for the chunk is read from the file.
Note that the heightmap is given in big-endian ints, so if you want it, you need to ntohl() it first!
*/
virtual bool OnHeightMap(const int * a_HeightMapBE) { return true; }
virtual bool OnHeightMap(const int * a_HeightMapBE) { return CALLBACK_ABORT; }
/** If there is data for the section, this callback is called; otherwise OnEmptySection() is called instead.
All OnSection() callbacks are called first, and only then all the remaining sections are reported in OnEmptySection().
@ -71,16 +80,16 @@ public:
const NIBBLETYPE * a_BlockMeta,
const NIBBLETYPE * a_BlockLight,
const NIBBLETYPE * a_BlockSkyLight
) { return true; }
) { return CALLBACK_ABORT; }
/** If there is no data for a section, this callback is called; otherwise OnSection() is called instead.
OnEmptySection() callbacks are called after all OnSection() callbacks.
*/
virtual bool OnEmptySection(unsigned char a_Y) { return false; }
virtual bool OnEmptySection(unsigned char a_Y) { return CALLBACK_CONTINUE; }
/** Called after all sections have been processed via either OnSection() or OnEmptySection().
*/
virtual bool OnSectionsFinished(void) { return true; }
virtual bool OnSectionsFinished(void) { return CALLBACK_ABORT; }
/** Called for each entity in the chunk.
Common parameters are parsed from the NBT.
@ -98,7 +107,7 @@ public:
char a_IsOnGround,
cParsedNBT & a_NBT,
int a_NBTTag
) { return true; }
) { return CALLBACK_ABORT; }
/** Called for each tile entity in the chunk.
Common parameters are parsed from the NBT.
@ -110,14 +119,17 @@ public:
int a_PosX, int a_PosY, int a_PosZ,
cParsedNBT & a_NBT,
int a_NBTTag
) { return true; }
) { return CALLBACK_ABORT; }
/// Called for each tile tick in the chunk
virtual bool OnTileTick(
int a_BlockType,
int a_TicksLeft,
int a_PosX, int a_PosY, int a_PosZ
) { return true; }
) { return CALLBACK_ABORT; }
/// Called after the entire region file has been processed. No more callbacks for this region will be called. No processing by default
virtual void OnRegionFinished(int a_RegionX, int a_RegionZ) {}
} ;
typedef std::vector<cCallback *> cCallbacks;

View File

@ -0,0 +1,230 @@
// HeightBiomeMap.cpp
// Declares the cHeightBiomeMap class representing a stats module that produces an image of heights and biomes combined
#include "Globals.h"
#include "HeightBiomeMap.h"
#include "HeightMap.h"
cHeightBiomeMap::cHeightBiomeMap(void) :
super("HeBi"),
m_MinRegionX(100000),
m_MaxRegionX(-100000),
m_MinRegionZ(100000),
m_MaxRegionZ(-100000)
{
}
bool cHeightBiomeMap::OnNewRegion(int a_RegionX, int a_RegionZ)
{
if (a_RegionX < m_MinRegionX)
{
m_MinRegionX = a_RegionX;
}
if (a_RegionX > m_MaxRegionX)
{
m_MaxRegionX = a_RegionX;
}
if (a_RegionZ < m_MinRegionZ)
{
m_MinRegionZ = a_RegionZ;
}
if (a_RegionZ > m_MaxRegionZ)
{
m_MaxRegionZ = a_RegionZ;
}
return super::OnNewRegion(a_RegionX, a_RegionZ);
}
bool cHeightBiomeMap::OnNewChunk(int a_ChunkX, int a_ChunkZ)
{
m_CurrentChunkX = a_ChunkX;
m_CurrentChunkZ = a_ChunkZ;
m_CurrentChunkRelX = m_CurrentChunkX - m_CurrentRegionX * 32;
m_CurrentChunkRelZ = m_CurrentChunkZ - m_CurrentRegionZ * 32;
ASSERT((m_CurrentChunkRelX >= 0) && (m_CurrentChunkRelX < 32));
ASSERT((m_CurrentChunkRelZ >= 0) && (m_CurrentChunkRelZ < 32));
memset(m_BlockTypes, 0, sizeof(m_BlockTypes));
return CALLBACK_CONTINUE;
}
bool cHeightBiomeMap::OnBiomes(const unsigned char * a_BiomeData)
{
memcpy(m_ChunkBiomes, a_BiomeData, sizeof(m_ChunkBiomes));
return CALLBACK_CONTINUE;
}
bool cHeightBiomeMap::OnHeightMap(const int * a_HeightMapBE)
{
for (int i = 0; i < ARRAYCOUNT(m_ChunkHeight); i++)
{
m_ChunkHeight[i] = ntohl(a_HeightMapBE[i]);
} // for i - m_ChunkHeight
return CALLBACK_CONTINUE;
}
bool cHeightBiomeMap::OnSection(
unsigned char a_Y,
const BLOCKTYPE * a_BlockTypes,
const NIBBLETYPE * a_BlockAdditional,
const NIBBLETYPE * a_BlockMeta,
const NIBBLETYPE * a_BlockLight,
const NIBBLETYPE * a_BlockSkyLight
)
{
// Copy the section data into the appropriate place in the internal buffer
memcpy(m_BlockTypes + a_Y * 16 * 16 * 16, a_BlockTypes, 16 * 16 * 16);
return CALLBACK_CONTINUE;
}
bool cHeightBiomeMap::OnSectionsFinished(void)
{
static const int BiomePalette[] =
{
// ARGB:
0xff0000ff, /* Ocean */
0xff00cf3f, /* Plains */
0xffffff00, /* Desert */
0xff7f7f7f, /* Extreme Hills */
0xff00cf00, /* Forest */
0xff007f3f, /* Taiga */
0xff3f7f00, /* Swampland */
0xff003fff, /* River */
0xff7f0000, /* Hell */
0xff007fff, /* Sky */
0xff3f3fff, /* Frozen Ocean */
0xff3f3fff, /* Frozen River */
0xff7fffcf, /* Ice Plains */
0xff3fcf7f, /* Ice Mountains */
0xffcf00cf, /* Mushroom Island */
0xff7f00ff, /* Mushroom Island Shore */
0xffffff3f, /* Beach */
0xffcfcf00, /* Desert Hills */
0xff00cf3f, /* Forest Hills */
0xff006f1f, /* Taiga Hills */
0xff7f8f7f, /* Extreme Hills Edge */
0xff004f00, /* Jungle */
0xff003f00, /* Jungle Hills */
} ;
// Remove trees and other unwanted stuff from the heightmap:
for (int z = 0; z < 16; z++)
{
int PixelLine[16]; // line of 16 pixels that is used as a buffer for setting the image pixels
for (int x = 0; x < 16; x++)
{
int Height = m_ChunkHeight[16 * z + x];
for (int y = Height; y >= 0; y--)
{
if (cHeightMap::IsGround(m_BlockTypes[256 * y + 16 * z + x]))
{
Height = y;
break; // for y
}
} // for y
// Set the color based on the biome and height:
char Biome = m_ChunkBiomes[16 * z + x];
PixelLine[x] = ShadeColor(BiomePalette[Biome], Height);
} // for x
// Set the pixelline into the image:
SetPixelURow(m_CurrentChunkRelX * 16, m_CurrentChunkRelZ * 16 + z, 16, PixelLine);
} // for z
return CALLBACK_ABORT;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cHeightBiomeMapFactory:
cHeightBiomeMapFactory::~cHeightBiomeMapFactory()
{
// Get the min and max region coords:
int MinRegionX = 100000;
int MaxRegionX = -100000;
int MinRegionZ = 100000;
int MaxRegionZ = -100000;
for (cCallbacks::iterator itr = m_Callbacks.begin(), end = m_Callbacks.end(); itr != end; ++itr)
{
cHeightBiomeMap * cb = (cHeightBiomeMap *)(*itr);
if (cb->m_MinRegionX < MinRegionX)
{
MinRegionX = cb->m_MinRegionX;
}
if (cb->m_MaxRegionX > MaxRegionX)
{
MaxRegionX = cb->m_MaxRegionX;
}
if (cb->m_MinRegionZ < MinRegionZ)
{
MinRegionZ = cb->m_MinRegionZ;
}
if (cb->m_MaxRegionZ > MaxRegionZ)
{
MaxRegionZ = cb->m_MaxRegionZ;
}
}
// If the size is small enough, write an HTML file referencing all the images in a table:
if ((MaxRegionX >= MinRegionX) && (MaxRegionZ >= MinRegionZ) && (MaxRegionX - MinRegionX < 100) && (MaxRegionZ - MinRegionZ < 100))
{
cFile HTML("HeBi.html", cFile::fmWrite);
if (HTML.IsOpen())
{
HTML.Printf("<html><body><table cellspacing=0 cellpadding=0>\n");
for (int z = MinRegionZ; z <= MaxRegionZ; z++)
{
HTML.Printf("<tr>");
for (int x = MinRegionX; x <= MaxRegionX; x++)
{
HTML.Printf("<td><img src=\"HeBi.%d.%d.bmp\" /></td>", x, z);
}
HTML.Printf("</tr>\n");
}
HTML.Printf("</table></body></html>");
}
}
}

View File

@ -0,0 +1,81 @@
// HeightBiomeMap.h
// Declares the cHeightBiomeMap class representing a stats module that produces an image of heights and biomes combined
#pragma once
#include "ImageComposingCallback.h"
class cHeightBiomeMap :
public cImageComposingCallback
{
typedef cImageComposingCallback super;
public:
// Minima and maxima for the regions processed through this callback
int m_MinRegionX, m_MaxRegionX;
int m_MinRegionZ, m_MaxRegionZ;
cHeightBiomeMap(void);
protected:
int m_CurrentChunkX; // Absolute chunk coords
int m_CurrentChunkZ;
int m_CurrentChunkRelX; // Chunk offset from the start of the region
int m_CurrentChunkRelZ;
char m_ChunkBiomes[16 * 16]; ///< Biome-map for the current chunk
int m_ChunkHeight[16 * 16]; ///< Height-map for the current chunk
BLOCKTYPE m_BlockTypes [16 * 16 * 256]; ///< Block data for the current chunk (between OnSection() and OnSectionsFinished() )
// cCallback overrides:
virtual bool OnNewRegion(int a_RegionX, int a_RegionZ) override;
virtual bool OnNewChunk(int a_ChunkX, int a_ChunkZ) override;
virtual bool OnHeader(int a_FileOffset, unsigned char a_NumSectors, int a_Timestamp) override { return CALLBACK_CONTINUE; }
virtual bool OnCompressedDataSizePos(int a_CompressedDataSize, int a_DataOffset, char a_CompressionMethod) override { return CALLBACK_CONTINUE; }
virtual bool OnDecompressedData(const char * a_DecompressedNBT, int a_DataSize) override { return CALLBACK_CONTINUE; }
virtual bool OnRealCoords(int a_ChunkX, int a_ChunkZ) override { return CALLBACK_CONTINUE; }
virtual bool OnLastUpdate(Int64 a_LastUpdate) override { return CALLBACK_CONTINUE; }
virtual bool OnTerrainPopulated(bool a_Populated) override { return a_Populated ? CALLBACK_CONTINUE : CALLBACK_ABORT; } // If not populated, we don't want it!
virtual bool OnBiomes(const unsigned char * a_BiomeData) override;
virtual bool OnHeightMap(const int * a_HeightMapBE) override;
virtual bool OnSection(
unsigned char a_Y,
const BLOCKTYPE * a_BlockTypes,
const NIBBLETYPE * a_BlockAdditional,
const NIBBLETYPE * a_BlockMeta,
const NIBBLETYPE * a_BlockLight,
const NIBBLETYPE * a_BlockSkyLight
) override;
virtual bool OnSectionsFinished(void) override;
} ;
class cHeightBiomeMapFactory :
public cCallbackFactory
{
public:
virtual ~cHeightBiomeMapFactory();
virtual cCallback * CreateNewCallback(void) override
{
return new cHeightBiomeMap;
}
} ;

View File

@ -23,6 +23,8 @@ public:
void Finish(void);
static bool IsGround(BLOCKTYPE a_BlockType);
protected:
int m_CurrentChunkX; // Absolute chunk coords
int m_CurrentChunkZ;
@ -55,8 +57,6 @@ protected:
virtual bool OnSectionsFinished(void) override;
void StartNewRegion(int a_RegionX, int a_RegionZ);
static bool IsGround(BLOCKTYPE a_BlockType);
} ;

View File

@ -0,0 +1,219 @@
// ImageComposingCallback.cpp
// Implements the cImageComposingCallback class that implements a subset of cCallback for composing per-region images
#include "Globals.h"
#include "ImageComposingCallback.h"
cImageComposingCallback::cImageComposingCallback(const AString & a_FileNamePrefix) :
m_FileNamePrefix(a_FileNamePrefix),
m_CurrentRegionX(INVALID_REGION_COORD),
m_CurrentRegionZ(INVALID_REGION_COORD),
m_ImageData(new int[32 * 16 * 32 * 16])
{
}
cImageComposingCallback::~cImageComposingCallback()
{
delete[] m_ImageData;
}
bool cImageComposingCallback::OnNewRegion(int a_RegionX, int a_RegionZ)
{
ASSERT(m_CurrentRegionX == INVALID_REGION_COORD);
ASSERT(m_CurrentRegionZ == INVALID_REGION_COORD); // Has any previous region been finished properly?
m_CurrentRegionX = a_RegionX;
m_CurrentRegionZ = a_RegionZ;
OnEraseImage();
return CALLBACK_CONTINUE;
}
void cImageComposingCallback::OnRegionFinished(int a_RegionX, int a_RegionZ)
{
ASSERT(m_CurrentRegionX != INVALID_REGION_COORD);
ASSERT(m_CurrentRegionZ != INVALID_REGION_COORD); // Has a region been started properly?
ASSERT(m_CurrentRegionX == a_RegionX);
ASSERT(m_CurrentRegionZ == a_RegionZ); // Is it the same region that has been started?
AString FileName = GetFileName(a_RegionX, a_RegionZ);
if (!FileName.empty())
{
OnBeforeImageSaved(a_RegionX, a_RegionZ, FileName);
SaveImage(FileName);
OnAfterImageSaved(a_RegionX, a_RegionZ, FileName);
}
m_CurrentRegionX = INVALID_REGION_COORD;
m_CurrentRegionZ = INVALID_REGION_COORD;
}
AString cImageComposingCallback::GetFileName(int a_RegionX, int a_RegionZ)
{
return Printf("%s.%d.%d.bmp", m_FileNamePrefix.c_str(), a_RegionX, a_RegionZ);
}
void cImageComposingCallback::OnEraseImage(void)
{
// By default erase the image to black:
EraseImage(0);
}
void cImageComposingCallback::EraseImage(int a_Color)
{
for (int i = 0; i < PIXEL_COUNT; i++)
{
m_ImageData[i] = a_Color;
}
}
void cImageComposingCallback::EraseChunk(int a_Color, int a_RelChunkX, int a_RelChunkZ)
{
int Base = a_RelChunkZ * IMAGE_HEIGHT + a_RelChunkX * 16;
for (int v = 0; v < 16; v++)
{
int BaseV = Base + v * IMAGE_HEIGHT;
for (int u = 0; u < 16; u++)
{
m_ImageData[BaseV + u] = a_Color;
}
} // for y
}
void cImageComposingCallback::SetPixel(int a_RelU, int a_RelV, int a_Color)
{
ASSERT((a_RelU >= 0) && (a_RelU < IMAGE_WIDTH));
ASSERT((a_RelV >= 0) && (a_RelV < IMAGE_HEIGHT));
m_ImageData[a_RelU + IMAGE_WIDTH * a_RelV] = a_Color;
}
int cImageComposingCallback::GetPixel(int a_RelU, int a_RelV)
{
if ((a_RelU < 0) || (a_RelU >= IMAGE_WIDTH) || (a_RelV < 0) || (a_RelV >= IMAGE_HEIGHT))
{
// Outside the image data
return -1;
}
return m_ImageData[a_RelU + IMAGE_WIDTH * a_RelV];
}
void cImageComposingCallback::SetPixelURow(int a_RelUStart, int a_RelV, int a_CountU, int * a_Pixels)
{
ASSERT((a_RelUStart >= 0) && (a_RelUStart + a_CountU <= IMAGE_WIDTH));
ASSERT((a_RelV >= 0) && (a_RelV < IMAGE_HEIGHT));
ASSERT(a_Pixels != NULL);
int Base = a_RelUStart + a_RelV * IMAGE_WIDTH;
for (int u = 0; u < a_CountU; u++)
{
m_ImageData[Base + u] = a_Pixels[u];
}
}
int cImageComposingCallback::ShadeColor(int a_Color, int a_Shade)
{
if (a_Shade < 64)
{
return MixColor(0, a_Color, a_Shade * 4);
}
return MixColor(a_Color, 0xffffff, (a_Shade - 64) * 4);
}
int cImageComposingCallback::MixColor(int a_Src, int a_Dest, int a_Amount)
{
int r = a_Src & 0xff;
int g = (a_Src >> 8) & 0xff;
int b = (a_Src >> 16) & 0xff;
int rd = a_Dest & 0xff;
int gd = (a_Dest >> 8) & 0xff;
int bd = (a_Dest >> 16) & 0xff;
int nr = r + (rd - r) * a_Amount / 256;
int ng = g + (gd - g) * a_Amount / 256;
int nb = b + (bd - b) * a_Amount / 256;
return nr | (ng << 8) | (nb << 16);
}
void cImageComposingCallback::SaveImage(const AString & a_FileName)
{
cFile f(a_FileName, cFile::fmWrite);
if (!f.IsOpen())
{
return;
}
// Header for BMP files (is the same for the same-size files)
static const unsigned char BMPHeader[] =
{
0x42, 0x4D, 0x36, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
} ;
f.Write(BMPHeader, sizeof(BMPHeader));
f.Write(m_ImageData, PIXEL_COUNT * 4);
}

View File

@ -0,0 +1,105 @@
// ImageComposingCallback
// Declares the cImageComposingCallback class that implements a subset of cCallback for composing per-region images
#pragma once
#include "Callback.h"
/** Implements the plumbing for composing per-region images from multiple chunks.
To use this class, create a descendant that writes the image data using
SetPixel() or SetPixelURow() functions.
For the purpose of this class the image data is indexed U (horz) * V (vert), to avoid confusion with other coords.
The image is a 32bpp raw imagedata, written into a BMP file.
*/
class cImageComposingCallback :
public cCallback
{
public:
enum
{
INVALID_REGION_COORD = 99999, ///< Used for un-assigned region coords
IMAGE_WIDTH = 32 * 16,
IMAGE_HEIGHT = 32 * 16,
PIXEL_COUNT = IMAGE_WIDTH * IMAGE_HEIGHT, ///< Total pixel count of the image data
} ;
cImageComposingCallback(const AString & a_FileNamePrefix);
virtual ~cImageComposingCallback();
// cCallback overrides:
virtual bool OnNewRegion(int a_RegionX, int a_RegionZ) override;
virtual void OnRegionFinished(int a_RegionX, int a_RegionZ) override;
// New introduced overridable functions:
/// Called when a file is about to be saved, to generate the filename
virtual AString GetFileName(int a_RegionX, int a_RegionZ);
/// Called before the file is saved
virtual void OnBeforeImageSaved(int a_RegionX, int a_RegionZ, const AString & a_FileName) {}
/// Called after the image is saved to a file
virtual void OnAfterImageSaved(int a_RegionX, int a_RegionZ, const AString & a_FileName) {}
/// Called when a new region is beginning, to erase the image data
virtual void OnEraseImage(void);
// Functions for manipulating the image:
/// Erases the entire image with the specified color
void EraseImage(int a_Color);
/// Erases the specified chunk's portion of the image with the specified color. Note that chunk coords are relative to the current region
void EraseChunk(int a_Color, int a_RelChunkX, int a_RelChunkZ);
/// Returns the current region X coord
int GetCurrentRegionX(void) const { return m_CurrentRegionX; }
/// Returns the current region Z coord
int GetCurrentRegionZ(void) const { return m_CurrentRegionZ; }
/// Sets the pixel at the specified UV coords to the specified color
void SetPixel(int a_RelU, int a_RelV, int a_Color);
/// Returns the color of the pixel at the specified UV coords; -1 if outside
int GetPixel(int a_RelU, int a_RelV);
/// Sets a row of pixels. a_Pixels is expected to be a_CountU pixels wide. a_RelUStart + a_CountU is assumed less than image width
void SetPixelURow(int a_RelUStart, int a_RelV, int a_CountU, int * a_Pixels);
/** "Shades" the given color based on the shade amount given
Shade amount 0 .. 63 shades the color from black to a_Color.
Shade amount 64 .. 127 shades the color from a_Color to white.
All other shade amounts have undefined results.
*/
static int ShadeColor(int a_Color, int a_Shade);
/// Mixes the two colors in the specified ratio; a_Ratio is between 0 and 256, 0 returning a_Src
static int MixColor(int a_Src, int a_Dest, int a_Ratio);
protected:
/// Prefix for the filenames, when generated by the default GetFileName() function
AString m_FileNamePrefix;
/// Coords of the currently processed region
int m_CurrentRegionX, m_CurrentRegionZ;
/// Raw image data; 1 MiB worth of data, therefore unsuitable for stack allocation. [u + IMAGE_WIDTH * v]
int * m_ImageData;
void SaveImage(const AString & a_FileName);
} ;

View File

@ -76,6 +76,12 @@ void cProcessor::cThread::ProcessFile(const AString & a_FileName)
return;
}
if (m_Callback.OnNewRegion(RegionX, RegionZ))
{
// Callback doesn't want the region file processed
return;
}
cFile f;
if (!f.Open(a_FileName, cFile::fmRead))
{
@ -92,6 +98,8 @@ void cProcessor::cThread::ProcessFile(const AString & a_FileName)
}
ProcessFileData(FileContents.data(), FileContents.size(), RegionX * 32, RegionZ * 32);
m_Callback.OnRegionFinished(RegionX, RegionZ);
}

View File

@ -201,6 +201,9 @@ enum
PACKET_ENCRYPTION_KEY_REQUEST = 0xfd,
PACKET_PING = 0xfe,
PACKET_KICK = 0xff,
// Synonyms:
PACKET_DISCONNECT = PACKET_KICK,
} ;
@ -238,7 +241,7 @@ cConnection::cConnection(SOCKET a_ClientSocket, cServer & a_Server) :
m_Server(a_Server),
m_ClientSocket(a_ClientSocket),
m_ServerSocket(-1),
m_BeginTick(clock()),
m_BeginTick(m_Timer.GetNowTime()),
m_ClientState(csUnencrypted),
m_ServerState(csUnencrypted),
m_Nonce(0),
@ -475,8 +478,7 @@ bool cConnection::RelayFromClient(void)
double cConnection::GetRelativeTime(void)
{
return (double)(clock() - m_BeginTick) / CLOCKS_PER_SEC;
return (double)(m_Timer.GetNowTime() - m_BeginTick) / 1000;
}
@ -558,9 +560,9 @@ bool cConnection::DecodeClientsPackets(const char * a_Data, int a_Size)
while (m_ClientBuffer.CanReadBytes(1))
{
Log("Decoding client's packets, there are now %d bytes in the queue", m_ClientBuffer.GetReadableSpace());
unsigned char PacketType;
m_ClientBuffer.ReadByte(PacketType);
Log("Decoding client's packets, there are now %d bytes in the queue; next packet is 0x%02x", m_ClientBuffer.GetReadableSpace(), PacketType);
switch (PacketType)
{
case PACKET_BLOCK_DIG: HANDLE_CLIENT_READ(HandleClientBlockDig); break;
@ -568,6 +570,7 @@ bool cConnection::DecodeClientsPackets(const char * a_Data, int a_Size)
case PACKET_CHAT_MESSAGE: HANDLE_CLIENT_READ(HandleClientChatMessage); break;
case PACKET_CLIENT_STATUSES: HANDLE_CLIENT_READ(HandleClientClientStatuses); break;
case PACKET_CREATIVE_INVENTORY_ACTION: HANDLE_CLIENT_READ(HandleClientCreativeInventoryAction); break;
case PACKET_DISCONNECT: HANDLE_CLIENT_READ(HandleClientDisconnect); break;
case PACKET_ENCRYPTION_KEY_RESPONSE: HANDLE_CLIENT_READ(HandleClientEncryptionKeyResponse); break;
case PACKET_ENTITY_ACTION: HANDLE_CLIENT_READ(HandleClientEntityAction); break;
case PACKET_HANDSHAKE: HANDLE_CLIENT_READ(HandleClientHandshake); break;
@ -644,7 +647,7 @@ bool cConnection::DecodeServersPackets(const char * a_Data, int a_Size)
{
unsigned char PacketType;
m_ServerBuffer.ReadByte(PacketType);
Log("Decoding server's packets, there are now %d bytes in the queue; next packet is 0x%x", m_ServerBuffer.GetReadableSpace(), PacketType);
Log("Decoding server's packets, there are now %d bytes in the queue; next packet is 0x%02x", m_ServerBuffer.GetReadableSpace(), PacketType);
LogFlush();
switch (PacketType)
{
@ -843,6 +846,19 @@ bool cConnection::HandleClientCreativeInventoryAction(void)
bool cConnection::HandleClientDisconnect(void)
{
HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Reason);
Log("Received a PACKET_DISCONNECT from the client:");
Log(" Reason = \"%s\"", Reason.c_str());
COPY_TO_SERVER();
return true;
}
bool cConnection::HandleClientEncryptionKeyResponse(void)
{
HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, EncKeyLength);
@ -1704,16 +1720,45 @@ bool cConnection::HandleServerMapChunkBulk(void)
{
return false;
}
AString Meta;
if (!m_ServerBuffer.ReadString(Meta, ChunkCount * 12))
// Read individual chunk metas.
// Need to read them first and only then start logging (in case we don't have the full packet yet)
struct sChunkMeta
{
return false;
int m_ChunkX, m_ChunkZ;
short m_PrimaryBitmap;
short m_AddBitmap;
sChunkMeta(int a_ChunkX, int a_ChunkZ, short a_PrimaryBitmap, short a_AddBitmap) :
m_ChunkX(a_ChunkX), m_ChunkZ(a_ChunkZ), m_PrimaryBitmap(a_PrimaryBitmap), m_AddBitmap(a_AddBitmap)
{
}
} ;
typedef std::vector<sChunkMeta> sChunkMetas;
sChunkMetas ChunkMetas;
ChunkMetas.reserve(ChunkCount);
for (short i = 0; i < ChunkCount; i++)
{
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkX);
HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkZ);
HANDLE_SERVER_PACKET_READ(ReadBEShort, short, PrimaryBitmap);
HANDLE_SERVER_PACKET_READ(ReadBEShort, short, AddBitmap);
ChunkMetas.push_back(sChunkMeta(ChunkX, ChunkZ, PrimaryBitmap, AddBitmap));
}
Log("Received a PACKET_MAP_CHUNK_BULK from the server:");
Log(" ChunkCount = %d", ChunkCount);
Log(" Compressed size = %d (0x%x)", CompressedSize, CompressedSize);
Log(" IsSkyLightSent = %s", IsSkyLightSent ? "true" : "false");
// Log individual chunk coords:
int idx = 0;
for (sChunkMetas::iterator itr = ChunkMetas.begin(), end = ChunkMetas.end(); itr != end; ++itr, ++idx)
{
Log(" [%d]: [%d, %d], primary bitmap 0x%02x, add bitmap 0x%02x",
idx, itr->m_ChunkX, itr->m_ChunkZ, itr->m_PrimaryBitmap, itr->m_AddBitmap
);
} // for itr - ChunkMetas[]
// TODO: Save the compressed data into a file for later analysis
COPY_TO_CLIENT();

View File

@ -9,8 +9,8 @@
#pragma once
#include <time.h>
#include "ByteBuffer.h"
#include "../../source/OSSupport/Timer.h"
@ -35,7 +35,8 @@ class cConnection
SOCKET m_ClientSocket;
SOCKET m_ServerSocket;
clock_t m_BeginTick; // Tick when the relative time was first retrieved (used for GetRelativeTime())
cTimer m_Timer;
long long m_BeginTick; // Tick when the relative time was first retrieved (used for GetRelativeTime())
enum eConnectionState
{
@ -115,6 +116,7 @@ protected:
bool HandleClientChatMessage(void);
bool HandleClientClientStatuses(void);
bool HandleClientCreativeInventoryAction(void);
bool HandleClientDisconnect(void);
bool HandleClientEncryptionKeyResponse(void);
bool HandleClientEntityAction(void);
bool HandleClientHandshake(void);

View File

@ -143,6 +143,7 @@ typedef short Int16;
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
#include <time.h>

View File

@ -256,6 +256,14 @@
RelativePath="..\..\source\StringUtils.h"
>
</File>
<File
RelativePath="..\..\source\OSSupport\Timer.cpp"
>
</File>
<File
RelativePath="..\..\source\OSSupport\Timer.h"
>
</File>
</Filter>
<File
RelativePath=".\ProtoProxy.txt"

View File

@ -1159,6 +1159,14 @@
RelativePath="..\source\Entities\Player.h"
>
</File>
<File
RelativePath="..\source\Entities\ProjectileEntity.cpp"
>
</File>
<File
RelativePath="..\source\Entities\ProjectileEntity.h"
>
</File>
<File
RelativePath="..\source\Entities\TNTEntity.cpp"
>
@ -2015,10 +2023,6 @@
RelativePath="..\source\Blocks\BlockDeadBush.h"
>
</File>
<File
RelativePath="..\source\Blocks\BlockDoubleSlab.h"
>
</File>
<File
RelativePath="..\source\blocks\BlockDirt.h"
>
@ -2235,6 +2239,10 @@
RelativePath="..\source\items\ItemBed.h"
>
</File>
<File
RelativePath="..\source\Items\ItemBow.h"
>
</File>
<File
RelativePath="..\source\Items\ItemBrewingStand.h"
>
@ -2339,6 +2347,10 @@
RelativePath="..\source\items\ItemSword.h"
>
</File>
<File
RelativePath="..\source\Items\ItemThrowable.h"
>
</File>
<File
RelativePath="..\source\items\ItemWood.h"
>

26
docs/Login sequence.txt Normal file
View File

@ -0,0 +1,26 @@
This is the connection sequence of the 1.6.2 client to a vanilla server, after the encryption has been established:
S->C: 0xfc (encryption key response) empty payload (4 * 0x00)
C->S: 0xcd (client statuses)
S->C: 0x01 (login)
S->C: 0xfa (plugin message) - "MC|Brand": "vanilla"
S->C: 0x06 (compass)
S->C: 0xca (player capabilities)
S->C: 0x10 (slot select)
S->C: 0x04 (time update)
S->C: 0xc9 (player list item)
S->C: 0x0d (player pos + look)
S->C: 0x04 (time update)
S->C: 0x68 (window contents)
S->C: 0x67 (slot contents) - Window -1, slot -1
S->C: 0x67 (slot contents) - Window 0, slot 9 .. 44, only occupied slots
S->C: 0x38 (chunk bulk)
S->C: 0x28 (entity metadata) player metadata
S->C: 0x2c (entity properties) player properties
S->C: 0x04 (time update)
S->C: 0x38 (chunk bulk)
C->S: 0xcc (client settings / locale and view)
C->S: 0xfa (plugin message) - "MC|Brand": "vanilla"
C->S: 0x0d (player pos + look)
S->C: 0x38 (chunk bulk)

View File

@ -27,6 +27,8 @@ $cfile "ClientHandle.h"
$cfile "Entities/Entity.h"
$cfile "Entities/Pawn.h"
$cfile "Entities/Player.h"
$cfile "Entities/Pickup.h"
$cfile "Entities/ProjectileEntity.h"
$cfile "PluginManager.h"
$cfile "Plugin.h"
$cfile "PluginLua.h"
@ -45,7 +47,6 @@ $cfile "BlockEntities/DropperEntity.h"
$cfile "BlockEntities/FurnaceEntity.h"
$cfile "WebAdmin.h"
$cfile "WebPlugin.h"
$cfile "Entities/Pickup.h"
$cfile "Root.h"
$cfile "Vector3f.h"
$cfile "Vector3d.h"

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
/*
** Lua binding: AllToLua
** Generated automatically by tolua++-1.0.92 on 08/19/13 11:57:27.
*/
/* Exported function */
TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S);
/*
** Lua binding: AllToLua
** Generated automatically by tolua++-1.0.92 on 09/01/13 14:42:05.
*/
/* Exported function */
TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S);

View File

@ -367,7 +367,9 @@ enum ENUM_ITEM_ID
// Keep these two as the last values of the disc list, without a number - they will get their correct number assigned automagically by C++
// IsValidItem() depends on this!
E_ITEM_LAST_DISC_PLUS_ONE, ///< Useless, really, but needs to be present for the following value
E_ITEM_LAST_DISC = E_ITEM_LAST_DISC_PLUS_ONE - 1 ///< Maximum disc itemtype number used
E_ITEM_LAST_DISC = E_ITEM_LAST_DISC_PLUS_ONE - 1, ///< Maximum disc itemtype number used
E_ITEM_LAST = E_ITEM_LAST_DISC, ///< Maximum valid ItemType
};

View File

@ -31,12 +31,12 @@ public:
/** Called on each block encountered along the path, including the first block (path start)
When this callback returns true, the tracing is aborted.
*/
virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) = 0;
/** Called on each block encountered along the path, including the first block (path start), if chunk data is not loaded
When this callback returns true, the tracing is aborted.
*/
virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ) { return false; }
virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ, char a_EntryFace) { return false; }
/** Called when the path goes out of world, either below (a_BlockY < 0) or above (a_BlockY >= cChunkDef::Height)
The coords specify the exact point at which the path exited the world.

View File

@ -18,8 +18,8 @@ class cBlockCarpetHandler :
public cBlockHandler
{
public:
cBlockCarpetHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType)
cBlockCarpetHandler(BLOCKTYPE a_BlockType) :
cBlockHandler(a_BlockType)
{
}

View File

@ -1,43 +0,0 @@
#pragma once
#include "BlockHandler.h"
#include "../Items/ItemHandler.h"
class cBlockDoubleSlabHandler :
public cBlockHandler
{
public:
cBlockDoubleSlabHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType)
{
}
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{
if (m_BlockType == E_BLOCK_DOUBLE_STONE_SLAB)
{
m_BlockType = E_BLOCK_STONE_SLAB;
}
else
{
m_BlockType = E_BLOCK_WOODEN_SLAB;
}
a_Pickups.push_back(cItem(m_BlockType, 2, a_BlockMeta));
}
virtual const char * GetStepSound(void) override
{
return ((m_BlockType == E_BLOCK_DOUBLE_WOODEN_SLAB) || (m_BlockType == E_BLOCK_DOUBLE_WOODEN_SLAB)) ? "step.wood" : "step.stone";
}
} ;

View File

@ -17,7 +17,6 @@
#include "BlockDeadBush.h"
#include "BlockDirt.h"
#include "BlockDoor.h"
#include "BlockDoubleSlab.h"
#include "BlockDropSpenser.h"
#include "BlockEnderchest.h"
#include "BlockEntity.h"

View File

@ -1,7 +1,17 @@
// BlockSlab.h
// Declares cBlockSlabHandler and cBlockDoubleSlabHandler classes
#pragma once
#include "BlockHandler.h"
#include "../Items/ItemHandler.h"
@ -30,36 +40,26 @@ public:
) override
{
a_BlockType = m_BlockType;
BLOCKTYPE Type = (BLOCKTYPE)(a_Player->GetEquippedItem().m_ItemType);
BLOCKTYPE Type = (BLOCKTYPE) (a_Player->GetEquippedItem().m_ItemType);
NIBBLETYPE Meta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x07);
int DoubleType;
if (Type == E_BLOCK_STONE_SLAB)
{
DoubleType = 43; // Make it a double slab (with old type wood)
}
else
{
DoubleType = 125; // Make it a wooden double slab (new type)
}
// HandlePlaceBlock wants a cItemHandler pointer thing, so let's give it one
cItemHandler * ItemHandler = cItemHandler::GetItemHandler(DoubleType);
cItemHandler * ItemHandler = cItemHandler::GetItemHandler(GetDoubleSlabType(Type));
// Check if the block at the coordinates is a slab. Eligibility for combining etc. were processed in ClientHandle
BLOCKTYPE IsSlab;
IsSlab = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
if ((IsSlab == E_BLOCK_STONE_SLAB) || (IsSlab == E_BLOCK_WOODEN_SLAB))
// Check if the block at the coordinates is a slab. Eligibility for combining has already been processed in ClientHandle
if (IsAnySlabType(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)))
{
// Special handling for non top/bottom clicks
// Call the function in ClientHandle that places a block when the client sends the packet,
// so that plugins may interfere with the placement.
if ((a_BlockFace == BLOCK_FACE_TOP) || (a_BlockFace == BLOCK_FACE_BOTTOM))
{
// As with previous, call the function in ClientHandle that places a block when the client sends the packet
// This effectively simulates a client placing a double slab, so it goes through plugins etc. so the slabbing can be cancelled
// Top and bottom faces need no parameter modification
a_Player->GetClientHandle()->HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler);
}
else
{
// If player cursor is at top half of block
// The other faces need to distinguish between top and bottom cursor positions
if (a_CursorY > 7)
{
// Edit the call to use BLOCK_FACE_BOTTOM, otherwise it places incorrectly
@ -71,19 +71,23 @@ public:
a_Player->GetClientHandle()->HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_BOTTOM, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler);
}
}
return false; // Cancel the event because dblslabs were already placed, nothing else needed
return false; // Cancel the event, because dblslabs were already placed, nothing else needed
}
// Place the single-slab with correct metas:
switch (a_BlockFace)
{
// Previous IF condition didn't cancel the event (not a slab at coords), so place slab with correct metas
case BLOCK_FACE_TOP:
{
a_BlockMeta = Meta & 0x7; break; // Bottom half slab block
// Bottom half slab block
a_BlockMeta = Meta & 0x7;
break;
}
case BLOCK_FACE_BOTTOM:
{
a_BlockMeta = Meta | 0x8; break; // Top half slab block
// Top half slab block
a_BlockMeta = Meta | 0x8;
break;
}
case BLOCK_FACE_EAST:
case BLOCK_FACE_NORTH:
@ -106,9 +110,70 @@ public:
}
virtual const char * GetStepSound(void) override
{
switch (m_BlockType)
{
case E_BLOCK_WOODEN_SLAB: return "step.wood";
case E_BLOCK_STONE_SLAB: return "step.stone";
}
ASSERT(!"Unhandled slab type!");
return "";
}
/// Returns true if the specified blocktype is one of the slabs handled by this handler
static bool IsAnySlabType(BLOCKTYPE a_BlockType)
{
return ((a_BlockType == E_BLOCK_WOODEN_SLAB) || (a_BlockType == E_BLOCK_STONE_SLAB));
}
/// Converts the single-slab blocktype to its equivalent double-slab blocktype
static BLOCKTYPE GetDoubleSlabType(BLOCKTYPE a_SingleSlabBlockType)
{
switch (a_SingleSlabBlockType)
{
case E_BLOCK_STONE_SLAB: return E_BLOCK_DOUBLE_STONE_SLAB;
case E_BLOCK_WOODEN_SLAB: return E_BLOCK_DOUBLE_WOODEN_SLAB;
}
ASSERT(!"Unhandled slab type!");
return E_BLOCK_AIR;
}
} ;
class cBlockDoubleSlabHandler :
public cBlockHandler
{
public:
cBlockDoubleSlabHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType)
{
}
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{
if (m_BlockType == E_BLOCK_DOUBLE_STONE_SLAB)
{
m_BlockType = E_BLOCK_STONE_SLAB;
}
else
{
m_BlockType = E_BLOCK_WOODEN_SLAB;
}
a_Pickups.push_back(cItem(m_BlockType, 2, a_BlockMeta));
}
virtual const char * GetStepSound(void) override
{
return ((m_BlockType == E_BLOCK_WOODEN_SLAB) || (m_BlockType == E_BLOCK_STONE_SLAB)) ? "step.wood" : "step.stone";
return ((m_BlockType == E_BLOCK_DOUBLE_WOODEN_SLAB) || (m_BlockType == E_BLOCK_DOUBLE_WOODEN_SLAB)) ? "step.wood" : "step.stone";
}
} ;

View File

@ -19,6 +19,7 @@
#include "OSSupport/Timer.h"
#include "Items/ItemHandler.h"
#include "Blocks/BlockHandler.h"
#include "Blocks/BlockSlab.h"
#include "Vector3f.h"
#include "Vector3d.h"
@ -51,6 +52,9 @@ static const int MAX_EXPLOSIONS_PER_TICK = 100;
/// How many explosions in the recent history are allowed
static const int MAX_RUNNING_SUM_EXPLOSIONS = cClientHandle::NUM_CHECK_EXPLOSIONS_TICKS * MAX_EXPLOSIONS_PER_TICK / 8;
/// How many ticks before the socket is closed after the client is destroyed (#31)
static const int TICKS_BEFORE_CLOSE = 20;
@ -84,6 +88,7 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance)
, m_bKeepThreadGoing(true)
, m_Ping(1000)
, m_PingID(1)
, m_TicksSinceDestruction(0)
, m_State(csConnected)
, m_LastStreamedChunkX(0x7fffffff) // bogus chunk coords to force streaming upon login
, m_LastStreamedChunkZ(0x7fffffff)
@ -111,7 +116,7 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance)
cClientHandle::~cClientHandle()
{
ASSERT(m_State == csDestroyed); // Has Destroy() been called?
ASSERT(m_State >= csDestroyedWaiting); // Has Destroy() been called?
LOGD("Deleting client \"%s\" at %p", GetUsername().c_str(), this);
@ -189,7 +194,7 @@ void cClientHandle::Destroy(void)
RemoveFromAllChunks();
m_Player->GetWorld()->RemoveClientFromChunkSender(this);
}
m_State = csDestroyed;
m_State = csDestroyedWaiting;
}
@ -571,8 +576,8 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, ch
// A plugin doesn't agree with the action. The plugin itself is responsible for handling the consequences (possible inventory mismatch)
return;
}
ItemHandler->OnItemShoot(m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
}
LOGINFO("%s: Status SHOOT not implemented", __FUNCTION__);
return;
}
@ -785,16 +790,16 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, c
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
cBlockHandler * Handler = cBlockHandler::GetBlockHandler(BlockType);
cBlockHandler * BlockHandler = cBlockHandler::GetBlockHandler(BlockType);
if (Handler->IsUseable() && !m_Player->IsCrouched())
if (BlockHandler->IsUseable() && !m_Player->IsCrouched())
{
if (PlgMgr->CallHookPlayerUsingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta))
{
// A plugin doesn't agree with using the block, abort
return;
}
Handler->OnUse(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
BlockHandler->OnUse(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
PlgMgr->CallHookPlayerUsedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta);
return;
}
@ -852,25 +857,24 @@ void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, c
BLOCKTYPE EquippedBlock = (BLOCKTYPE)(m_Player->GetEquippedItem().m_ItemType);
NIBBLETYPE EquippedBlockDamage = (NIBBLETYPE)(m_Player->GetEquippedItem().m_ItemDamage);
if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
{
// The block is being placed outside the world, ignore this packet altogether (#128)
return;
}
World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta);
// Special slab handler coding
// Special slab handling - placing a slab onto another slab produces a dblslab instead:
if (
// If clicked face top: is slab there in the "bottom" position?
// If clicked face bottom: is the slab there in the "top" position?
// This prevents a dblslab forming below if you click the top face of a "top" slab.
(((a_BlockFace == BLOCK_FACE_TOP) && (ClickedBlockMeta == (EquippedBlockDamage & 0x07))) || ((a_BlockFace == BLOCK_FACE_BOTTOM) && (ClickedBlockMeta == (EquippedBlockDamage | 0x08)))) &&
// Is clicked a slab? This is a SLAB handler, not stone or something!
((ClickedBlock == E_BLOCK_STONE_SLAB) || (ClickedBlock == E_BLOCK_WOODEN_SLAB)) &&
// Is equipped a some type of slab?
// This prevents a bug where, well, you get a dblslab by placing TNT or something not a slab.
((EquippedBlock == E_BLOCK_STONE_SLAB) || (EquippedBlock == E_BLOCK_WOODEN_SLAB)) &&
// Is equipped slab type same as the slab in the world? After all, we can't combine different slabs!
((ClickedBlockMeta & 0x07) == (EquippedBlockDamage & 0x07))
cBlockSlabHandler::IsAnySlabType(ClickedBlock) && // Is there a slab already?
cBlockSlabHandler::IsAnySlabType(EquippedBlock) && // Is the player placing another slab?
((ClickedBlockMeta & 0x07) == (EquippedBlockDamage & 0x07)) && // Is it the same slab type?
(
(a_BlockFace == BLOCK_FACE_TOP) || // Clicking the top of a bottom slab
(a_BlockFace == BLOCK_FACE_BOTTOM) // Clicking the bottom of a top slab
)
)
{
// Coordinates at CLICKED block, don't move them anywhere
}
@ -881,17 +885,26 @@ void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, c
if (Handler->DoesIgnoreBuildCollision())
{
Handler->OnDestroyedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ);
//World->FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
}
else
BLOCKTYPE PlaceBlock = World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
if (!BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision())
{
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
{
// The block is being placed outside the world, ignore this packet altogether (#128)
return;
}
BLOCKTYPE PlaceBlock = World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
// Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed.
// No need to do combinability (dblslab) checks, client will do that here.
if ((World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_STONE_SLAB) || (World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_WOODEN_SLAB))
if (cBlockSlabHandler::IsAnySlabType(PlaceBlock))
{
//Is a slab, don't do checks and proceed to double-slabbing
// It's a slab, don't do checks and proceed to double-slabbing
}
else
{
@ -899,13 +912,11 @@ void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, c
if ((a_BlockFace == BLOCK_FACE_TOP) && !Handler->DoesAllowBlockOnTop())
{
// Resend the old block
// Some times the client still places the block O.o
// Sometimes the client still places the block O.o
World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
return;
}
BLOCKTYPE PlaceBlock = World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
if (!BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision())
{
// Tried to place a block *into* another?
@ -915,7 +926,6 @@ void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, c
}
}
}
// Special slab handler coding end
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
@ -951,7 +961,7 @@ void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, c
NewBlock->OnPlacedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta);
// Step sound with 0.8f pitch is used as block placement sound
World->BroadcastSoundEffect(NewBlock->GetStepSound(),a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 1.0f, 0.8f);
World->BroadcastSoundEffect(NewBlock->GetStepSound(), a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 1.0f, 0.8f);
cRoot::Get()->GetPluginManager()->CallHookPlayerPlacedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta);
}
@ -1324,6 +1334,12 @@ void cClientHandle::HandleTabCompletion(const AString & a_Text)
void cClientHandle::SendData(const char * a_Data, int a_Size)
{
if (m_HasSentDC)
{
// This could crash the client, because they've already unloaded the world etc., and suddenly a wild packet appears (#31)
return;
}
{
cCSLock Lock(m_CSOutgoingData);
@ -1438,6 +1454,17 @@ bool cClientHandle::CheckBlockInteractionsRate(void)
void cClientHandle::Tick(float a_Dt)
{
// Handle clients that are waiting for final close while destroyed:
if (m_State == csDestroyedWaiting)
{
m_TicksSinceDestruction += 1; // This field is misused for the timeout counting
if (m_TicksSinceDestruction > TICKS_BEFORE_CLOSE)
{
m_State = csDestroyed;
}
return;
}
// Process received network data:
AString IncomingData;
{
@ -1563,6 +1590,16 @@ void cClientHandle::SendChat(const AString & a_Message)
void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
{
ASSERT(m_Player != NULL);
if ((m_State == csAuthenticated) || (m_State == csDownloadingWorld))
{
if ((a_ChunkX == m_Player->GetChunkX()) && (a_ChunkZ == m_Player->GetChunkZ()))
{
m_Protocol->SendPlayerMoveLook();
}
}
// Check chunks being sent, erase them from m_ChunksToSend:
bool Found = false;
{

View File

@ -257,6 +257,9 @@ private:
int m_LastDigBlockX;
int m_LastDigBlockY;
int m_LastDigBlockZ;
/// Used while csDestroyedWaiting for counting the ticks until the connection is closed
int m_TicksSinceDestruction;
enum eState
{
@ -267,6 +270,7 @@ private:
csConfirmingPos, ///< The client has been sent the position packet, waiting for them to repeat the position back
csPlaying, ///< Normal gameplay
csDestroying, ///< The client is being destroyed, don't queue any more packets / don't add to chunks
csDestroyedWaiting, ///< The client has been destroyed, but is still kept so that the Kick packet is delivered (#31)
csDestroyed, ///< The client has been destroyed, the destructor is to be called from the owner thread
// TODO: Add Kicking here as well

View File

@ -29,6 +29,16 @@ extern bool g_BlockTransparent[];
/// Is a block destroyed after a single hit?
extern bool g_BlockOneHitDig[];
/// Can a piston break this block?
extern bool g_BlockPistonBreakable[256];
/// Can this block hold snow atop?
extern bool g_BlockIsSnowable[256];
extern bool g_BlockRequiresSpecialTool[256];
extern bool g_BlockIsSolid[256];

View File

@ -144,6 +144,10 @@ bool cEntity::Initialize(cWorld * a_World)
m_World->AddEntity(this);
cPluginManager::Get()->CallHookSpawnedEntity(*a_World, *this);
// Spawn the entity on the clients:
a_World->BroadcastSpawnEntity(*this);
return true;
}
@ -477,7 +481,7 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
// TODO Add collision detection with entities.
a_Dt /= 1000;
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);
@ -493,7 +497,7 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
}
// Make sure we got the correct chunk and a valid one. No one ever knows...
cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX,BlockZ);
cChunk * NextChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ);
if (NextChunk != NULL)
{
int RelBlockX = BlockX - (NextChunk->GetPosX() * cChunkDef::Width);
@ -513,11 +517,12 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
}
else
{
//Push out entity.
// Push out entity.
m_bOnGround = true;
NextPos.y += 0.2;
LOGD("Entity #%d (%s) is inside a block at {%d,%d,%d}",
m_UniqueID, GetClass(), BlockX, BlockY, BlockZ);
LOGD("Entity #%d (%s) is inside a block at {%d, %d, %d}",
m_UniqueID, GetClass(), BlockX, BlockY, BlockZ
);
}
if (!m_bOnGround)
@ -525,16 +530,16 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
float fallspeed;
if (IsBlockWater(BlockIn))
{
fallspeed = -3.0f * a_Dt; //Fall slower in water.
fallspeed = m_Gravity * a_Dt / 3; // Fall 3x slower in water.
}
else if (BlockIn == E_BLOCK_COBWEB)
{
NextSpeed.y *= 0.05; //Reduce overall falling speed
fallspeed = 0; //No falling.
NextSpeed.y *= 0.05; // Reduce overall falling speed
fallspeed = 0; // No falling.
}
else
{
//Normal gravity
// Normal gravity
fallspeed = m_Gravity * a_Dt;
}
NextSpeed.y += fallspeed;
@ -548,19 +553,25 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
(BlockBelow != E_BLOCK_ACTIVATOR_RAIL)
)
{
//Friction
// Friction
if (NextSpeed.SqrLength() > 0.0004f)
{
NextSpeed.x *= 0.7f/(1+a_Dt);
if ( fabs(NextSpeed.x) < 0.05 ) NextSpeed.x = 0;
NextSpeed.z *= 0.7f/(1+a_Dt);
if ( fabs(NextSpeed.z) < 0.05 ) NextSpeed.z = 0;
NextSpeed.x *= 0.7f / (1 + a_Dt);
if (fabs(NextSpeed.x) < 0.05)
{
NextSpeed.x = 0;
}
NextSpeed.z *= 0.7f / (1 + a_Dt);
if (fabs(NextSpeed.z) < 0.05)
{
NextSpeed.z = 0;
}
}
}
}
//Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we
//might have different speed modifiers according to terrain.
// Adjust X and Z speed for COBWEB temporary. This speed modification should be handled inside block handlers since we
// might have different speed modifiers according to terrain.
if (BlockIn == E_BLOCK_COBWEB)
{
NextSpeed.x *= 0.25;
@ -1037,9 +1048,9 @@ void cEntity::SetMass(double a_Mass)
}
else
{
//Make sure that mass is not zero. 1g is the default because we
//have to choose a number. It's perfectly legal to have a mass
//less than 1g as long as is NOT equal or less than zero.
// Make sure that mass is not zero. 1g is the default because we
// have to choose a number. It's perfectly legal to have a mass
// less than 1g as long as is NOT equal or less than zero.
m_Mass = 0.001;
}
}

View File

@ -90,12 +90,13 @@ public:
etPlayer,
etPickup,
etMonster,
etMob = etMonster, // DEPRECATED, use etMonster instead!
etFallingBlock,
etMinecart,
etTNT,
etProjectile,
// DEPRECATED older constants, left over for compatibility reasons (plugins)
etMob = etMonster, // DEPRECATED, use etMonster instead!
eEntityType_Entity = etEntity,
eEntityType_Player = etPlayer,
eEntityType_Pickup = etPickup,
@ -162,7 +163,7 @@ public:
void SetPosY (double a_PosY);
void SetPosZ (double a_PosZ);
void SetPosition(double a_PosX, double a_PosY, double a_PosZ);
void SetPosition(const Vector3d & a_Pos) { SetPosition(a_Pos.x,a_Pos.y,a_Pos.z);}
void SetPosition(const Vector3d & a_Pos) { SetPosition(a_Pos.x, a_Pos.y, a_Pos.z); }
void SetRot (const Vector3f & a_Rot);
void SetRotation(double a_Rotation);
void SetPitch (double a_Pitch);
@ -276,9 +277,8 @@ public:
/** Descendants override this function to send a command to the specified client to spawn the entity on the client.
To spawn on all eligible clients, use cChunkMap::BroadcastSpawnEntity()
Needs to have a default implementation due to Lua bindings.
*/
virtual void SpawnOn(cClientHandle & a_Client) {ASSERT(!"SpawnOn() unimplemented!"); }
virtual void SpawnOn(cClientHandle & a_Client) = 0;
// tolua_begin

View File

@ -22,20 +22,6 @@ cFallingBlock::cFallingBlock(const Vector3i & a_BlockPosition, BLOCKTYPE a_Block
bool cFallingBlock::Initialize(cWorld * a_World)
{
if (super::Initialize(a_World))
{
a_World->BroadcastSpawnEntity(*this);
return true;
}
return false;
}
void cFallingBlock::SpawnOn(cClientHandle & a_ClientHandle)
{
a_ClientHandle.SendSpawnFallingBlock(*this);

View File

@ -29,7 +29,6 @@ public:
NIBBLETYPE GetBlockMeta(void) const { return m_BlockMeta; }
// cEntity overrides:
virtual bool Initialize(cWorld * a_World) override;
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;

View File

@ -26,20 +26,6 @@ cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) :
bool cMinecart::Initialize(cWorld * a_World)
{
if (super::Initialize(a_World))
{
a_World->BroadcastSpawnEntity(*this);
return true;
}
return false;
}
void cMinecart::SpawnOn(cClientHandle & a_ClientHandle)
{
char SubType = 0;

View File

@ -35,7 +35,6 @@ public:
} ;
// cEntity overrides:
virtual bool Initialize(cWorld * a_World) override;
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
void HandleRailPhysics(float a_Dt, cChunk & a_Chunk);

View File

@ -40,20 +40,6 @@ cPickup::cPickup(int a_MicroPosX, int a_MicroPosY, int a_MicroPosZ, const cItem
bool cPickup::Initialize(cWorld * a_World)
{
if (super::Initialize(a_World))
{
a_World->BroadcastSpawnEntity(*this);
return true;
}
return false;
}
void cPickup::SpawnOn(cClientHandle & a_Client)
{
a_Client.SendPickupSpawn(*this);
@ -73,18 +59,19 @@ void cPickup::Tick(float a_Dt, cChunk & a_Chunk)
if (!m_bCollected)
{
int BlockY = (int) floor(GetPosY());
if (BlockY < cChunkDef::Height) // Don't do anything except for falling when above the world
if ((BlockY >= 0) && (BlockY < cChunkDef::Height)) // Don't do anything except for falling when outside the world
{
int BlockX = (int) floor(GetPosX());
int BlockZ = (int) floor(GetPosZ());
//Position might have changed due to physics. So we have to make sure we have the correct chunk.
// Position might have changed due to physics. So we have to make sure we have the correct chunk.
cChunk * CurrentChunk = a_Chunk.GetNeighborChunk(BlockX, BlockZ);
if (CurrentChunk != NULL) // Make sure the chunk is loaded
{
int RelBlockX = BlockX - (CurrentChunk->GetPosX() * cChunkDef::Width);
int RelBlockZ = BlockZ - (CurrentChunk->GetPosZ() * cChunkDef::Width);
BLOCKTYPE BlockBelow = CurrentChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ);
// If the pickup is on the bottommost block position, make it think the void is made of air: (#131)
BLOCKTYPE BlockBelow = (BlockY > 0) ? CurrentChunk->GetBlock(RelBlockX, BlockY - 1, RelBlockZ) : E_BLOCK_AIR;
BLOCKTYPE BlockIn = CurrentChunk->GetBlock(RelBlockX, BlockY, RelBlockZ);
if (

View File

@ -26,8 +26,6 @@ public:
cPickup(int a_MicroPosX, int a_MicroPosY, int a_MicroPosZ, const cItem & a_Item, float a_SpeedX = 0.f, float a_SpeedY = 0.f, float a_SpeedZ = 0.f); // tolua_export
virtual bool Initialize(cWorld * a_World) override;
cItem & GetItem(void) {return m_Item; } // tolua_export
const cItem & GetItem(void) const {return m_Item; }

View File

@ -64,6 +64,8 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
, m_IsSwimming(false)
, m_IsSubmerged(false)
, m_EatingFinishTick(-1)
, m_IsChargingBow(false)
, m_BowCharge(0)
{
LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d",
a_PlayerName.c_str(), a_Client->GetIPString().c_str(),
@ -200,6 +202,12 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
}
}
if (!a_Chunk.IsValid())
{
// This may happen if the cPlayer is created before the chunks have the chance of being loaded / generated (#83)
return;
}
super::Tick(a_Dt, a_Chunk);
// Set player swimming state
@ -207,6 +215,13 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
// Handle air drowning stuff
HandleAir();
// Handle charging the bow:
if (m_IsChargingBow)
{
m_BowCharge += 1;
LOGD("Player \"%s\" charging bow: %d", m_PlayerName.c_str(), m_BowCharge);
}
if (m_bDirtyPosition)
{
@ -247,6 +262,41 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
void cPlayer::StartChargingBow(void)
{
LOGD("Player \"%s\" started charging their bow", m_PlayerName.c_str());
m_IsChargingBow = true;
m_BowCharge = 0;
}
int cPlayer::FinishChargingBow(void)
{
LOGD("Player \"%s\" finished charging their bow at a charge of %d", m_PlayerName.c_str(), m_BowCharge);
int res = m_BowCharge;
m_IsChargingBow = false;
m_BowCharge = 0;
return res;
}
void cPlayer::CancelChargingBow(void)
{
LOGD("Player \"%s\" cancelled charging their bow at a charge of %d", m_PlayerName.c_str(), m_BowCharge);
m_IsChargingBow = false;
m_BowCharge = 0;
}
void cPlayer::SetTouchGround(bool a_bTouchGround)
{
// If just
@ -807,6 +857,36 @@ void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ)
Vector3d cPlayer::GetThrowStartPos(void) const
{
Vector3d res = GetEyePosition();
// Adjust the position to be just outside the player's bounding box:
res.x += 0.16 * cos(GetPitch());
res.y += -0.1;
res.z += 0.16 * sin(GetPitch());
return res;
}
Vector3d cPlayer::GetThrowSpeed(double a_SpeedCoeff) const
{
Vector3d res = GetLookVector();
res.Normalize();
// TODO: Add a slight random change (+-0.0075 in each direction)
return res * a_SpeedCoeff;
}
void cPlayer::MoveTo( const Vector3d & a_NewPos )
{
if ((a_NewPos.y < -990) && (GetPosY() > -100))

View File

@ -62,6 +62,18 @@ public:
/// Returns the currently equipped boots; empty item if none
virtual cItem GetEquippedBoots(void) const override { return m_Inventory.GetEquippedBoots(); }
/// Starts charging the equipped bow
void StartChargingBow(void);
/// Finishes charging the current bow. Returns the number of ticks for which the bow has been charged
int FinishChargingBow(void);
/// Cancels the current bow charging
void CancelChargingBow(void);
/// Returns true if the player is currently charging the bow
bool IsChargingBow(void) const { return m_IsChargingBow; }
void SetTouchGround( bool a_bTouchGround );
inline void SetStance( const double a_Stance ) { m_Stance = a_Stance; }
@ -78,6 +90,12 @@ public:
// tolua_begin
/// Returns the position where projectiles thrown by this player should start, player eye position + adjustment
Vector3d GetThrowStartPos(void) const;
/// Returns the initial speed vector of a throw, with a 3D length of a_SpeedCoeff.
Vector3d GetThrowSpeed(double a_SpeedCoeff) const;
/// Returns the current gamemode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable
eGameMode GetGameMode(void) const { return m_GameMode; }
@ -351,6 +369,9 @@ protected:
/// The world tick in which eating will be finished. -1 if not eating
Int64 m_EatingFinishTick;
bool m_IsChargingBow;
int m_BowCharge;
virtual void Destroyed(void);

View File

@ -0,0 +1,351 @@
// ProjectileEntity.cpp
// Implements the cProjectileEntity class representing the common base class for projectiles, as well as individual projectile types
#include "Globals.h"
#include "ProjectileEntity.h"
#include "../ClientHandle.h"
#include "Player.h"
#include "../LineBlockTracer.h"
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cProjectileTracerCallback:
class cProjectileTracerCallback :
public cBlockTracer::cCallbacks
{
public:
cProjectileTracerCallback(cProjectileEntity * a_Projectile) :
m_Projectile(a_Projectile)
{
}
protected:
cProjectileEntity * m_Projectile;
virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override
{
if (g_BlockIsSolid[a_BlockType])
{
// The projectile hit a solid block
m_Projectile->OnHitSolidBlock(a_BlockX, a_BlockY, a_BlockZ, a_EntryFace);
return true;
}
// Convey some special effects from special blocks:
switch (a_BlockType)
{
case E_BLOCK_LAVA:
case E_BLOCK_STATIONARY_LAVA:
{
m_Projectile->StartBurning(30);
break;
}
case E_BLOCK_WATER:
case E_BLOCK_STATIONARY_WATER:
{
m_Projectile->StopBurning();
break;
}
} // switch (a_BlockType)
// Continue tracing
return false;
}
} ;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cProjectileEntity:
cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height) :
super(etProjectile, a_X, a_Y, a_Z, a_Width, a_Height),
m_ProjectileKind(a_Kind),
m_Creator(a_Creator),
m_IsInGround(false)
{
}
cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height) :
super(etProjectile, a_Pos.x, a_Pos.y, a_Pos.z, a_Width, a_Height),
m_ProjectileKind(a_Kind),
m_Creator(a_Creator),
m_IsInGround(false)
{
SetSpeed(a_Speed);
}
cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d * a_Speed)
{
Vector3d Speed;
if (a_Speed != NULL)
{
Speed = *a_Speed;
}
switch (a_Kind)
{
case pkArrow: return new cArrowEntity (a_Creator, a_X, a_Y, a_Z, Speed);
case pkEgg: return new cThrownEggEntity (a_Creator, a_X, a_Y, a_Z, Speed);
case pkEnderPearl: return new cThrownEnderPearlEntity(a_Creator, a_X, a_Y, a_Z, Speed);
case pkSnowball: return new cThrownSnowballEntity (a_Creator, a_X, a_Y, a_Z, Speed);
// TODO: the rest
}
LOGWARNING("%s: Unknown projectile kind: %d", __FUNCTION__, a_Kind);
return NULL;
}
void cProjectileEntity::OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
{
// TODO: Set proper position based on what face was hit
switch (a_BlockFace)
{
case BLOCK_FACE_TOP: SetPosition(0.5 + a_BlockX, 1.0 + a_BlockY, 0.5 + a_BlockZ); break;
case BLOCK_FACE_BOTTOM: SetPosition(0.5 + a_BlockX, a_BlockY, 0.5 + a_BlockZ); break;
case BLOCK_FACE_EAST: SetPosition( a_BlockX, 0.5 + a_BlockY, 0.5 + a_BlockZ); break;
case BLOCK_FACE_WEST: SetPosition(1.0 + a_BlockX, 0.5 + a_BlockY, 0.5 + a_BlockZ); break;
case BLOCK_FACE_NORTH: SetPosition(0.5 + a_BlockX, 0.5 + a_BlockY, 1.0 + a_BlockZ); break;
case BLOCK_FACE_SOUTH: SetPosition(0.5 + a_BlockX, 0.5 + a_BlockY, a_BlockZ); break;
case BLOCK_FACE_NONE: SetPosition(0.5 + a_BlockX, 0.5 + a_BlockY, 0.5 + a_BlockZ); break;
}
SetSpeed(0, 0, 0);
// DEBUG:
LOGD("Projectile %d: pos {%.02f, %.02f, %.02f}, hit solid block at face %d",
m_UniqueID,
GetPosX(), GetPosY(), GetPosZ(),
a_BlockFace
);
m_IsInGround = true;
}
AString cProjectileEntity::GetMCAClassName(void) const
{
switch (m_ProjectileKind)
{
case pkArrow: return "Arrow";
case pkSnowball: return "Snowball";
case pkEgg: return "Egg";
case pkGhastFireball: return "Fireball";
case pkFireCharge: return "SmallFireball";
case pkEnderPearl: return "ThrownEnderPearl";
case pkExpBottle: return "ThrownExpBottle";
case pkSplashPotion: return "ThrownPotion";
case pkWitherSkull: return "WitherSkull";
case pkFishingFloat: return ""; // Unknown, perhaps MC doesn't save this?
}
ASSERT(!"Unhandled projectile entity kind!");
return "";
}
void cProjectileEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
BroadcastMovementUpdate();
}
void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
if (m_IsInGround)
{
// Already-grounded projectiles don't move at all
return;
}
Vector3d PerTickSpeed = GetSpeed() / 20;
Vector3d Pos = GetPosition();
// Trace the tick's worth of movement as a line:
Vector3d NextPos = Pos + PerTickSpeed;
cProjectileTracerCallback TracerCallback(this);
if (cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos))
{
// Nothing in the way, update the position
SetPosition(NextPos);
}
// Add gravity effect to the vertical speed component:
SetSpeedY(GetSpeedY() + m_Gravity / 20);
// DEBUG:
LOGD("Arrow %d: pos {%.02f, %.02f, %.02f}, speed {%.02f, %.02f, %.02f}",
m_UniqueID,
GetPosX(), GetPosY(), GetPosZ(),
GetSpeedX(), GetSpeedY(), GetSpeedZ()
);
}
void cProjectileEntity::SpawnOn(cClientHandle & a_Client)
{
// Default spawning - use the projectile kind to spawn an object:
a_Client.SendSpawnObject(*this, m_ProjectileKind, 0, 0, 0);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cArrowEntity:
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)
{
SetSpeed(a_Speed);
SetMass(0.1);
LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f}",
m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ()
);
}
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)
{
}
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::SpawnOn(cClientHandle & a_Client)
{
a_Client.SendSpawnObject(*this, pkArrow, 0, 0, 0);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cThrownEggEntity:
cThrownEggEntity::cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
super(pkEgg, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
{
SetSpeed(a_Speed);
}
void cThrownEggEntity::OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
{
// TODO: Random-spawn a chicken or four
Destroy();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cThrownEnderPearlEntity :
cThrownEnderPearlEntity::cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
super(pkEnderPearl, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
{
SetSpeed(a_Speed);
}
void cThrownEnderPearlEntity::OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
{
// TODO: Teleport the creator here, make them take 5 damage
Destroy();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cThrownSnowballEntity :
cThrownSnowballEntity::cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
super(pkSnowball, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
{
SetSpeed(a_Speed);
}
void cThrownSnowballEntity::OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
{
// TODO: Apply damage to certain mobs (blaze etc.) and anger all mobs
Destroy();
}

View File

@ -0,0 +1,238 @@
// ProjectileEntity.h
// Declares the cProjectileEntity class representing the common base class for projectiles, as well as individual projectile types
#pragma once
#include "Entity.h"
// tolua_begin
class cProjectileEntity :
public cEntity
{
typedef cEntity super;
public:
/// The kind of the projectile. The numbers correspond to the network type ID used for spawning via the 0x17 packet.
enum eKind
{
pkArrow = 60,
pkSnowball = 61,
pkEgg = 62,
pkGhastFireball = 63,
pkFireCharge = 64,
pkEnderPearl = 65,
pkExpBottle = 75,
pkSplashPotion = 73,
pkWitherSkull = 66,
pkFishingFloat = 90,
} ;
// tolua_end
CLASS_PROTODEF(cProjectileEntity);
cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height);
cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height);
static cProjectileEntity * Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d * a_Speed = NULL);
/// Called by the physics blocktracer when the entity hits a solid block, the block's coords and the face hit is given
virtual void OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace);
// tolua_begin
/// Returns the kind of the projectile (fast class identification)
eKind GetProjectileKind(void) const { return m_ProjectileKind; }
/// Returns the entity who created this projectile; may be NULL
cEntity * GetCreator(void) { return m_Creator; }
/// Returns the string that is used as the entity type (class name) in MCA files
AString GetMCAClassName(void) const;
/// Returns true if the projectile has hit the ground and is stuck there
bool IsInGround(void) const { return m_IsInGround; }
protected:
eKind m_ProjectileKind;
/// The entity who has created this projectile; may be NULL (e. g. for dispensers)
cEntity * m_Creator;
/// True if the projectile has hit the ground and is stuck there
bool m_IsInGround;
// tolua_end
// cEntity overrides:
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
virtual void SpawnOn(cClientHandle & a_Client) override;
// 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;
// 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;
// cProjectileEntity overrides:
virtual void SpawnOn(cClientHandle & a_Client) override;
// tolua_begin
} ;
class cThrownEggEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
// tolua_end
CLASS_PROTODEF(cThrownEggEntity);
cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
protected:
// tolua_end
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override;
// tolua_begin
} ;
class cThrownEnderPearlEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
// tolua_end
CLASS_PROTODEF(cThrownEnderPearlEntity);
cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
protected:
// tolua_end
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override;
// tolua_begin
} ;
class cThrownSnowballEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
// tolua_end
CLASS_PROTODEF(cThrownSnowballEntity);
cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
protected:
// tolua_end
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override;
// tolua_begin
} ;
// tolua_end

View File

@ -29,20 +29,6 @@ cTNTEntity::cTNTEntity(const Vector3d & a_Pos, double a_FuseTimeInSec) :
bool cTNTEntity::Initialize(cWorld * a_World)
{
if (super::Initialize(a_World))
{
a_World->BroadcastSpawnEntity(*this);
return true;
}
return false;
}
void cTNTEntity::SpawnOn(cClientHandle & a_ClientHandle)
{
a_ClientHandle.SendSpawnObject(*this, 50, 1, 0, 0); // 50 means TNT

View File

@ -19,7 +19,6 @@ public:
cTNTEntity(const Vector3d & a_Pos, double a_FuseTimeInSec);
// cEntity overrides:
virtual bool Initialize(cWorld * a_World) override;
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;

80
source/Items/ItemBow.h Normal file
View File

@ -0,0 +1,80 @@
// ItemBow.h
// Declares the cItemBowHandler class representing the itemhandler for bows
#pragma once
#include "../Entities/ProjectileEntity.h"
class cItemBowHandler :
public cItemHandler
{
typedef cItemHandler super;
public:
cItemBowHandler(void) :
super(E_ITEM_BOW)
{
}
virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
{
ASSERT(a_Player != NULL);
// Check if the player has an arrow in the inventory, or is in Creative:
if (!(a_Player->IsGameModeCreative() || a_Player->GetInventory().HasItems(cItem(E_ITEM_ARROW))))
{
return false;
}
a_Player->StartChargingBow();
return true;
}
virtual void OnItemShoot(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) override
{
// Actual shot - produce the arrow with speed based on the ticks that the bow was charged
ASSERT(a_Player != NULL);
int BowCharge = a_Player->FinishChargingBow();
double Force = (double)BowCharge / 20;
Force = (Force * Force + 2 * Force) / 3; // This formula is used by the 1.6.2 client
if (Force < 0.1)
{
// Too little force, ignore the shot
return;
}
if (Force > 1)
{
Force = 1;
}
// Create the arrow entity:
cArrowEntity * Arrow = new cArrowEntity(*a_Player, Force * 2);
if (Arrow == NULL)
{
return;
}
if (!Arrow->Initialize(a_Player->GetWorld()))
{
delete Arrow;
return;
}
a_Player->GetWorld()->BroadcastSpawnEntity(*Arrow);
}
} ;

View File

@ -8,6 +8,7 @@
// Handlers:
#include "ItemBed.h"
#include "ItemBow.h"
#include "ItemBrewingStand.h"
#include "ItemBucket.h"
#include "ItemCauldron.h"
@ -21,6 +22,7 @@
#include "ItemLighter.h"
#include "ItemMinecart.h"
#include "ItemPickaxe.h"
#include "ItemThrowable.h"
#include "ItemRedstoneDust.h"
#include "ItemRedstoneRepeater.h"
#include "ItemSapling.h"
@ -47,18 +49,24 @@ cItemHandler * cItemHandler::m_ItemHandler[2268];
cItemHandler *cItemHandler::GetItemHandler(int a_ItemType)
cItemHandler * cItemHandler::GetItemHandler(int a_ItemType)
{
if(a_ItemType < 0) a_ItemType = 0;
if (a_ItemType < 0)
{
ASSERT(!"Bad item type");
a_ItemType = 0;
}
if(!m_HandlerInitialized)
{ //We have to initialize
if (!m_HandlerInitialized)
{
// We need to initialize
memset(m_ItemHandler, 0, sizeof(m_ItemHandler));
m_HandlerInitialized = true;
}
if(m_ItemHandler[a_ItemType])
return m_ItemHandler[a_ItemType];
m_ItemHandler[a_ItemType] = CreateItemHandler(a_ItemType);
if (m_ItemHandler[a_ItemType] == NULL)
{
m_ItemHandler[a_ItemType] = CreateItemHandler(a_ItemType);
}
return m_ItemHandler[a_ItemType];
}
@ -77,15 +85,19 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType)
case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType);
case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType);
case E_ITEM_BED: return new cItemBedHandler(a_ItemType);
case E_ITEM_BOW: return new cItemBowHandler;
case E_ITEM_BREWING_STAND: return new cItemBrewingStandHandler(a_ItemType);
case E_ITEM_CAULDRON: return new cItemCauldronHandler(a_ItemType);
case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType);
case E_ITEM_EGG: return new cItemEggHandler();
case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler();
case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType);
case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType);
case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType);
case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType);
case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType);
case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType);
case E_ITEM_SNOWBALL: return new cItemSnowballHandler();
case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType);
case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(a_ItemType);

View File

@ -21,8 +21,11 @@ class cItemHandler
public:
cItemHandler(int a_ItemType);
/// Called when the player tries to use the item. Return false to make the item unusable. DEFAULT: False
virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir); //eg for fishing or hoes
/// Called when the player tries to use the item (right mouse button). Return false to make the item unusable. DEFAULT: False
virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir);
/// Called when the client sends the SHOOT status in the lclk packet
virtual void OnItemShoot(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) {}
/// Called while the player diggs a block using this item
virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace);
@ -88,7 +91,7 @@ protected:
int m_ItemType;
static cItemHandler *CreateItemHandler(int m_ItemType);
static cItemHandler *m_ItemHandler[2268];
static cItemHandler * m_ItemHandler[E_ITEM_LAST + 1];
static bool m_HandlerInitialized; //used to detect if the itemhandlers are initialized
};

View File

@ -0,0 +1,90 @@
// ItemThrowable.h
// Declares the itemhandlers for throwable items: eggs, snowballs and ender pearls
#pragma once
class cItemThrowableHandler :
public cItemHandler
{
typedef cItemHandler super;
public:
cItemThrowableHandler(int a_ItemType, cProjectileEntity::eKind a_ProjectileKind, double a_SpeedCoeff) :
super(a_ItemType),
m_ProjectileKind(a_ProjectileKind),
m_SpeedCoeff(a_SpeedCoeff)
{
}
virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
{
Vector3d Pos = a_Player->GetThrowStartPos();
Vector3d Speed = a_Player->GetLookVector() * m_SpeedCoeff;
a_World->CreateProjectile(Pos.x, Pos.y, Pos.z, m_ProjectileKind, a_Player, &Speed);
return true;
}
protected:
cProjectileEntity::eKind m_ProjectileKind;
double m_SpeedCoeff;
} ;
class cItemEggHandler :
public cItemThrowableHandler
{
typedef cItemThrowableHandler super;
public:
cItemEggHandler(void) :
super(E_ITEM_EGG, cProjectileEntity::pkEgg, 30)
{
}
} ;
class cItemSnowballHandler :
public cItemThrowableHandler
{
typedef cItemThrowableHandler super;
public:
cItemSnowballHandler(void) :
super(E_ITEM_SNOWBALL, cProjectileEntity::pkSnowball, 30)
{
}
} ;
class cItemEnderPearlHandler :
public cItemThrowableHandler
{
typedef cItemThrowableHandler super;
public:
cItemEnderPearlHandler(void) :
super(E_ITEM_ENDER_PEARL, cProjectileEntity::pkEnderPearl, 30)
{
}
} ;

View File

@ -116,12 +116,15 @@
/* _X: MSVC 2012 (MSC 1700) seems to use a different allocation scheme for STL containers,
* allocating lots of small objects and running out of memory very soon
* Thus for MSVC 2012 we cut the callstack buffer length in half
*
* _X 2013_08_25: The callstack tracking gets worse even for MSVC 2008, a single lua_state eats 50 MiB of RAM
* Therefore I decided to further reduce the buffers from 0x2000 to 0x1000
*/
// Controlling the callstack depth
#if (_MSC_VER < 1700)
#define MAX_CALLSTACK_LEN_BUF 0x2000
#else
#define MAX_CALLSTACK_LEN_BUF 0x1000
#else
#define MAX_CALLSTACK_LEN_BUF 0x0800
#endif
@ -942,10 +945,10 @@ static int MyAllocHook(int nAllocType, void *pvData,
g_CurrentMemUsage += nSize ;
g_pCRTTable->Insert(lRequest, c, nSize);
if (g_CurrentMemUsage > 1073741824) //This is 1 gb = 1024 * 1024* 1024.
if (g_CurrentMemUsage > 1536 * 1024* 1024)
{
printf("******************************************\n");
printf("** Server reached 1 GiB memory usage, **\n");
printf("** Server reached 1.5 GiB memory usage, **\n");
printf("** something is probably wrong. **\n");
printf("** Writing memory dump into memdump.xml **\n");
printf("******************************************\n");

View File

@ -55,6 +55,7 @@ bool cLineBlockTracer::Trace(double a_StartX, double a_StartY, double a_StartZ,
m_DirX = (m_StartX < m_EndX) ? 1 : -1;
m_DirY = (m_StartY < m_EndY) ? 1 : -1;
m_DirZ = (m_StartZ < m_EndZ) ? 1 : -1;
m_CurrentFace = BLOCK_FACE_NONE;
// Check the start coords, adjust into the world:
if (m_StartY < 0)
@ -178,9 +179,9 @@ bool cLineBlockTracer::MoveToNextBlock(void)
// Based on the wall hit, adjust the current coords
switch (Direction)
{
case dirX: m_CurrentX += m_DirX; break;
case dirY: m_CurrentY += m_DirY; break;
case dirZ: m_CurrentZ += m_DirZ; break;
case dirX: m_CurrentX += m_DirX; m_CurrentFace = (m_DirX > 0) ? BLOCK_FACE_EAST : BLOCK_FACE_WEST; break;
case dirY: m_CurrentY += m_DirY; m_CurrentFace = (m_DirY > 0) ? BLOCK_FACE_BOTTOM : BLOCK_FACE_TOP; break;
case dirZ: m_CurrentZ += m_DirZ; m_CurrentFace = (m_DirZ > 0) ? BLOCK_FACE_SOUTH : BLOCK_FACE_NORTH; break;
case dirNONE: return false;
}
return true;
@ -211,7 +212,7 @@ bool cLineBlockTracer::Item(cChunk * a_Chunk)
int RelX = m_CurrentX - a_Chunk->GetPosX() * cChunkDef::Width;
int RelZ = m_CurrentZ - a_Chunk->GetPosZ() * cChunkDef::Width;
a_Chunk->GetBlockTypeMeta(RelX, m_CurrentY, RelZ, BlockType, BlockMeta);
if (m_Callbacks->OnNextBlock(m_CurrentX, m_CurrentY, m_CurrentZ, BlockType, BlockMeta))
if (m_Callbacks->OnNextBlock(m_CurrentX, m_CurrentY, m_CurrentZ, BlockType, BlockMeta, m_CurrentFace))
{
// The callback terminated the trace
return false;
@ -219,7 +220,7 @@ bool cLineBlockTracer::Item(cChunk * a_Chunk)
}
else
{
if (m_Callbacks->OnNextBlockNoData(m_CurrentX, m_CurrentY, m_CurrentZ))
if (m_Callbacks->OnNextBlockNoData(m_CurrentX, m_CurrentY, m_CurrentZ, m_CurrentFace))
{
// The callback terminated the trace
return false;

View File

@ -60,6 +60,9 @@ protected:
// The current block
int m_CurrentX, m_CurrentY, m_CurrentZ;
// The face through which the current block has been entered
char m_CurrentFace;
/// Adjusts the start point above the world to just at the world's top

View File

@ -884,6 +884,49 @@ bool cLuaState::ReportErrors(lua_State * a_LuaState, int a_Status)
void cLuaState::LogStackTrace(void)
{
LOGWARNING("Stack trace:");
lua_Debug entry;
int depth = 0;
while (lua_getstack(m_LuaState, depth, &entry))
{
int status = lua_getinfo(m_LuaState, "Sln", &entry);
assert(status);
LOGWARNING(" %s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "?");
depth++;
}
LOGWARNING("Stack trace end");
}
AString cLuaState::GetTypeText(int a_StackPos)
{
int Type = lua_type(m_LuaState, a_StackPos);
switch (Type)
{
case LUA_TNONE: return "TNONE";
case LUA_TNIL: return "TNIL";
case LUA_TBOOLEAN: return "TBOOLEAN";
case LUA_TLIGHTUSERDATA: return "TLIGHTUSERDATA";
case LUA_TNUMBER: return "TNUMBER";
case LUA_TSTRING: return "TSTRING";
case LUA_TTABLE: return "TTABLE";
case LUA_TFUNCTION: return "TFUNCTION";
case LUA_TUSERDATA: return "TUSERDATA";
case LUA_TTHREAD: return "TTHREAD";
}
return Printf("Unknown (%d)", Type);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cLuaState::cRef:

View File

@ -77,7 +77,7 @@ public:
bool IsValid(void) const {return (m_Ref != LUA_REFNIL); }
/// Allows to use this class wherever an int (i. e. ref) is to be used
operator int(void) { return m_Ref; }
operator int(void) const { return m_Ref; }
protected:
cLuaState & m_LuaState;
@ -782,6 +782,12 @@ public:
/// If the status is nonzero, prints the text on the top of Lua stack and returns true
static bool ReportErrors(lua_State * a_LuaState, int status);
/// Logs all items in the current stack trace to the server console
void LogStackTrace(void);
/// Returns the type of the item on the specified position in the stack
AString GetTypeText(int a_StackPos);
protected:
lua_State * m_LuaState;

View File

@ -755,6 +755,159 @@ static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S)
static int tolua_cPluginManager_AddHook_FnRef(cPluginManager * a_PluginManager, cLuaState & S, int a_ParamIdx)
{
// Helper function for cPluginmanager:AddHook() binding
// Takes care of the new case (#121): args are HOOK_TYPE and CallbackFunction
// The arg types have already been checked
// Retrieve the cPlugin from the LuaState:
cPluginLua * Plugin = GetLuaPlugin(S);
if (Plugin == NULL)
{
// An error message has been already printed in GetLuaPlugin()
return 0;
}
// Retrieve and check the hook type
int HookType = (int)tolua_tonumber(S, a_ParamIdx, -1);
if (!a_PluginManager->IsValidHookType(HookType))
{
LOGWARNING("cPluginManager.AddHook(): Invalid HOOK_TYPE parameter: %d", HookType);
S.LogStackTrace();
return 0;
}
// Add the hook to the plugin
if (!Plugin->AddHookRef(HookType, a_ParamIdx + 1))
{
LOGWARNING("cPluginManager.AddHook(): Cannot add hook %d, unknown error.", HookType);
S.LogStackTrace();
return 0;
}
a_PluginManager->AddHook(Plugin, HookType);
// Success
return 0;
}
static int tolua_cPluginManager_AddHook_DefFn(cPluginManager * a_PluginManager, cLuaState & S, int a_ParamIdx)
{
// Helper function for cPluginmanager:AddHook() binding
// Takes care of the old case (#121): args are cPluginLua and HOOK_TYPE
// The arg types have already been checked
// Retrieve and check the cPlugin parameter
cPluginLua * Plugin = (cPluginLua *)tolua_tousertype(S, a_ParamIdx, NULL);
if (Plugin == NULL)
{
LOGWARNING("cPluginManager.AddHook(): Invalid Plugin parameter, expected a valid cPlugin object. Hook not added");
S.LogStackTrace();
return 0;
}
if (Plugin != GetLuaPlugin(S))
{
// The plugin parameter passed to us is not our stored plugin. Disallow this!
LOGWARNING("cPluginManager.AddHook(): Invalid Plugin parameter, cannot add hook to foreign plugins. Hook not added.");
S.LogStackTrace();
return 0;
}
// Retrieve and check the hook type
int HookType = (int)tolua_tonumber(S, a_ParamIdx + 1, -1);
if (!a_PluginManager->IsValidHookType(HookType))
{
LOGWARNING("cPluginManager.AddHook(): Invalid HOOK_TYPE parameter: %d", HookType);
S.LogStackTrace();
return 0;
}
// Get the standard name for the callback function:
const char * FnName = cPluginLua::GetHookFnName(HookType);
if (FnName == NULL)
{
LOGWARNING("cPluginManager.AddHook(): Unknown hook type (%d). Hook not added.", HookType);
S.LogStackTrace();
return 0;
}
// Retrieve the function to call and add it to the plugin:
lua_pushstring(S, FnName);
bool res = Plugin->AddHookRef(HookType, 1);
lua_pop(S, 1); // Pop the function off the stack
if (!res)
{
LOGWARNING("cPluginManager.AddHook(): Function %s not found. Hook not added.", FnName);
S.LogStackTrace();
return 0;
}
a_PluginManager->AddHook(Plugin, HookType);
// Success
return 0;
}
static int tolua_cPluginManager_AddHook(lua_State * tolua_S)
{
/*
Function signatures:
cPluginManager.AddHook(HOOK_TYPE, CallbackFunction) -- (1) recommended
cPluginManager:Get():AddHook(HOOK_TYPE, CallbackFunction) -- (2) accepted silently
cPluginManager:Get():AddHook(Plugin, HOOK_TYPE) -- (3) old style (#121), accepted but complained about
cPluginManager.AddHook(Plugin, HOOK_TYPE) -- (4) old style (#121) mangled, accepted but complained about
*/
cLuaState S(tolua_S);
cPluginManager * PlgMgr = cPluginManager::Get();
// If the first param is a cPluginManager, use it instead of the global one:
int ParamIdx = 1;
tolua_Error err;
if (tolua_isusertype(S, 1, "cPluginManager", 0, &err))
{
// Style 2 or 3, retrieve the PlgMgr instance
PlgMgr = (cPluginManager *)tolua_tousertype(S, 1, NULL);
if (PlgMgr == NULL)
{
LOGWARNING("Malformed plugin, use cPluginManager.AddHook(HOOK_TYPE, CallbackFunction). Fixing the call for you.");
S.LogStackTrace();
PlgMgr = cPluginManager::Get();
}
ParamIdx += 1;
}
if (lua_isnumber(S, ParamIdx) && lua_isfunction(S, ParamIdx + 1))
{
// The next params are a number and a function, assume style 1 or 2
return tolua_cPluginManager_AddHook_FnRef(PlgMgr, S, ParamIdx);
}
else if (tolua_isusertype(S, ParamIdx, "cPlugin", 0, &err) && lua_isnumber(S, ParamIdx + 1))
{
// The next params are a cPlugin and a number, assume style 3 or 4
LOGINFO("cPluginManager.AddHook(): Deprecated format used, use cPluginManager.AddHook(HOOK_TYPE, CallbackFunction) instead. Fixing the call for you.");
S.LogStackTrace();
return tolua_cPluginManager_AddHook_DefFn(PlgMgr, S, ParamIdx);
}
AString ParamDesc;
Printf(ParamDesc, "%s, %s, %s", S.GetTypeText(1).c_str(), S.GetTypeText(2).c_str(), S.GetTypeText(3).c_str());
LOGWARNING("cPluginManager.AddHook(): bad parameters. Expected HOOK_TYPE and CallbackFunction, got %s. Hook not added.", ParamDesc.c_str());
S.LogStackTrace();
return 0;
}
static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S)
{
int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */
@ -1496,7 +1649,7 @@ public:
{
}
virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override
virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override
{
if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNextBlock"))
{
@ -1508,6 +1661,7 @@ public:
m_LuaState.Push(a_BlockZ);
m_LuaState.Push(a_BlockType);
m_LuaState.Push(a_BlockMeta);
m_LuaState.Push(a_EntryFace);
if (!m_LuaState.CallFunction(1))
{
return false;
@ -1521,7 +1675,7 @@ public:
return res;
}
virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ) override
virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ, char a_EntryFace) override
{
if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNextBlockNoData"))
{
@ -1531,6 +1685,7 @@ public:
m_LuaState.Push(a_BlockX);
m_LuaState.Push(a_BlockY);
m_LuaState.Push(a_BlockZ);
m_LuaState.Push(a_EntryFace);
if (!m_LuaState.CallFunction(1))
{
return false;
@ -1699,6 +1854,7 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "ForEachCommand", tolua_cPluginManager_ForEachCommand);
tolua_function(tolua_S, "ForEachConsoleCommand", tolua_cPluginManager_ForEachConsoleCommand);
tolua_function(tolua_S, "GetAllPlugins", tolua_cPluginManager_GetAllPlugins);
tolua_function(tolua_S, "AddHook", tolua_cPluginManager_AddHook);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cPlayer");

View File

@ -136,17 +136,29 @@ bool cIsThread::Wait(void)
{
return true;
}
LOGD("Waiting for thread %s to finish", m_ThreadName.c_str());
#ifdef LOGD // ProtoProxy doesn't have LOGD
LOGD("Waiting for thread %s to finish", m_ThreadName.c_str());
#endif // LOGD
#ifdef _WIN32
int res = WaitForSingleObject(m_Handle, INFINITE);
m_Handle = NULL;
LOGD("Thread %s finished", m_ThreadName.c_str());
#ifdef LOGD // ProtoProxy doesn't have LOGD
LOGD("Thread %s finished", m_ThreadName.c_str());
#endif // LOGD
return (res == WAIT_OBJECT_0);
#else // _WIN32
int res = pthread_join(m_Handle, NULL);
m_Handle = NULL;
LOGD("Thread %s finished", m_ThreadName.c_str());
#ifdef LOGD // ProtoProxy doesn't have LOGD
LOGD("Thread %s finished", m_ThreadName.c_str());
#endif // LOGD
m_HasStarted = false;
return (res == 0);
#endif // else _WIN32
}

View File

@ -80,7 +80,7 @@ void cListenThread::Stop(void)
super::Wait();
// Close all the listening sockets:
for (cSockets::iterator itr = m_Sockets.begin(), end = m_Sockets.end(); itr != end; ++itr)
for (cSockets::iterator itr = m_Sockets.begin() + 1, end = m_Sockets.end(); itr != end; ++itr)
{
itr->CloseSocket();
} // for itr - m_Sockets[]

View File

@ -74,11 +74,11 @@ void cSocket::CloseSocket()
if (shutdown(m_Socket, SHUT_RDWR) != 0)//SD_BOTH);
{
LOGWARN("Error on shutting down socket (%s): %s", m_IPString.c_str(), GetLastErrorString().c_str());
LOGWARN("Error on shutting down socket %d (%s): %s", m_Socket, m_IPString.c_str(), GetLastErrorString().c_str());
}
if (close(m_Socket) != 0)
{
LOGWARN("Error closing socket (%s): %s", m_IPString.c_str(), GetLastErrorString().c_str());
LOGWARN("Error closing socket %d (%s): %s", m_Socket, m_IPString.c_str(), GetLastErrorString().c_str());
}
#endif // else _WIN32

View File

@ -8,33 +8,30 @@
cTimer::cTimer()
#ifdef _WIN32
: m_TicksPerSecond( new LARGE_INTEGER )
#endif
cTimer::cTimer(void)
{
#ifdef _WIN32
QueryPerformanceFrequency( (LARGE_INTEGER*)m_TicksPerSecond );
#endif
#ifdef _WIN32
QueryPerformanceFrequency(&m_TicksPerSecond);
#endif
}
cTimer::~cTimer()
long long cTimer::GetNowTime(void)
{
#ifdef _WIN32
delete (LARGE_INTEGER*)m_TicksPerSecond;
#endif
#ifdef _WIN32
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
return ((now.QuadPart * 1000) / m_TicksPerSecond.QuadPart);
#else
struct timeval now;
gettimeofday(&now, NULL);
return (long long)(now.tv_sec * 1000 + now.tv_usec / 1000);
#endif
}
long long cTimer::GetNowTime()
{
#ifdef _WIN32
LARGE_INTEGER now;
QueryPerformanceCounter( &now );
LARGE_INTEGER & tps = *((LARGE_INTEGER*)m_TicksPerSecond);
return ((now.QuadPart*1000) / tps.QuadPart );
#else
struct timeval now;
gettimeofday(&now, NULL);
return (long long)(now.tv_sec*1000 + now.tv_usec/1000);
#endif
}

View File

@ -1,15 +1,32 @@
// Timer.h
// Declares the cTimer class representing an OS-independent of retrieving current time with msec accuracy
#pragma once
class cTimer
{
public:
cTimer();
~cTimer();
cTimer(void);
long long GetNowTime();
// Returns the current time expressed in milliseconds
long long GetNowTime(void);
private:
#ifdef _WIN32
void* m_TicksPerSecond; // LARGE_INTEGER*
#endif
};
#ifdef _WIN32
LARGE_INTEGER m_TicksPerSecond;
#endif
} ;

View File

@ -113,13 +113,6 @@ public:
/// All bound console commands are to be removed, do any language-dependent cleanup here
virtual void ClearConsoleCommands(void) {} ;
/** Called from cPluginManager::AddHook() to check if the hook can be added.
Plugin API providers may check if the plugin is written correctly (has the hook handler function)
Returns true if the hook can be added (handler exists)
Descendants should also log the specific error message as a warning if they return false.
*/
virtual bool CanAddHook(cPluginManager::PluginHook a_Hook) = 0;
// tolua_begin
const AString & GetName(void) const { return m_Name; }
void SetName(const AString & a_Name) { m_Name = a_Name; }

File diff suppressed because it is too large Load Diff

View File

@ -100,7 +100,8 @@ public:
virtual void ClearConsoleCommands(void) override;
virtual bool CanAddHook(cPluginManager::PluginHook a_Hook) override;
/// Returns true if the plugin contains the function for the specified hook type, using the old-style registration (#121)
bool CanAddOldStyleHook(int a_HookType);
// cWebPlugin override
virtual const AString GetWebTitle(void) const {return GetName(); }
@ -128,18 +129,35 @@ public:
/// Calls the plugin-specified "cLuaWindow slot changed" callback.
void CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int a_SlotNum);
protected:
cCriticalSection m_CriticalSection;
cLuaState m_LuaState;
/// Returns the name of Lua function that should handle the specified hook type in the older (#121) API
static const char * GetHookFnName(int a_HookType);
/** Adds a Lua function to be called for the specified hook.
The function has to be on the Lua stack at the specified index a_FnRefIdx
Returns true if the hook was added successfully.
*/
bool AddHookRef(int a_HookType, int a_FnRefIdx);
protected:
/// Maps command name into Lua function reference
typedef std::map<AString, int> CommandMap;
/// Provides an array of Lua function references
typedef std::vector<cLuaState::cRef *> cLuaRefs;
/// Maps hook types into arrays of Lua function references to call for each hook type
typedef std::map<int, cLuaRefs> cHookMap;
cCriticalSection m_CriticalSection;
cLuaState m_LuaState;
CommandMap m_Commands;
CommandMap m_ConsoleCommands;
/// Returns the name of Lua function that should handle the specified hook
const char * GetHookFnName(cPluginManager::PluginHook a_Hook);
cHookMap m_HookMap;
/// Releases all Lua references and closes the LuaState
void Close(void);
} ; // tolua_export

View File

@ -61,7 +61,7 @@ void cPluginManager::FindPlugins(void)
// First get a clean list of only the currently running plugins, we don't want to mess those up
for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end();)
{
if( itr->second == NULL )
if (itr->second == NULL)
{
PluginMap::iterator thiz = itr;
++thiz;
@ -1585,6 +1585,15 @@ void cPluginManager::TabCompleteCommand(const AString & a_Text, AStringVector &
bool cPluginManager::IsValidHookType(int a_HookType)
{
return ((a_HookType >= 0) && (a_HookType <= HOOK_MAX));
}
bool cPluginManager::AddPlugin(cPlugin * a_Plugin)
{
m_Plugins[a_Plugin->GetDirectory()] = a_Plugin;
@ -1603,17 +1612,13 @@ bool cPluginManager::AddPlugin(cPlugin * a_Plugin)
void cPluginManager::AddHook(cPlugin * a_Plugin, PluginHook a_Hook)
void cPluginManager::AddHook(cPlugin * a_Plugin, int a_Hook)
{
if (!a_Plugin)
{
LOGWARN("Called cPluginManager::AddHook() with a_Plugin == NULL");
return;
}
if (!a_Plugin->CanAddHook(a_Hook))
{
return;
}
PluginList & Plugins = m_Hooks[a_Hook];
Plugins.remove(a_Plugin);
Plugins.push_back(a_Plugin);

View File

@ -110,6 +110,10 @@ public: // tolua_export
// Note that if a hook type is added, it may need processing in cPlugin::CanAddHook() descendants,
// and it definitely needs adding in cPluginLua::GetHookFnName() !
// Keep these two as the last items, they are used for validity checking and get their values automagically
HOOK_NUM_HOOKS,
HOOK_MAX = HOOK_NUM_HOOKS - 1,
} ;
// tolua_end
@ -133,7 +137,9 @@ public: // tolua_export
void FindPlugins(); // tolua_export
void ReloadPlugins(); // tolua_export
void AddHook( cPlugin* a_Plugin, PluginHook a_Hook ); // tolua_export
/// Adds the plugin to the list of plugins called for the specified hook type. Handles multiple adds as a single add
void AddHook(cPlugin * a_Plugin, int a_HookType);
unsigned int GetNumPlugins() const; // tolua_export
@ -237,6 +243,9 @@ public: // tolua_export
*/
void TabCompleteCommand(const AString & a_Text, AStringVector & a_Results, cPlayer * a_Player);
/// Returns true if the specified hook type is within the allowed range
static bool IsValidHookType(int a_HookType);
private:
friend class cRoot;
@ -248,7 +257,7 @@ private:
AString m_HelpString;
} ;
typedef std::map< cPluginManager::PluginHook, cPluginManager::PluginList > HookMap;
typedef std::map<int, cPluginManager::PluginList> HookMap;
typedef std::map<AString, cCommandReg> CommandMap;
PluginList m_DisablePluginList;

View File

@ -340,9 +340,6 @@ void cProtocol132::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
Flush();
SendCompass(a_World);
// Send the initial position (so that confirmation works, FS #245):
SendPlayerMoveLook();
}

View File

@ -350,6 +350,33 @@ AString & RawBEToUTF8(short * a_RawData, int a_NumShorts, AString & a_UTF8)
// UTF-8 conversion code adapted from:
// http://stackoverflow.com/questions/2867123/convert-utf-16-to-utf-8-under-windows-and-linux-in-c
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Begin of Unicode, Inc.'s code / information
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
Notice from the original file:
* Copyright 2001-2004 Unicode, Inc.
*
* Disclaimer
*
* This source code is provided as is by Unicode, Inc. No claims are
* made as to fitness for any particular purpose. No warranties of any
* kind are expressed or implied. The recipient agrees to determine
* applicability of information provided. If this file has been
* purchased on magnetic or optical media from Unicode, Inc., the
* sole remedy for any claim will be exchange of defective media
* within 90 days of receipt.
*
* Limitations on Rights to Redistribute This Code
*
* Unicode, Inc. hereby grants the right to freely use the information
* supplied in this file in the creation of products supporting the
* Unicode Standard, and to make copies of this file in any form
* for internal or external distribution as long as this notice
* remains attached.
*/
#define UNI_MAX_BMP 0x0000FFFF
#define UNI_MAX_UTF16 0x0010FFFF
#define UNI_MAX_UTF32 0x7FFFFFFF
@ -505,18 +532,24 @@ AString & UTF8ToRawBEUTF16(const char * a_UTF8, size_t a_UTF8Length, AString & a
} while (tmpBytesToRead > 0);
}
--------------------------------------------------------------------- */
---------------------------------------------------------------------
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// End of Unicode, Inc.'s code / information
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
#define HEX(x) ((x) > 9 ? (x) + 'A' - 10 : (x) + '0')
/**
format binary data this way:
00001234: 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 1234567890abcdef
*/
#define HEX(x) ((x) > 9 ? (x) + 'A' - 10 : (x) + '0')
AString & CreateHexDump(AString & a_Out, const void * a_Data, int a_Size, int a_LineLength)
{
ASSERT(a_LineLength <= 120); // Due to using a fixed size line buffer; increase line[]'s size to lift this max

View File

@ -133,9 +133,9 @@ void cTracer::SetValues(const Vector3f & a_Start, const Vector3f & a_Direction)
int cTracer::Trace( const Vector3f & a_Start, const Vector3f & a_Direction, int a_Distance)
{
if (a_Start.y < 0)
if ((a_Start.y < 0) || (a_Start.y >= cChunkDef::Height))
{
LOGD("%s: Start is below the world", __FUNCTION__);
LOGD("%s: Start Y is outside the world (%.2f), not tracing.", __FUNCTION__, a_Start.y);
return 0;
}

View File

@ -30,12 +30,13 @@ public: // tolua_export
void operator -= ( Vector3d* a_V ) { x -= a_V->x; y -= a_V->y; z -= a_V->z; }
void operator *= ( double a_f ) { x *= a_f; y *= a_f; z *= a_f; }
Vector3d operator + ( const Vector3d& v2 ) const { return Vector3d( x + v2.x, y + v2.y, z + v2.z ); } // tolua_export
Vector3d operator + ( const Vector3d* v2 ) const { return Vector3d( x + v2->x, y + v2->y, z + v2->z ); } // tolua_export
Vector3d operator - ( const Vector3d& v2 ) const { return Vector3d( x - v2.x, y - v2.y, z - v2.z ); } // tolua_export
Vector3d operator - ( const Vector3d* v2 ) const { return Vector3d( x - v2->x, y - v2->y, z - v2->z ); } // tolua_export
Vector3d operator * ( const double f ) const { return Vector3d( x * f, y * f, z * f ); } // tolua_export
Vector3d operator * ( const Vector3d& v2 ) const { return Vector3d( x * v2.x, y * v2.y, z * v2.z ); } // tolua_export
Vector3d operator + (const Vector3d & v2) const { return Vector3d(x + v2.x, y + v2.y, z + v2.z ); } // tolua_export
Vector3d operator + (const Vector3d * v2) const { return Vector3d(x + v2->x, y + v2->y, z + v2->z ); } // tolua_export
Vector3d operator - (const Vector3d & v2) const { return Vector3d(x - v2.x, y - v2.y, z - v2.z ); } // tolua_export
Vector3d operator - (const Vector3d * v2) const { return Vector3d(x - v2->x, y - v2->y, z - v2->z ); } // tolua_export
Vector3d operator * (const double f) const { return Vector3d(x * f, y * f, z * f ); } // tolua_export
Vector3d operator * (const Vector3d & v2) const { return Vector3d(x * v2.x, y * v2.y, z * v2.z ); } // tolua_export
Vector3d operator / (const double f) const { return Vector3d(x / f, y / f, z / f ); } // tolua_export
double x, y, z; // tolua_export

View File

@ -5,8 +5,6 @@
#include "World.h"
#include "ChunkDef.h"
#include "ClientHandle.h"
#include "Entities/Pickup.h"
#include "Entities/Player.h"
#include "Server.h"
#include "Item.h"
#include "Root.h"
@ -14,6 +12,11 @@
#include "ChunkMap.h"
#include "OSSupport/Timer.h"
// Entities (except mobs):
#include "Entities/Pickup.h"
#include "Entities/Player.h"
#include "Entities/TNTEntity.h"
// Simulators:
#include "Simulator/SimulatorManager.h"
#include "Simulator/FloodyFluidSimulator.h"
@ -55,7 +58,6 @@
#include "PluginManager.h"
#include "Blocks/BlockHandler.h"
#include "Vector3d.h"
#include "Entities/TNTEntity.h"
#include "Tracer.h"
#include "tolua++.h"
@ -2202,15 +2204,12 @@ cPlayer * cWorld::FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit)
Vector3f Pos = (*itr)->GetPosition();
float Distance = (Pos - a_Pos).Length();
if (Distance <= a_SightLimit)
if (Distance < ClosestDistance)
{
if (!LineOfSight.Trace(a_Pos,(Pos - a_Pos),(int)(Pos - a_Pos).Length()))
{
if (Distance < ClosestDistance)
{
ClosestDistance = Distance;
ClosestPlayer = *itr;
}
ClosestDistance = Distance;
ClosestPlayer = *itr;
}
}
}
@ -2650,6 +2649,26 @@ int cWorld::SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eTyp
int cWorld::CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const Vector3d * a_Speed)
{
cProjectileEntity * Projectile = cProjectileEntity::Create(a_Kind, a_Creator, a_PosX, a_PosY, a_PosZ, a_Speed);
if (Projectile == NULL)
{
return -1;
}
if (!Projectile->Initialize(this))
{
delete Projectile;
return -1;
}
BroadcastSpawnEntity(*Projectile);
return Projectile->GetUniqueID();
}
void cWorld::TabCompleteUserName(const AString & a_Text, AStringVector & a_Results)
{
cCSLock Lock(m_CSPlayers);

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,8 @@
#include "../Entities/Minecart.h"
#include "../Mobs/Monster.h"
#include "../Entities/Pickup.h"
#include "../Entities/ProjectileEntity.h"
@ -330,6 +332,62 @@ void cNBTChunkSerializer::AddPickupEntity(cPickup * a_Pickup)
void cNBTChunkSerializer::AddProjectileEntity(cProjectileEntity * a_Projectile)
{
m_Writer.BeginCompound("");
AddBasicEntity(a_Projectile, a_Projectile->GetMCAClassName());
Vector3d Pos = a_Projectile->GetPosition();
m_Writer.AddShort("xTile", (Int16)floor(Pos.x));
m_Writer.AddShort("yTile", (Int16)floor(Pos.y));
m_Writer.AddShort("zTile", (Int16)floor(Pos.z));
m_Writer.AddShort("inTile", 0); // TODO: Query the block type (is it needed?)
m_Writer.AddShort("shake", 0); // TODO: Any shake?
m_Writer.AddByte ("inGround", a_Projectile->IsInGround() ? 1 : 0);
switch (a_Projectile->GetProjectileKind())
{
case cProjectileEntity::pkArrow:
{
m_Writer.AddByte("inData", 0); // TODO: Query the block meta (is it needed?)
m_Writer.AddByte("pickup", ((cArrowEntity *)a_Projectile)->GetPickupState());
m_Writer.AddDouble("damage", ((cArrowEntity *)a_Projectile)->GetDamageCoeff());
break;
}
case cProjectileEntity::pkGhastFireball:
{
m_Writer.AddInt("ExplosionPower", 1);
// fall-through:
}
case cProjectileEntity::pkFireCharge:
case cProjectileEntity::pkWitherSkull:
{
m_Writer.BeginList("Motion", TAG_Double);
m_Writer.AddDouble("", a_Projectile->GetSpeedX());
m_Writer.AddDouble("", a_Projectile->GetSpeedY());
m_Writer.AddDouble("", a_Projectile->GetSpeedZ());
m_Writer.EndList();
break;
}
default:
{
ASSERT(!"Unsaved projectile entity!");
}
} // switch (ProjectileKind)
cEntity * Creator = a_Projectile->GetCreator();
if (Creator != NULL)
{
if (Creator->GetEntityType() == cEntity::etPlayer)
{
m_Writer.AddString("ownerName", ((cPlayer *)Creator)->GetName());
}
}
m_Writer.EndCompound();
}
void cNBTChunkSerializer::AddMinecartChestContents(cMinecartWithChest * a_Minecart)
{
m_Writer.BeginList("Items", TAG_Compound);
@ -403,10 +461,11 @@ void cNBTChunkSerializer::Entity(cEntity * a_Entity)
switch (a_Entity->GetEntityType())
{
case cEntity::etFallingBlock: AddFallingBlockEntity((cFallingBlock *)a_Entity); break;
case cEntity::etMinecart: AddMinecartEntity ((cMinecart *) a_Entity); break;
case cEntity::etMonster: AddMonsterEntity ((cMonster *) a_Entity); break;
case cEntity::etPickup: AddPickupEntity ((cPickup *) a_Entity); break;
case cEntity::etFallingBlock: AddFallingBlockEntity((cFallingBlock *) a_Entity); break;
case cEntity::etMinecart: AddMinecartEntity ((cMinecart *) a_Entity); break;
case cEntity::etMonster: AddMonsterEntity ((cMonster *) a_Entity); break;
case cEntity::etPickup: AddPickupEntity ((cPickup *) a_Entity); break;
case cEntity::etProjectile: AddProjectileEntity ((cProjectileEntity *)a_Entity); break;
case cEntity::etPlayer: return; // Players aren't saved into the world
default:
{

View File

@ -36,6 +36,7 @@ class cMinecartWithHopper;
class cMonster;
class cPickup;
class cItemGrid;
class cProjectileEntity;
@ -97,6 +98,7 @@ protected:
void AddMinecartEntity (cMinecart * a_Minecart);
void AddMonsterEntity (cMonster * a_Monster);
void AddPickupEntity (cPickup * a_Pickup);
void AddProjectileEntity (cProjectileEntity * a_Projectile);
void AddMinecartChestContents(cMinecartWithChest * a_Minecart);

View File

@ -20,13 +20,13 @@
#include "../Item.h"
#include "../ItemGrid.h"
#include "../StringCompression.h"
#include "../Entities/Entity.h"
#include "../OSSupport/MakeDir.h"
#include "FastNBT.h"
#include "../Mobs/Monster.h"
#include "../Entities/FallingBlock.h"
#include "../Entities/Minecart.h"
#include "../Mobs/Monster.h"
#include "../Entities/Pickup.h"
#include "../Entities/ProjectileEntity.h"
@ -956,6 +956,10 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
LoadPickupFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
if (strncmp(a_IDTag, "Arrow", a_IDTagLength) == 0)
{
LoadArrowFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
// TODO: other entities
}
@ -1043,7 +1047,7 @@ void cWSSAnvil::LoadMinecartTFromNBT(cEntityList & a_Entities, const cParsedNBT
return;
}
//TODO: Everything to do with TNT carts
// TODO: Everything to do with TNT carts
a_Entities.push_back(Minecart.release());
}
@ -1060,7 +1064,7 @@ void cWSSAnvil::LoadMinecartHFromNBT(cEntityList & a_Entities, const cParsedNBT
return;
}
//TODO: Everything to do with hopper carts
// TODO: Everything to do with hopper carts
a_Entities.push_back(Minecart.release());
}
@ -1093,6 +1097,45 @@ void cWSSAnvil::LoadPickupFromNBT(cEntityList & a_Entities, const cParsedNBT & a
void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cArrowEntity> Arrow(new cArrowEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0)));
if (!LoadEntityBaseFromNBT(*Arrow.get(), a_NBT, a_TagIdx))
{
return;
}
// Load pickup state:
int PickupIdx = a_NBT.FindChildByName(a_TagIdx, "pickup");
if (PickupIdx > 0)
{
Arrow->SetPickupState((cArrowEntity::ePickupState)a_NBT.GetByte(PickupIdx));
}
else
{
// Try the older "player" tag:
int PlayerIdx = a_NBT.FindChildByName(a_TagIdx, "player");
if (PlayerIdx > 0)
{
Arrow->SetPickupState((a_NBT.GetByte(PlayerIdx) == 0) ? cArrowEntity::psNoPickup : cArrowEntity::psInSurvivalOrCreative);
}
}
// Load damage:
int DamageIdx = a_NBT.FindChildByName(a_TagIdx, "damage");
if (DamageIdx > 0)
{
Arrow->SetDamageCoeff(a_NBT.GetDouble(DamageIdx));
}
// Store the new arrow in the entities list:
a_Entities.push_back(Arrow.release());
}
bool cWSSAnvil::LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx)
{
double Pos[3];

View File

@ -145,6 +145,7 @@ protected:
void LoadMinecartTFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadMinecartHFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadPickupFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadArrowFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
/// Loads entity common data from the NBT compound; returns true if successful
bool LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx);