Merge remote-tracking branch 'upstream/master'
Conflicts: source/Bindings.cpp source/Entities/Entity.cpp Updated from upsteammaster
commit
20b64e18e4
|
@ -11,3 +11,4 @@ cloc.xsl
|
|||
*.user
|
||||
*.suo
|
||||
/EveryNight.cmd
|
||||
*.sublime-*
|
||||
|
|
|
@ -10,7 +10,7 @@ schematics
|
|||
*.schematic
|
||||
*.ilk
|
||||
*.pdb
|
||||
memdump.xml
|
||||
memdump*
|
||||
*.grab
|
||||
ProtectionAreas.sqlite
|
||||
helgrind.log
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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
|
|
@ -1,5 +1,9 @@
|
|||
.xls
|
||||
Statistics.txt
|
||||
*.bmp
|
||||
Debug/
|
||||
Release/
|
||||
Profiling
|
||||
*.png
|
||||
world/
|
||||
*.html
|
|
@ -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]);
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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"
|
||||
>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
|
@ -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);
|
||||
} ;
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -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);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -143,6 +143,7 @@ typedef short Int16;
|
|||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
>
|
||||
|
|
|
@ -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)
|
|
@ -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"
|
||||
|
|
1377
source/Bindings.cpp
1377
source/Bindings.cpp
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -18,8 +18,8 @@ class cBlockCarpetHandler :
|
|||
public cBlockHandler
|
||||
{
|
||||
public:
|
||||
cBlockCarpetHandler(BLOCKTYPE a_BlockType)
|
||||
: cBlockHandler(a_BlockType)
|
||||
cBlockCarpetHandler(BLOCKTYPE a_BlockType) :
|
||||
cBlockHandler(a_BlockType)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
|
@ -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"
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
} ;
|
||||
|
||||
|
|
|
@ -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;
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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[]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
1444
source/World.h
1444
source/World.h
File diff suppressed because it is too large
Load Diff
|
@ -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:
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue