summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/BlockID.cpp31
-rw-r--r--src/BlockID.h7
-rw-r--r--src/BlockInfo.cpp1
-rw-r--r--src/Blocks/BlockBed.cpp4
-rw-r--r--src/Blocks/BlockCauldron.h18
-rw-r--r--src/Blocks/WorldInterface.h6
-rw-r--r--src/Chunk.cpp75
-rw-r--r--src/ClientHandle.cpp10
-rw-r--r--src/ClientHandle.h2
-rw-r--r--src/Entities/Entity.cpp190
-rw-r--r--src/Entities/Entity.h41
-rw-r--r--src/Entities/Player.cpp76
-rw-r--r--src/Entities/Player.h41
-rw-r--r--src/Generating/BioGen.cpp2
-rw-r--r--src/Generating/ChunkDesc.cpp21
-rw-r--r--src/Generating/ChunkDesc.h3
-rw-r--r--src/Generating/ChunkGenerator.cpp2
-rw-r--r--src/Generating/ComposableGenerator.cpp16
-rw-r--r--src/Generating/EndGen.cpp2
-rw-r--r--src/Generating/EndGen.h3
-rw-r--r--src/Generating/FinishGen.cpp9
-rw-r--r--src/MobSpawner.cpp3
-rw-r--r--src/Mobs/Monster.cpp2
-rw-r--r--src/Protocol/Protocol.h2
-rw-r--r--src/Protocol/Protocol125.cpp8
-rw-r--r--src/Protocol/Protocol125.h2
-rw-r--r--src/Protocol/Protocol16x.cpp4
-rw-r--r--src/Protocol/Protocol16x.h2
-rw-r--r--src/Protocol/Protocol17x.cpp8
-rw-r--r--src/Protocol/Protocol17x.h2
-rw-r--r--src/Protocol/ProtocolRecognizer.cpp4
-rw-r--r--src/Protocol/ProtocolRecognizer.h2
-rw-r--r--src/Root.cpp32
-rw-r--r--src/Root.h13
-rw-r--r--src/UI/Window.cpp2
-rw-r--r--src/World.cpp180
-rw-r--r--src/World.h40
-rw-r--r--src/WorldStorage/NBTChunkSerializer.cpp1
-rw-r--r--src/WorldStorage/WSSAnvil.cpp12
39 files changed, 643 insertions, 236 deletions
diff --git a/src/BlockID.cpp b/src/BlockID.cpp
index af96b4414..d145a2e5b 100644
--- a/src/BlockID.cpp
+++ b/src/BlockID.cpp
@@ -346,6 +346,37 @@ eDimension StringToDimension(const AString & a_DimensionString)
+AString DimensionToString(eDimension a_Dimension)
+{
+ // Decode using a built-in map:
+ static struct
+ {
+ eDimension m_Dimension;
+ const char * m_String;
+ } DimensionMap[] =
+ {
+ { dimOverworld, "Overworld" },
+ { dimNether, "Nether" },
+ { dimEnd, "End" },
+ };
+
+ for (size_t i = 0; i < ARRAYCOUNT(DimensionMap); i++)
+ {
+ if (DimensionMap[i].m_Dimension == a_Dimension)
+ {
+ return DimensionMap[i].m_String;
+ }
+ } // for i - DimensionMap[]
+
+ // Not found
+ LOGWARNING("Unknown dimension: \"%i\". Setting to Overworld", (int)a_Dimension);
+ return "Overworld";
+}
+
+
+
+
+
/// Translates damage type constant to a string representation (built-in).
AString DamageTypeToString(eDamageType a_DamageType)
{
diff --git a/src/BlockID.h b/src/BlockID.h
index 997ee2cf9..08c576886 100644
--- a/src/BlockID.h
+++ b/src/BlockID.h
@@ -920,9 +920,14 @@ extern AString ItemToFullString(const cItem & a_Item);
/// Translates a mob string ("ocelot") to mobtype (E_ENTITY_TYPE_OCELOT)
extern int StringToMobType(const AString & a_MobString);
-/// Translates a dimension string to dimension enum. Takes either a number or a dimension alias (built-in). Returns -1000 on failure
+/// Translates a dimension string to dimension enum. Takes either a number or a dimension alias (built-in). Returns dimOverworld on failure
extern eDimension StringToDimension(const AString & a_DimensionString);
+/** Translates a dimension enum to dimension string.
+Takes an eDimension enum value and returns "Overworld" on failure
+*/
+extern AString DimensionToString(eDimension a_Dimension);
+
/// Translates damage type constant to a string representation (built-in).
extern AString DamageTypeToString(eDamageType a_DamageType);
diff --git a/src/BlockInfo.cpp b/src/BlockInfo.cpp
index 3ec8f100b..602deb26d 100644
--- a/src/BlockInfo.cpp
+++ b/src/BlockInfo.cpp
@@ -450,6 +450,7 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info)
a_Info[E_BLOCK_CROPS ].m_IsSolid = false;
a_Info[E_BLOCK_DANDELION ].m_IsSolid = false;
a_Info[E_BLOCK_DETECTOR_RAIL ].m_IsSolid = false;
+ a_Info[E_BLOCK_END_PORTAL ].m_IsSolid = false;
a_Info[E_BLOCK_FIRE ].m_IsSolid = false;
a_Info[E_BLOCK_FLOWER ].m_IsSolid = false;
a_Info[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE].m_IsSolid = false;
diff --git a/src/Blocks/BlockBed.cpp b/src/Blocks/BlockBed.cpp
index 80ac18560..cd5783f58 100644
--- a/src/Blocks/BlockBed.cpp
+++ b/src/Blocks/BlockBed.cpp
@@ -108,7 +108,7 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
if (Meta & 0x4)
{
- a_Player->SendMessageFailure("This bed is occupied.");
+ a_Player->SendMessageFailure("This bed is occupied");
}
else
{
@@ -133,6 +133,8 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x4); // Where 0x4 = occupied bit
a_Player->SetIsInBed(true);
+ a_Player->SetBedPos(Vector3i(a_BlockX, a_BlockY, a_BlockZ));
+ a_Player->SendMessageSuccess("Home position set successfully");
cTimeFastForwardTester Tester;
if (a_WorldInterface.ForEachPlayer(Tester))
diff --git a/src/Blocks/BlockCauldron.h b/src/Blocks/BlockCauldron.h
index 41b79b6c3..e0f86f4cb 100644
--- a/src/Blocks/BlockCauldron.h
+++ b/src/Blocks/BlockCauldron.h
@@ -58,6 +58,24 @@ public:
{
return true;
}
+
+ virtual void OnUpdate(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cBlockPluginInterface & a_PluginInterface, cChunk & a_Chunk, int a_RelX, int a_RelY, int a_RelZ) override
+ {
+ int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
+ int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
+ if (!a_WorldInterface.IsWeatherWetAt(BlockX, BlockZ) || (a_RelY != a_WorldInterface.GetHeight(BlockX, BlockZ)))
+ {
+ // It's not raining at our current location or we do not have a direct view of the sky
+ // We cannot eat the rain :(
+ return;
+ }
+
+ NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ);
+ if (Meta < 3)
+ {
+ a_Chunk.SetMeta(a_RelX, a_RelY, a_RelZ, Meta + 1);
+ }
+ }
} ;
diff --git a/src/Blocks/WorldInterface.h b/src/Blocks/WorldInterface.h
index a75ee9e26..d1c6f9bfc 100644
--- a/src/Blocks/WorldInterface.h
+++ b/src/Blocks/WorldInterface.h
@@ -46,6 +46,12 @@ public:
virtual void SetTimeOfDay(Int64 a_TimeOfDay) = 0;
+ /** Returns true if it is raining, stormy or snowing at the specified location. This takes into account biomes. */
+ virtual bool IsWeatherWetAt(int a_BlockX, int a_BlockZ) = 0;
+
+ /** Returns the world height at the specified coords; waits for the chunk to get loaded / generated */
+ virtual int GetHeight(int a_BlockX, int a_BlockZ) = 0;
+
/** Wakes up the simulators for the specified block */
virtual void WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ) = 0;
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index 10bc2ff23..b83036b6d 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -577,39 +577,34 @@ void cChunk::Tick(float a_Dt)
m_IsDirty = (*itr)->Tick(a_Dt, *this) | m_IsDirty;
}
- // Tick all entities in this chunk (except mobs):
- for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr)
- {
- // Mobs are tickes inside MobTick (as we don't have to tick them if they are far away from players)
- if (!((*itr)->IsMob()))
+ for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end();)
+ {
+ if (!((*itr)->IsMob())) // Mobs are ticked inside cWorld::TickMobs() (as we don't have to tick them if they are far away from players)
{
+ // Tick all entities in this chunk (except mobs):
(*itr)->Tick(a_Dt, *this);
}
- } // for itr - m_Entitites[]
-
- // Remove all entities that were scheduled for removal:
- for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end();)
- {
- if ((*itr)->IsDestroyed())
- {
- LOGD("Destroying entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass());
- cEntity * ToDelete = *itr;
- itr = m_Entities.erase(itr);
- delete ToDelete;
- ToDelete = NULL;
- continue;
- }
- ++itr;
- } // for itr - m_Entitites[]
-
- // If any entity moved out of the chunk, move it to the neighbor:
- for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end();)
- {
- if (
+
+ if ((*itr)->IsDestroyed()) // Remove all entities that were scheduled for removal:
+ {
+ LOGD("Destroying entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass());
+ MarkDirty();
+ cEntity * ToDelete = *itr;
+ itr = m_Entities.erase(itr);
+ delete ToDelete;
+ }
+ else if ((*itr)->IsWorldTravellingFrom(m_World)) // Remove all entities that are travelling to another world:
+ {
+ MarkDirty();
+ (*itr)->SetWorldTravellingFrom(NULL);
+ itr = m_Entities.erase(itr);
+ }
+ else if ( // If any entity moved out of the chunk, move it to the neighbor:
((*itr)->GetChunkX() != m_PosX) ||
((*itr)->GetChunkZ() != m_PosZ)
)
{
+ MarkDirty();
MoveEntityToNewChunk(*itr);
itr = m_Entities.erase(itr);
}
@@ -617,7 +612,7 @@ void cChunk::Tick(float a_Dt)
{
++itr;
}
- }
+ } // for itr - m_Entitites[]
ApplyWeatherToTop();
}
@@ -902,7 +897,6 @@ void cChunk::ApplyWeatherToTop()
}
break;
} // case (snowy biomes)
- // TODO: Rainy biomes should check for farmland and cauldrons
default:
{
break;
@@ -1798,7 +1792,7 @@ void cChunk::RemoveBlockEntity( cBlockEntity* a_BlockEntity)
-bool cChunk::AddClient(cClientHandle* a_Client)
+bool cChunk::AddClient(cClientHandle * a_Client)
{
for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr)
{
@@ -1829,7 +1823,7 @@ bool cChunk::AddClient(cClientHandle* a_Client)
-void cChunk::RemoveClient( cClientHandle* a_Client)
+void cChunk::RemoveClient(cClientHandle * a_Client)
{
for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr)
{
@@ -1837,7 +1831,7 @@ void cChunk::RemoveClient( cClientHandle* a_Client)
{
continue;
}
-
+
m_LoadedByClient.erase(itr);
if (!a_Client->IsDestroyed())
@@ -1862,7 +1856,7 @@ void cChunk::RemoveClient( cClientHandle* a_Client)
-bool cChunk::HasClient( cClientHandle* a_Client)
+bool cChunk::HasClient(cClientHandle * a_Client)
{
for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr)
{
@@ -1893,9 +1887,9 @@ void cChunk::AddEntity(cEntity * a_Entity)
{
MarkDirty();
}
-
+
ASSERT(std::find(m_Entities.begin(), m_Entities.end(), a_Entity) == m_Entities.end()); // Not there already
-
+
m_Entities.push_back(a_Entity);
}
@@ -1905,17 +1899,12 @@ void cChunk::AddEntity(cEntity * a_Entity)
void cChunk::RemoveEntity(cEntity * a_Entity)
{
- size_t SizeBefore = m_Entities.size();
m_Entities.remove(a_Entity);
- size_t SizeAfter = m_Entities.size();
-
- if (SizeBefore != SizeAfter)
+
+ // Mark as dirty if it was a server-generated entity:
+ if (!a_Entity->IsPlayer())
{
- // Mark as dirty if it was a server-generated entity:
- if (!a_Entity->IsPlayer())
- {
- MarkDirty();
- }
+ MarkDirty();
}
}
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index e4ad218a2..aaed483e2 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -120,7 +120,8 @@ cClientHandle::~cClientHandle()
}
if (World != NULL)
{
- World->RemovePlayer(m_Player);
+ m_Player->SetWorldTravellingFrom(NULL); // Make sure that the player entity is actually removed
+ World->RemovePlayer(m_Player); // Must be called before cPlayer::Destroy() as otherwise cChunk tries to delete the player, and then we do it again
m_Player->Destroy();
}
delete m_Player;
@@ -1795,8 +1796,7 @@ void cClientHandle::RemoveFromWorld(void)
m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ);
} // for itr - Chunks[]
- // StreamChunks() called in cPlayer::MoveToWorld() after new world has been set
- // Meanwhile here, we set last streamed values to bogus ones so everything is resent
+ // Here, we set last streamed values to bogus ones so everything is resent
m_LastStreamedChunkX = 0x7fffffff;
m_LastStreamedChunkZ = 0x7fffffff;
m_HasSentPlayerChunk = false;
@@ -2377,9 +2377,9 @@ void cClientHandle::SendRemoveEntityEffect(const cEntity & a_Entity, int a_Effec
-void cClientHandle::SendRespawn(const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks)
+void cClientHandle::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks)
{
- m_Protocol->SendRespawn(a_World, a_ShouldIgnoreDimensionChecks);
+ m_Protocol->SendRespawn(a_Dimension, a_ShouldIgnoreDimensionChecks);
}
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index 50ed596d5..48eba4de1 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -161,7 +161,7 @@ public:
void SendPlayerSpawn (const cPlayer & a_Player);
void SendPluginMessage (const AString & a_Channel, const AString & a_Message); // Exported in ManualBindings.cpp
void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID);
- void SendRespawn (const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks = false);
+ void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks = false);
void SendExperience (void);
void SendExperienceOrb (const cExpOrb & a_ExpOrb);
void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode);
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 6c3d7b40c..da578013d 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -38,6 +38,7 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
, m_Gravity(-9.81f)
, m_LastPos(a_X, a_Y, a_Z)
, m_IsInitialized(false)
+ , m_WorldTravellingFrom(NULL)
, m_EntityType(a_EntityType)
, m_World(NULL)
, m_IsFireproof(false)
@@ -617,9 +618,12 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
// Handle drowning
HandleAir();
}
-
- // None of the above functions change position, we remain in the chunk of NextChunk
- HandlePhysics(a_Dt, *NextChunk);
+
+ if (!DetectPortal()) // Our chunk is invalid if we have moved to another world
+ {
+ // None of the above functions changed position, we remain in the chunk of NextChunk
+ HandlePhysics(a_Dt, *NextChunk);
+ }
}
}
@@ -856,7 +860,7 @@ void cEntity::TickBurning(cChunk & a_Chunk)
// Remember the current burning state:
bool HasBeenBurning = (m_TicksLeftBurning > 0);
- if (m_World->IsWeatherWet())
+ if (GetWorld()->IsWeatherWetAt(POSX_TOINT, POSZ_TOINT))
{
if (POSY_TOINT > m_World->GetHeight(POSX_TOINT, POSZ_TOINT))
{
@@ -1027,6 +1031,184 @@ void cEntity::DetectCacti(void)
+bool cEntity::DetectPortal()
+{
+ if (GetWorld()->GetDimension() == dimOverworld)
+ {
+ if (GetWorld()->GetNetherWorldName().empty() && GetWorld()->GetEndWorldName().empty())
+ {
+ // Teleportation to either dimension not enabled, don't bother proceeding
+ return false;
+ }
+ }
+ else if (GetWorld()->GetLinkedOverworldName().empty())
+ {
+ // Overworld teleportation disabled, abort
+ return false;
+ }
+
+ int X = POSX_TOINT, Y = POSY_TOINT, Z = POSZ_TOINT;
+ if ((Y > 0) && (Y < cChunkDef::Height))
+ {
+ switch (GetWorld()->GetBlock(X, Y, Z))
+ {
+ case E_BLOCK_NETHER_PORTAL:
+ {
+ if (m_PortalCooldownData.m_ShouldPreventTeleportation)
+ {
+ // Just exited a portal, don't teleport again
+ return false;
+ }
+
+ if (IsPlayer() && !((cPlayer *)this)->IsGameModeCreative() && m_PortalCooldownData.m_TicksDelayed != 80)
+ {
+ // Delay teleportation for four seconds if the entity is a non-creative player
+ m_PortalCooldownData.m_TicksDelayed++;
+ return false;
+ }
+ m_PortalCooldownData.m_TicksDelayed = 0;
+
+ switch (GetWorld()->GetDimension())
+ {
+ case dimNether:
+ {
+ if (GetWorld()->GetLinkedOverworldName().empty())
+ {
+ return false;
+ }
+
+ m_PortalCooldownData.m_ShouldPreventTeleportation = true; // Stop portals from working on respawn
+
+ if (IsPlayer())
+ {
+ ((cPlayer *)this)->GetClientHandle()->SendRespawn(dimOverworld); // Send a respawn packet before world is loaded/generated so the client isn't left in limbo
+ }
+
+ return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName()), false);
+ }
+ case dimOverworld:
+ {
+ if (GetWorld()->GetNetherWorldName().empty())
+ {
+ return false;
+ }
+
+ m_PortalCooldownData.m_ShouldPreventTeleportation = true;
+
+ if (IsPlayer())
+ {
+ ((cPlayer *)this)->AwardAchievement(achEnterPortal);
+ ((cPlayer *)this)->GetClientHandle()->SendRespawn(dimNether);
+ }
+
+ return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetNetherWorldName(), dimNether, GetWorld()->GetName()), false);
+ }
+ default: return false;
+ }
+ }
+ case E_BLOCK_END_PORTAL:
+ {
+ if (m_PortalCooldownData.m_ShouldPreventTeleportation)
+ {
+ return false;
+ }
+
+ switch (GetWorld()->GetDimension())
+ {
+ case dimEnd:
+ {
+ if (GetWorld()->GetLinkedOverworldName().empty())
+ {
+ return false;
+ }
+
+ m_PortalCooldownData.m_ShouldPreventTeleportation = true;
+
+ if (IsPlayer())
+ {
+ cPlayer * Player = (cPlayer *)this;
+ Player->TeleportToCoords(Player->GetLastBedPos().x, Player->GetLastBedPos().y, Player->GetLastBedPos().z);
+ Player->GetClientHandle()->SendRespawn(dimOverworld);
+ }
+
+ return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedOverworldName()), false);
+ }
+ case dimOverworld:
+ {
+ if (GetWorld()->GetEndWorldName().empty())
+ {
+ return false;
+ }
+
+ m_PortalCooldownData.m_ShouldPreventTeleportation = true;
+
+ if (IsPlayer())
+ {
+ ((cPlayer *)this)->AwardAchievement(achEnterTheEnd);
+ ((cPlayer *)this)->GetClientHandle()->SendRespawn(dimEnd);
+ }
+
+ return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetEndWorldName(), dimEnd, GetWorld()->GetName()), false);
+ }
+ default: return false;
+ }
+ }
+ default: break;
+ }
+ }
+
+ // Allow portals to work again
+ m_PortalCooldownData.m_ShouldPreventTeleportation = false;
+ m_PortalCooldownData.m_TicksDelayed = 0;
+ return false;
+}
+
+
+
+
+
+bool cEntity::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
+{
+ UNUSED(a_ShouldSendRespawn);
+ ASSERT(a_World != NULL);
+
+ if (GetWorld() == a_World)
+ {
+ // Don't move to same world
+ return false;
+ }
+
+ // Remove all links to the old world
+ SetWorldTravellingFrom(GetWorld()); // cChunk::Tick() handles entity removal
+ GetWorld()->BroadcastDestroyEntity(*this);
+
+ // Queue add to new world
+ a_World->AddEntity(this);
+ SetWorld(a_World);
+
+ return true;
+}
+
+
+
+
+
+bool cEntity::MoveToWorld(const AString & a_WorldName, bool a_ShouldSendRespawn)
+{
+ cWorld * World = cRoot::Get()->GetWorld(a_WorldName);
+ if (World == NULL)
+ {
+ LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName.c_str());
+ return false;
+ }
+
+ return DoMoveToWorld(World, a_ShouldSendRespawn);
+}
+
+
+
+
+
void cEntity::SetSwimState(cChunk & a_Chunk)
{
int RelY = (int)floor(GetPosY() + 0.1);
diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h
index efca60a6c..e66194ca2 100644
--- a/src/Entities/Entity.h
+++ b/src/Entities/Entity.h
@@ -336,6 +336,11 @@ public:
/** Detects the time for application of cacti damage */
virtual void DetectCacti(void);
+
+ /** Detects whether we are in a portal block and begins teleportation procedures if so
+ Returns true if MoveToWorld() was called, false if not
+ */
+ virtual bool DetectPortal(void);
/// Handles when the entity is in the void
virtual void TickInVoid(cChunk & a_Chunk);
@@ -378,8 +383,22 @@ public:
/// Teleports to the coordinates specified
virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ);
+
+ /** Moves entity to specified world, taking a world pointer */
+ bool MoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn = true) { return DoMoveToWorld(a_World, a_ShouldSendRespawn); }
+
+ /** Moves entity to specified world, taking a world name */
+ bool MoveToWorld(const AString & a_WorldName, bool a_ShouldSendRespawn = true);
// tolua_end
+
+ virtual bool DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn);
+
+ /** Returns if the entity is travelling away from a specified world */
+ bool IsWorldTravellingFrom(cWorld * a_World) const { return (m_WorldTravellingFrom == a_World); }
+
+ /** Sets the world the entity will be leaving */
+ void SetWorldTravellingFrom(cWorld * a_World) { m_WorldTravellingFrom = a_World; }
/// Updates clients of changes in the entity.
virtual void BroadcastMovementUpdate(const cClientHandle * a_Exclude = NULL);
@@ -482,6 +501,12 @@ protected:
/** True when entity is initialised (Initialize()) and false when destroyed pending deletion (Destroy()) */
bool m_IsInitialized;
+ /** World entity is travelling from
+ Set to a valid world pointer by MoveToWorld; reset to NULL when the entity is removed from the old world
+ Can't be a simple boolean as context switches between worlds may leave the new chunk processing (and therefore immediately removing) the entity before the old chunk could remove it
+ */
+ cWorld * m_WorldTravellingFrom;
+
eEntityType m_EntityType;
cWorld * m_World;
@@ -503,7 +528,6 @@ protected:
/// Time, in ticks, since the last damage dealt by the void. Reset to zero when moving out of the void.
int m_TicksSinceLastVoidDamage;
-
/** Does the actual speed-setting. The default implementation just sets the member variable value;
overrides can provide further processing, such as forcing players to move at the given speed. */
@@ -523,6 +547,21 @@ protected:
/** Air level of a mobile */
int m_AirLevel;
int m_AirTickTimer;
+
+ /** Structure storing the portal delay timer and cooldown boolean */
+ struct sPortalCooldownData
+ {
+ /** Ticks since entry of portal, used to delay teleportation */
+ unsigned short m_TicksDelayed;
+
+ /** Whether the entity has just exited the portal, and should therefore not be teleported again
+ This prevents teleportation loops, and is reset when the entity has moved out of the portal
+ */
+ bool m_ShouldPreventTeleportation;
+ };
+
+ /** Portal delay timer and cooldown boolean data */
+ sPortalCooldownData m_PortalCooldownData;
/** The number of ticks this entity has been alive for */
long int m_TicksAlive;
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index afe5965aa..ffdcca2ec 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -88,13 +88,14 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) :
m_PlayerName = a_PlayerName;
- if (!LoadFromDisk())
+ cWorld * World = NULL;
+ if (!LoadFromDisk(World))
{
m_Inventory.Clear();
- cWorld * DefaultWorld = cRoot::Get()->GetDefaultWorld();
- SetPosX(DefaultWorld->GetSpawnX());
- SetPosY(DefaultWorld->GetSpawnY());
- SetPosZ(DefaultWorld->GetSpawnZ());
+ SetPosX(World->GetSpawnX());
+ SetPosY(World->GetSpawnY());
+ SetPosZ(World->GetSpawnZ());
+ SetBedPos(Vector3i((int)World->GetSpawnX(), (int)World->GetSpawnY(), (int)World->GetSpawnZ()));
LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}",
a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ()
@@ -107,11 +108,6 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) :
if (m_GameMode == gmNotSet)
{
- cWorld * World = cRoot::Get()->GetWorld(GetLoadedWorldName());
- if (World == NULL)
- {
- World = cRoot::Get()->GetDefaultWorld();
- }
if (World->IsGameModeCreative())
{
m_CanFly = true;
@@ -140,8 +136,6 @@ cPlayer::~cPlayer(void)
SaveToDisk();
- m_World->RemovePlayer( this);
-
m_ClientHandle = NULL;
delete m_InventoryWindow;
@@ -157,8 +151,6 @@ cPlayer::~cPlayer(void)
void cPlayer::Destroyed()
{
CloseWindow(false);
-
- m_ClientHandle = NULL;
}
@@ -983,12 +975,12 @@ void cPlayer::Respawn(void)
m_LifetimeTotalXp = 0;
// ToDo: send score to client? How?
- m_ClientHandle->SendRespawn(*m_World, true);
+ m_ClientHandle->SendRespawn(GetWorld()->GetDimension(), true);
// Extinguish the fire:
StopBurning();
- TeleportToCoords(GetWorld()->GetSpawnX(), GetWorld()->GetSpawnY(), GetWorld()->GetSpawnZ());
+ TeleportToCoords(GetLastBedPos().x, GetLastBedPos().y, GetLastBedPos().z);
SetVisible(true);
}
@@ -1617,29 +1609,29 @@ void cPlayer::TossItems(const cItems & a_Items)
-bool cPlayer::MoveToWorld(const char * a_WorldName)
+bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
{
- cWorld * World = cRoot::Get()->GetWorld(a_WorldName);
- if (World == NULL)
+ ASSERT(a_World != NULL);
+
+ if (GetWorld() == a_World)
{
- LOG("%s: Couldn't find world \"%s\".", __FUNCTION__, a_WorldName);
+ // Don't move to same world
return false;
}
// Send the respawn packet:
- if (m_ClientHandle != NULL)
+ if (a_ShouldSendRespawn && (m_ClientHandle != NULL))
{
- m_ClientHandle->SendRespawn(*World);
+ m_ClientHandle->SendRespawn(a_World->GetDimension());
}
- // Remove all links to the old world
- m_World->RemovePlayer(this);
-
- // If the dimension is different, we can send the respawn packet
- // http://wiki.vg/Protocol#0x09 says "don't send if dimension is the same" as of 2013_07_02
+ // Remove player from the old world
+ SetWorldTravellingFrom(GetWorld()); // cChunk handles entity removal
+ GetWorld()->RemovePlayer(this);
// Queue adding player to the new world, including all the necessary adjustments to the object
- World->AddPlayer(this);
+ a_World->AddPlayer(this);
+ SetWorld(a_World); // Chunks may be streamed before cWorld::AddPlayer() sets the world to the new value
return true;
}
@@ -1687,13 +1679,12 @@ void cPlayer::LoadPermissionsFromDisk()
-
-bool cPlayer::LoadFromDisk(void)
+bool cPlayer::LoadFromDisk(cWorldPtr & a_World)
{
LoadPermissionsFromDisk();
// Load from the UUID file:
- if (LoadFromFile(GetUUIDFileName(m_UUID)))
+ if (LoadFromFile(GetUUIDFileName(m_UUID), a_World))
{
return true;
}
@@ -1702,7 +1693,7 @@ bool cPlayer::LoadFromDisk(void)
AString OfflineUUID = cClientHandle::GenerateOfflineUUID(GetName());
if (cRoot::Get()->GetServer()->ShouldLoadOfflinePlayerData())
{
- if (LoadFromFile(GetUUIDFileName(OfflineUUID)))
+ if (LoadFromFile(GetUUIDFileName(OfflineUUID), a_World))
{
return true;
}
@@ -1712,7 +1703,7 @@ bool cPlayer::LoadFromDisk(void)
if (cRoot::Get()->GetServer()->ShouldLoadNamedPlayerData())
{
AString OldStyleFileName = Printf("players/%s.json", GetName().c_str());
- if (LoadFromFile(OldStyleFileName))
+ if (LoadFromFile(OldStyleFileName, a_World))
{
// Save in new format and remove the old file
if (SaveToDisk())
@@ -1727,6 +1718,11 @@ bool cPlayer::LoadFromDisk(void)
LOG("Player data file not found for %s (%s, offline %s), will be reset to defaults.",
GetName().c_str(), m_UUID.c_str(), OfflineUUID.c_str()
);
+
+ if (a_World == NULL)
+ {
+ a_World = cRoot::Get()->GetDefaultWorld();
+ }
return false;
}
@@ -1734,7 +1730,7 @@ bool cPlayer::LoadFromDisk(void)
-bool cPlayer::LoadFromFile(const AString & a_FileName)
+bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World)
{
// Load the data from the file:
cFile f;
@@ -1799,6 +1795,11 @@ bool cPlayer::LoadFromFile(const AString & a_FileName)
cEnderChestEntity::LoadFromJson(root["enderchestinventory"], m_EnderChestContents);
m_LoadedWorldName = root.get("world", "world").asString();
+ a_World = cRoot::Get()->GetWorld(GetLoadedWorldName(), true);
+
+ m_LastBedPos.x = root.get("SpawnX", a_World->GetSpawnX()).asInt();
+ m_LastBedPos.y = root.get("SpawnY", a_World->GetSpawnY()).asInt();
+ m_LastBedPos.z = root.get("SpawnZ", a_World->GetSpawnZ()).asInt();
// Load the player stats.
// We use the default world name (like bukkit) because stats are shared between dimensions/worlds.
@@ -1806,7 +1807,7 @@ bool cPlayer::LoadFromFile(const AString & a_FileName)
StatSerializer.Load();
LOGD("Player %s was read from file \"%s\", spawning at {%.2f, %.2f, %.2f} in world \"%s\"",
- GetName().c_str(), a_FileName.c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str()
+ GetName().c_str(), a_FileName.c_str(), GetPosX(), GetPosY(), GetPosZ(), a_World->GetName().c_str()
);
return true;
@@ -1818,7 +1819,6 @@ bool cPlayer::LoadFromFile(const AString & a_FileName)
bool cPlayer::SaveToDisk()
{
- cFile::CreateFolder(FILE_IO_PREFIX + AString("players"));
cFile::CreateFolder(FILE_IO_PREFIX + AString("players/") + m_UUID.substr(0, 2));
// create the JSON data
@@ -1853,6 +1853,10 @@ bool cPlayer::SaveToDisk()
root["foodExhaustion"] = m_FoodExhaustionLevel;
root["isflying"] = IsFlying();
root["lastknownname"] = GetName();
+ root["SpawnX"] = GetLastBedPos().x;
+ root["SpawnY"] = GetLastBedPos().y;
+ root["SpawnZ"] = GetLastBedPos().z;
+
if (m_World != NULL)
{
root["world"] = m_World->GetName();
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index 69149c90b..65c1e33a8 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -131,7 +131,7 @@ public:
inline const cItem & GetEquippedItem(void) const { return GetInventory().GetEquippedItem(); } // tolua_export
- /** Returns whether the player is climbing (ladders, vines e.t.c). */
+ /** Returns whether the player is climbing (ladders, vines etc.) */
bool IsClimbing(void) const;
virtual void TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) override;
@@ -325,7 +325,7 @@ public:
virtual void KilledBy(TakeDamageInfo & a_TDI) override;
virtual void Killed(cEntity * a_Victim) override;
-
+
void Respawn(void); // tolua_export
void SetVisible( bool a_bVisible); // tolua_export
@@ -333,17 +333,24 @@ public:
/** Moves the player to the specified world.
Returns true if successful, false on failure (world not found). */
- bool MoveToWorld(const char * a_WorldName); // tolua_export
+ virtual bool DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn) override;
+ /** Saves all player data, such as inventory, to JSON */
bool SaveToDisk(void);
+
+ typedef cWorld * cWorldPtr;
- /** Loads the player data from the disk file.
- Returns true on success, false on failure. */
- bool LoadFromDisk(void);
+ /** Loads the player data from the disk file
+ Sets a_World to the world where the player will spawn, based on the stored world name or the default world by calling LoadFromFile()
+ Returns true on success, false on failure
+ */
+ bool LoadFromDisk(cWorldPtr & a_World);
- /** Loads the player data from the specified file.
- Returns true on success, false on failure. */
- bool LoadFromFile(const AString & a_FileName);
+ /** Loads the player data from the specified file
+ Sets a_World to the world where the player will spawn, based on the stored world name or the default world
+ Returns true on success, false on failure
+ */
+ bool LoadFromFile(const AString & a_FileName, cWorldPtr & a_World);
void LoadPermissionsFromDisk(void); // tolua_export
@@ -355,8 +362,7 @@ public:
void SendExperience(void);
- // In UI windows, the item that the player is dragging:
- bool IsDraggingItem(void) const { return !m_DraggingItem.IsEmpty(); }
+ /** In UI windows, get the item that the player is dragging */
cItem & GetDraggingItem(void) {return m_DraggingItem; }
// In UI windows, when inventory-painting:
@@ -404,11 +410,20 @@ public:
/** If true the player can fly even when he's not in creative. */
void SetCanFly(bool a_CanFly);
+ /** Gets the last position that the player slept in
+ This is initialised to the world spawn point if the player has not slept in a bed as of yet
+ */
+ Vector3i GetLastBedPos(void) const { return m_LastBedPos; }
+
+ /** Sets the player's bed (home) position */
+ void SetBedPos(const Vector3i & a_Pos) { m_LastBedPos = a_Pos; }
+
/** Update movement-related statistics. */
void UpdateMovementStats(const Vector3d & a_DeltaPos);
/** Returns wheter the player can fly or not. */
virtual bool CanFly(void) const { return m_CanFly; }
+
// tolua_end
// cEntity overrides:
@@ -466,6 +481,9 @@ protected:
cWindow * m_CurrentWindow;
cWindow * m_InventoryWindow;
+ /** The player's last saved bed position */
+ Vector3i m_LastBedPos;
+
char m_Color;
eGameMode m_GameMode;
@@ -540,7 +558,6 @@ protected:
If no ClientHandle is given, the UUID is initialized to empty. */
AString m_UUID;
-
/** Sets the speed and sends it to the client, so that they are forced to move so. */
virtual void DoSetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ) override;
diff --git a/src/Generating/BioGen.cpp b/src/Generating/BioGen.cpp
index d314fc13e..8fad9f5c9 100644
--- a/src/Generating/BioGen.cpp
+++ b/src/Generating/BioGen.cpp
@@ -95,7 +95,7 @@ void cBioGenConstant::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap
void cBioGenConstant::InitializeBiomeGen(cIniFile & a_IniFile)
{
- AString Biome = a_IniFile.GetValueSet("Generator", "ConstantBiome", "Plains");
+ AString Biome = a_IniFile.GetValueSet("Generator", "ConstantBiome", "");
m_Biome = StringToBiome(Biome);
if (m_Biome == biInvalidBiome)
{
diff --git a/src/Generating/ChunkDesc.cpp b/src/Generating/ChunkDesc.cpp
index e4b305022..c0b646fd0 100644
--- a/src/Generating/ChunkDesc.cpp
+++ b/src/Generating/ChunkDesc.cpp
@@ -20,7 +20,6 @@ cChunkDesc::cChunkDesc(int a_ChunkX, int a_ChunkZ) :
m_bUseDefaultBiomes(true),
m_bUseDefaultHeight(true),
m_bUseDefaultComposition(true),
- m_bUseDefaultStructures(true),
m_bUseDefaultFinish(true)
{
m_BlockArea.Create(cChunkDef::Width, cChunkDef::Height, cChunkDef::Width);
@@ -207,26 +206,6 @@ bool cChunkDesc::IsUsingDefaultComposition(void) const
-void cChunkDesc::SetUseDefaultStructures(bool a_bUseDefaultStructures)
-{
- LOGWARNING("%s: Structures are no longer accounted for, use Finishers instead", __FUNCTION__);
- m_bUseDefaultStructures = a_bUseDefaultStructures;
-}
-
-
-
-
-
-bool cChunkDesc::IsUsingDefaultStructures(void) const
-{
- LOGWARNING("%s: Structures are no longer accounted for, use Finishers instead", __FUNCTION__);
- return m_bUseDefaultStructures;
-}
-
-
-
-
-
void cChunkDesc::SetUseDefaultFinish(bool a_bUseDefaultFinish)
{
m_bUseDefaultFinish = a_bUseDefaultFinish;
diff --git a/src/Generating/ChunkDesc.h b/src/Generating/ChunkDesc.h
index b306b1ee5..eeea0c957 100644
--- a/src/Generating/ChunkDesc.h
+++ b/src/Generating/ChunkDesc.h
@@ -68,8 +68,6 @@ public:
bool IsUsingDefaultHeight(void) const;
void SetUseDefaultComposition(bool a_bUseDefaultComposition);
bool IsUsingDefaultComposition(void) const;
- void SetUseDefaultStructures(bool a_bUseDefaultStructures);
- bool IsUsingDefaultStructures(void) const;
void SetUseDefaultFinish(bool a_bUseDefaultFinish);
bool IsUsingDefaultFinish(void) const;
@@ -214,7 +212,6 @@ private:
bool m_bUseDefaultBiomes;
bool m_bUseDefaultHeight;
bool m_bUseDefaultComposition;
- bool m_bUseDefaultStructures;
bool m_bUseDefaultFinish;
} ; // tolua_export
diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp
index 0baf1e991..3d5af152c 100644
--- a/src/Generating/ChunkGenerator.cpp
+++ b/src/Generating/ChunkGenerator.cpp
@@ -52,7 +52,7 @@ bool cChunkGenerator::Start(cPluginInterface & a_PluginInterface, cChunkSink & a
m_ChunkSink = &a_ChunkSink;
MTRand rnd;
- m_Seed = a_IniFile.GetValueSetI("Seed", "Seed", rnd.randInt());
+ m_Seed = a_IniFile.GetValueSetI("Seed", "Seed", (int)rnd.randInt());
AString GeneratorName = a_IniFile.GetValueSet("Generator", "Generator", "Composable");
if (NoCaseCompare(GeneratorName, "Noise3D") == 0)
diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp
index a0355ff8d..a7659149a 100644
--- a/src/Generating/ComposableGenerator.cpp
+++ b/src/Generating/ComposableGenerator.cpp
@@ -45,7 +45,6 @@ cTerrainCompositionGen * cTerrainCompositionGen::CreateCompositionGen(cIniFile &
{
LOGWARN("[Generator] CompositionGen value not set in world.ini, using \"Biomal\".");
CompoGenName = "Biomal";
- a_IniFile.SetValue("Generator", "CompositionGen", CompoGenName);
}
cTerrainCompositionGen * res = NULL;
@@ -99,7 +98,6 @@ cTerrainCompositionGen * cTerrainCompositionGen::CreateCompositionGen(cIniFile &
else
{
LOGWARN("Unknown CompositionGen \"%s\", using \"Biomal\" instead.", CompoGenName.c_str());
- a_IniFile.DeleteValue("Generator", "CompositionGen");
a_IniFile.SetValue("Generator", "CompositionGen", "Biomal");
return CreateCompositionGen(a_IniFile, a_BiomeGen, a_HeightGen, a_Seed);
}
@@ -297,19 +295,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
int Seed = m_ChunkGenerator.GetSeed();
eDimension Dimension = StringToDimension(a_IniFile.GetValue("General", "Dimension", "Overworld"));
- // Older configuration used "Structures" in addition to "Finishers"; we don't distinguish between the two anymore (#398)
- // Therefore, we load Structures from the ini file for compatibility, but move its contents over to Finishers:
- AString Structures = a_IniFile.GetValue("Generator", "Structures", "");
- AString Finishers = a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, LavaLakes, OreNests, Trees, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, PreSimulator");
- if (!Structures.empty())
- {
- LOGINFO("[Generator].Structures is deprecated, moving the contents to [Generator].Finishers.");
- // Structures used to generate before Finishers, so place them first:
- Structures.append(", ");
- Finishers = Structures + Finishers;
- a_IniFile.SetValue("Generator", "Finishers", Finishers);
- }
- a_IniFile.DeleteValue("Generator", "Structures");
+ AString Finishers = a_IniFile.GetValueSet("Generator", "Finishers", "");
// Create all requested finishers:
AStringVector Str = StringSplitAndTrim(Finishers, ",");
diff --git a/src/Generating/EndGen.cpp b/src/Generating/EndGen.cpp
index abbf050f7..c94cd1eff 100644
--- a/src/Generating/EndGen.cpp
+++ b/src/Generating/EndGen.cpp
@@ -50,7 +50,7 @@ cEndGen::cEndGen(int a_Seed) :
-void cEndGen::Initialize(cIniFile & a_IniFile)
+void cEndGen::InitializeCompoGen(cIniFile & a_IniFile)
{
m_IslandSizeX = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeX", m_IslandSizeX);
m_IslandSizeY = a_IniFile.GetValueSetI("Generator", "EndGenIslandSizeY", m_IslandSizeY);
diff --git a/src/Generating/EndGen.h b/src/Generating/EndGen.h
index 4904a0e3d..322061810 100644
--- a/src/Generating/EndGen.h
+++ b/src/Generating/EndGen.h
@@ -23,8 +23,6 @@ class cEndGen :
public:
cEndGen(int a_Seed);
- void Initialize(cIniFile & a_IniFile);
-
protected:
/// Seed for the noise
@@ -66,4 +64,5 @@ protected:
// cTerrainCompositionGen overrides:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
+ virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
} ;
diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp
index 842c9ccc5..f53addb68 100644
--- a/src/Generating/FinishGen.cpp
+++ b/src/Generating/FinishGen.cpp
@@ -135,6 +135,15 @@ void cFinishGenNetherClumpFoliage::TryPlaceClump(cChunkDesc & a_ChunkDesc, int a
int zz = a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z;
for (int y = MinY; y < MaxY; y++)
{
+ if (
+ ((x < 0) || (x >= cChunkDef::Width)) ||
+ ((y < 0) || (y >= cChunkDef::Height)) ||
+ ((z < 0) || (z >= cChunkDef::Width))
+ )
+ {
+ continue;
+ }
+
if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) // Don't replace non air blocks.
{
continue;
diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp
index 454fabce8..db05b70af 100644
--- a/src/MobSpawner.cpp
+++ b/src/MobSpawner.cpp
@@ -241,7 +241,8 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R
(m_Random.NextInt(2, a_Biome) == 0)
);
}
-
+
+ case cMonster::mtMagmaCube:
case cMonster::mtSlime:
{
return (
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index c1247c1f8..622a67816 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -1015,7 +1015,7 @@ void cMonster::HandleDaylightBurning(cChunk & a_Chunk)
(a_Chunk.GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand
(GetWorld()->GetTimeOfDay() < (12000 + 1000)) && // It is nighttime
!IsOnFire() && // Not already burning
- (GetWorld()->GetWeather() != eWeather_Rain) // Not raining
+ GetWorld()->IsWeatherWetAt(POSX_TOINT, POSZ_TOINT) // Not raining
)
{
// Burn for 100 ticks, then decide again
diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h
index 166e9ab1b..d110f2af9 100644
--- a/src/Protocol/Protocol.h
+++ b/src/Protocol/Protocol.h
@@ -100,7 +100,7 @@ public:
virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0;
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) = 0;
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) = 0;
- virtual void SendRespawn (const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks = false) = 0;
+ virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) = 0;
virtual void SendExperience (void) = 0;
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) = 0;
virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) = 0;
diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp
index cad47e6c2..538d31642 100644
--- a/src/Protocol/Protocol125.cpp
+++ b/src/Protocol/Protocol125.cpp
@@ -833,23 +833,23 @@ void cProtocol125::SendRemoveEntityEffect(const cEntity & a_Entity, int a_Effect
-void cProtocol125::SendRespawn(const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks)
+void cProtocol125::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks)
{
cCSLock Lock(m_CSPacket);
- if ((m_LastSentDimension == a_World.GetDimension()) && !a_ShouldIgnoreDimensionChecks)
+ if ((m_LastSentDimension == a_Dimension) && !a_ShouldIgnoreDimensionChecks)
{
// Must not send a respawn for the world with the same dimension, the client goes cuckoo if we do (unless we are respawning from death)
return;
}
cPlayer * Player = m_Client->GetPlayer();
WriteByte (PACKET_RESPAWN);
- WriteInt (a_World.GetDimension());
+ WriteInt ((int)(a_Dimension));
WriteByte (2); // TODO: Difficulty; 2 = Normal
WriteChar ((char)Player->GetGameMode());
WriteShort (256); // Current world height
WriteString("default");
Flush();
- m_LastSentDimension = a_World.GetDimension();
+ m_LastSentDimension = a_Dimension;
}
diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h
index bf08b6c50..18efeb079 100644
--- a/src/Protocol/Protocol125.h
+++ b/src/Protocol/Protocol125.h
@@ -72,7 +72,7 @@ public:
virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
- virtual void SendRespawn (const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks = false) override;
+ virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override;
virtual void SendExperience (void) override;
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;
diff --git a/src/Protocol/Protocol16x.cpp b/src/Protocol/Protocol16x.cpp
index a163922c5..0d354a030 100644
--- a/src/Protocol/Protocol16x.cpp
+++ b/src/Protocol/Protocol16x.cpp
@@ -158,10 +158,10 @@ void cProtocol161::SendPlayerMaxSpeed(void)
-void cProtocol161::SendRespawn(const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks)
+void cProtocol161::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks)
{
// Besides sending the respawn, we need to also send the player max speed, otherwise the client reverts to super-fast
- super::SendRespawn(a_World, a_ShouldIgnoreDimensionChecks);
+ super::SendRespawn(a_Dimension, a_ShouldIgnoreDimensionChecks);
SendPlayerMaxSpeed();
}
diff --git a/src/Protocol/Protocol16x.h b/src/Protocol/Protocol16x.h
index e6e79027e..add761d1e 100644
--- a/src/Protocol/Protocol16x.h
+++ b/src/Protocol/Protocol16x.h
@@ -42,7 +42,7 @@ protected:
virtual void SendGameMode (eGameMode a_GameMode) override;
virtual void SendHealth (void) override;
virtual void SendPlayerMaxSpeed(void) override;
- virtual void SendRespawn (const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks = false) override;
+ virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override;
virtual void SendWindowOpen (const cWindow & a_Window) override;
virtual int ParseEntityAction (void) override;
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index dadf82716..45d39e0e9 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -986,9 +986,9 @@ void cProtocol172::SendRemoveEntityEffect(const cEntity & a_Entity, int a_Effect
-void cProtocol172::SendRespawn(const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks)
+void cProtocol172::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks)
{
- if ((m_LastSentDimension == a_World.GetDimension()) && !a_ShouldIgnoreDimensionChecks)
+ if ((m_LastSentDimension == a_Dimension) && !a_ShouldIgnoreDimensionChecks)
{
// Must not send a respawn for the world with the same dimension, the client goes cuckoo if we do (unless we are respawning from death)
return;
@@ -996,11 +996,11 @@ void cProtocol172::SendRespawn(const cWorld & a_World, bool a_ShouldIgnoreDimens
cPacketizer Pkt(*this, 0x07); // Respawn packet
cPlayer * Player = m_Client->GetPlayer();
- Pkt.WriteInt(a_World.GetDimension());
+ Pkt.WriteInt((int)a_Dimension);
Pkt.WriteByte(2); // TODO: Difficulty (set to Normal)
Pkt.WriteByte((Byte)Player->GetEffectiveGameMode());
Pkt.WriteString("default");
- m_LastSentDimension = a_World.GetDimension();
+ m_LastSentDimension = a_Dimension;
}
diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h
index 1a91d5203..9c9f563e0 100644
--- a/src/Protocol/Protocol17x.h
+++ b/src/Protocol/Protocol17x.h
@@ -104,7 +104,7 @@ public:
virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
- virtual void SendRespawn (const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks = false) override;
+ virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override;
virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override;
virtual void SendExperience (void) override;
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp
index bf46a15ef..a7fb7bcc2 100644
--- a/src/Protocol/ProtocolRecognizer.cpp
+++ b/src/Protocol/ProtocolRecognizer.cpp
@@ -556,10 +556,10 @@ void cProtocolRecognizer::SendRemoveEntityEffect(const cEntity & a_Entity, int a
-void cProtocolRecognizer::SendRespawn(const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks)
+void cProtocolRecognizer::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks)
{
ASSERT(m_Protocol != NULL);
- m_Protocol->SendRespawn(a_World, a_ShouldIgnoreDimensionChecks);
+ m_Protocol->SendRespawn(a_Dimension, a_ShouldIgnoreDimensionChecks);
}
diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h
index 02143f17f..65829ef73 100644
--- a/src/Protocol/ProtocolRecognizer.h
+++ b/src/Protocol/ProtocolRecognizer.h
@@ -107,7 +107,7 @@ public:
virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
- virtual void SendRespawn (const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks = false) override;
+ virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override;
virtual void SendExperience (void) override;
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;
diff --git a/src/Root.cpp b/src/Root.cpp
index b03a13382..b65e9b067 100644
--- a/src/Root.cpp
+++ b/src/Root.cpp
@@ -312,13 +312,15 @@ void cRoot::LoadWorlds(cIniFile & IniFile)
-cWorld * cRoot::CreateAndInitializeWorld(const AString & a_WorldName)
+cWorld * cRoot::CreateAndInitializeWorld(const AString & a_WorldName, eDimension a_Dimension, const AString & a_OverworldName)
{
- if (m_WorldsByName[a_WorldName] != NULL)
+ cWorld * World = m_WorldsByName[a_WorldName];
+ if (World != NULL)
{
- return NULL;
+ return World;
}
- cWorld* NewWorld = new cWorld(a_WorldName.c_str());
+
+ cWorld * NewWorld = new cWorld(a_WorldName.c_str(), a_Dimension, a_OverworldName);
m_WorldsByName[a_WorldName] = NewWorld;
NewWorld->Start();
NewWorld->InitializeSpawn();
@@ -370,7 +372,7 @@ void cRoot::UnloadWorlds(void)
-cWorld* cRoot::GetDefaultWorld()
+cWorld * cRoot::GetDefaultWorld()
{
return m_pDefaultWorld;
}
@@ -379,12 +381,19 @@ cWorld* cRoot::GetDefaultWorld()
-cWorld* cRoot::GetWorld( const AString & a_WorldName)
+cWorld * cRoot::GetWorld(const AString & a_WorldName, bool a_SearchForFolder)
{
- WorldMap::iterator itr = m_WorldsByName.find( a_WorldName);
+ WorldMap::iterator itr = m_WorldsByName.find(a_WorldName);
if (itr != m_WorldsByName.end())
+ {
return itr->second;
- return 0;
+ }
+
+ if (a_SearchForFolder && cFile::IsFolder(FILE_IO_PREFIX + a_WorldName))
+ {
+ return CreateAndInitializeWorld(a_WorldName);
+ }
+ return NULL;
}
@@ -396,9 +405,12 @@ bool cRoot::ForEachWorld(cWorldListCallback & a_Callback)
for (WorldMap::iterator itr = m_WorldsByName.begin(), itr2 = itr; itr != m_WorldsByName.end(); itr = itr2)
{
++itr2;
- if (a_Callback.Item(itr->second))
+ if (itr->second != NULL)
{
- return false;
+ if (a_Callback.Item(itr->second))
+ {
+ return false;
+ }
}
}
return true;
diff --git a/src/Root.h b/src/Root.h
index 08aafe3c9..93117389e 100644
--- a/src/Root.h
+++ b/src/Root.h
@@ -53,8 +53,17 @@ public:
// tolua_begin
cServer * GetServer(void) { return m_Server; }
cWorld * GetDefaultWorld(void);
- cWorld * GetWorld(const AString & a_WorldName);
- cWorld * CreateAndInitializeWorld(const AString & a_WorldName);
+
+ /** Returns a pointer to the world specified
+ If no world of that name was currently loaded and a_SearchForFolder was true, it will consult cFile::IsFolder() to see if a world folder of that name exists and if so, initialise a world based on that name
+ */
+ cWorld * GetWorld(const AString & a_WorldName, bool a_SearchForFolder = false);
+
+ /** Returns a pointer to a world of specified name - will search loaded worlds first, then create anew if not found
+ The dimension parameter is used to create a world with a specific dimension
+ a_OverworldName should be set for non-overworld dimensions if one wishes that world to link back to an overworld via portals
+ */
+ cWorld * CreateAndInitializeWorld(const AString & a_WorldName, eDimension a_Dimension = dimOverworld, const AString & a_OverworldName = "");
// tolua_end
/// Calls the callback for each world; returns true if the callback didn't abort (return true)
diff --git a/src/UI/Window.cpp b/src/UI/Window.cpp
index 2c22b41bd..4731f282b 100644
--- a/src/UI/Window.cpp
+++ b/src/UI/Window.cpp
@@ -274,7 +274,7 @@ void cWindow::OpenedByPlayer(cPlayer & a_Player)
bool cWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse)
{
// Checks whether the player is still holding an item
- if (a_Player.IsDraggingItem())
+ if (!a_Player.GetDraggingItem().IsEmpty())
{
LOGD("Player holds item! Dropping it...");
a_Player.TossHeldItem(a_Player.GetDraggingItem().m_ItemCount);
diff --git a/src/World.cpp b/src/World.cpp
index 7ad350e24..0b0f7870b 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -230,8 +230,9 @@ void cWorld::cTickThread::Execute(void)
////////////////////////////////////////////////////////////////////////////////
// cWorld:
-cWorld::cWorld(const AString & a_WorldName) :
+cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AString & a_OverworldName) :
m_WorldName(a_WorldName),
+ m_OverworldName(a_OverworldName),
m_IniFileName(m_WorldName + "/world.ini"),
m_StorageSchema("Default"),
#ifdef __arm__
@@ -239,6 +240,7 @@ cWorld::cWorld(const AString & a_WorldName) :
#else
m_StorageCompressionFactor(6),
#endif
+ m_Dimension(a_Dimension),
m_IsSpawnExplicitlySet(false),
m_WorldAgeSecs(0),
m_TimeOfDaySecs(0),
@@ -518,9 +520,15 @@ void cWorld::Start(void)
if (!IniFile.ReadFile(m_IniFileName))
{
LOGWARNING("Cannot read world settings from \"%s\", defaults will be used.", m_IniFileName.c_str());
+
+ // TODO: More descriptions for each key
+ IniFile.AddHeaderComment(" This is the per-world configuration file, managing settings such as generators, simulators, and spawn points");
+ IniFile.AddKeyComment(" LinkedWorlds", "This section governs portal world linkage; leave a value blank to disabled that associated method of teleportation");
}
- AString Dimension = IniFile.GetValueSet("General", "Dimension", "Overworld");
- m_Dimension = StringToDimension(Dimension);
+
+ // The presence of a configuration value overrides everything
+ // If no configuration value is found, GetDimension() is written to file and the variable is written to again to ensure that cosmic rays haven't sneakily changed its value
+ m_Dimension = StringToDimension(IniFile.GetValueSet("General", "Dimension", DimensionToString(GetDimension())));
// Try to find the "SpawnPosition" key and coord values in the world configuration, set the flag if found
int KeyNum = IniFile.FindKey("SpawnPosition");
@@ -528,8 +536,8 @@ void cWorld::Start(void)
(
(KeyNum >= 0) &&
(
- (IniFile.FindValue(KeyNum, "X") >= 0) ||
- (IniFile.FindValue(KeyNum, "Y") >= 0) ||
+ (IniFile.FindValue(KeyNum, "X") >= 0) &&
+ (IniFile.FindValue(KeyNum, "Y") >= 0) &&
(IniFile.FindValue(KeyNum, "Z") >= 0)
)
);
@@ -565,36 +573,26 @@ void cWorld::Start(void)
m_bUseChatPrefixes = IniFile.GetValueSetB("Mechanics", "UseChatPrefixes", true);
m_VillagersShouldHarvestCrops = IniFile.GetValueSetB("Monsters", "VillagersShouldHarvestCrops", true);
int GameMode = IniFile.GetValueSetI("General", "Gamemode", (int)m_GameMode);
-
- // Adjust the enum-backed variables into their respective bounds:
- m_GameMode = (eGameMode) Clamp(GameMode, (int)gmSurvival, (int)gmAdventure);
- m_TNTShrapnelLevel = (eShrapnelLevel)Clamp(TNTShrapnelLevel, (int)slNone, (int)slAll);
+ int Weather = IniFile.GetValueSetI("General", "Weather", (int)m_Weather);
+ m_TimeOfDay = IniFile.GetValueSetI("General", "TimeInTicks", m_TimeOfDay);
- // Load allowed mobs:
- AString DefaultMonsters;
- switch (m_Dimension)
+ if (GetDimension() == dimOverworld)
{
- case dimOverworld: DefaultMonsters = "bat, cavespider, chicken, cow, creeper, enderman, horse, mooshroom, ocelot, pig, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie"; break;
- case dimNether: DefaultMonsters = "blaze, ghast, magmacube, skeleton, zombie, zombiepigman"; break;
- case dimEnd: DefaultMonsters = "enderman"; break;
- case dimNotSet: break;
+ m_NetherWorldName = IniFile.GetValueSet("LinkedWorlds", "NetherWorldName", GetName() + "_nether");
+ m_EndWorldName = IniFile.GetValueSet("LinkedWorlds", "EndWorldName", GetName() + "_end");
}
- m_bAnimals = IniFile.GetValueSetB("Monsters", "AnimalsOn", true);
- AString AllMonsters = IniFile.GetValueSet("Monsters", "Types", DefaultMonsters);
- AStringVector SplitList = StringSplitAndTrim(AllMonsters, ",");
- for (AStringVector::const_iterator itr = SplitList.begin(), end = SplitList.end(); itr != end; ++itr)
+ else
{
- cMonster::eType ToAdd = cMonster::StringToMobType(*itr);
- if (ToAdd != cMonster::mtInvalidType)
- {
- m_AllowedMobs.insert(ToAdd);
- LOGD("Allowed mob: %s", itr->c_str());
- }
- else
- {
- LOG("World \"%s\": Unknown mob type: %s", m_WorldName.c_str(), itr->c_str());
- }
+ m_OverworldName = IniFile.GetValueSet("LinkedWorlds", "OverworldName", GetLinkedOverworldName());
}
+
+ // Adjust the enum-backed variables into their respective bounds:
+ m_GameMode = (eGameMode) Clamp(GameMode, (int)gmSurvival, (int)gmAdventure);
+ m_TNTShrapnelLevel = (eShrapnelLevel)Clamp(TNTShrapnelLevel, (int)slNone, (int)slAll);
+ m_Weather = (eWeather) Clamp(Weather, (int)wSunny, (int)wStorm);
+
+ InitialiseGeneratorDefaults(IniFile);
+ InitialiseAndLoadMobSpawningValues(IniFile);
m_ChunkMap = new cChunkMap(this);
@@ -691,6 +689,82 @@ eWeather cWorld::ChooseNewWeather()
+void cWorld::InitialiseGeneratorDefaults(cIniFile & a_IniFile)
+{
+ switch (GetDimension())
+ {
+ case dimEnd:
+ {
+ a_IniFile.GetValueSet("Generator", "BiomeGen", "Constant");
+ a_IniFile.GetValueSet("Generator", "ConstantBiome", "End");
+ a_IniFile.GetValueSet("Generator", "HeightGen", "Biomal");
+ a_IniFile.GetValueSet("Generator", "CompositionGen", "End");
+ break;
+ }
+ case dimOverworld:
+ {
+ a_IniFile.GetValueSet("Generator", "BiomeGen", "MultiStepMap");
+ a_IniFile.GetValueSet("Generator", "HeightGen", "DistortedHeightmap");
+ a_IniFile.GetValueSet("Generator", "CompositionGen", "DistortedHeightmap");
+ a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, PreSimulator");
+ break;
+ }
+ case dimNether:
+ {
+ a_IniFile.GetValueSet("Generator", "BiomeGen", "Constant");
+ a_IniFile.GetValueSet("Generator", "ConstantBiome", "Nether");
+ a_IniFile.GetValueSet("Generator", "HeightGen", "Flat");
+ a_IniFile.GetValueSet("Generator", "FlatHeight", "128");
+ a_IniFile.GetValueSet("Generator", "CompositionGen", "Nether");
+ a_IniFile.GetValueSet("Generator", "Finishers", "WormNestCaves, BottomLava, LavaSprings, NetherClumpFoliage, NetherForts, PreSimulator");
+ a_IniFile.GetValueSet("Generator", "BottomLavaHeight", "30");
+ break;
+ }
+ }
+}
+
+
+
+
+
+void cWorld::InitialiseAndLoadMobSpawningValues(cIniFile & a_IniFile)
+{
+ AString DefaultMonsters;
+ switch (m_Dimension)
+ {
+ case dimOverworld: DefaultMonsters = "bat, cavespider, chicken, cow, creeper, enderman, horse, mooshroom, ocelot, pig, sheep, silverfish, skeleton, slime, spider, squid, wolf, zombie"; break;
+ case dimNether: DefaultMonsters = "blaze, ghast, magmacube, skeleton, zombie, zombiepigman"; break;
+ case dimEnd: DefaultMonsters = "enderman"; break;
+ }
+
+ m_bAnimals = a_IniFile.GetValueSetB("Monsters", "AnimalsOn", true);
+ AString AllMonsters = a_IniFile.GetValueSet("Monsters", "Types", DefaultMonsters);
+
+ if (!m_bAnimals)
+ {
+ return;
+ }
+
+ AStringVector SplitList = StringSplitAndTrim(AllMonsters, ",");
+ for (AStringVector::const_iterator itr = SplitList.begin(), end = SplitList.end(); itr != end; ++itr)
+ {
+ cMonster::eType ToAdd = cMonster::StringToMobType(*itr);
+ if (ToAdd != cMonster::mtInvalidType)
+ {
+ m_AllowedMobs.insert(ToAdd);
+ LOGD("Allowed mob: %s", itr->c_str());
+ }
+ else
+ {
+ LOG("World \"%s\": Unknown mob type: %s", m_WorldName.c_str(), itr->c_str());
+ }
+ }
+}
+
+
+
+
+
void cWorld::Stop(void)
{
// Delete the clients that have been in this world:
@@ -703,6 +777,25 @@ void cWorld::Stop(void)
} // for itr - m_Clients[]
m_Clients.clear();
}
+
+ // Write settings to file; these are all plugin changeable values - keep updated!
+ cIniFile IniFile;
+ IniFile.ReadFile(m_IniFileName);
+ if (GetDimension() == dimOverworld)
+ {
+ IniFile.SetValue("LinkedWorlds", "NetherWorldName", m_NetherWorldName);
+ IniFile.SetValue("LinkedWorlds", "EndWorldName", m_EndWorldName);
+ }
+ else
+ {
+ IniFile.SetValue("LinkedWorlds", "OverworldName", m_OverworldName);
+ }
+ IniFile.SetValueI("Physics", "TNTShrapnelLevel", (int)m_TNTShrapnelLevel);
+ IniFile.SetValueB("Mechanics", "CommandBlocksEnabled", m_bCommandBlocksEnabled);
+ IniFile.SetValueB("Mechanics", "UseChatPrefixes", m_bUseChatPrefixes);
+ IniFile.SetValueI("General", "Weather", (int)m_Weather);
+ IniFile.SetValueI("General", "TimeInTicks", m_TimeOfDay);
+ IniFile.WriteFile(m_IniFileName);
m_TickThread.Stop();
m_Lighting.Stop();
@@ -955,11 +1048,7 @@ void cWorld::TickClients(float a_Dt)
// Add clients scheduled for adding:
for (cClientHandleList::iterator itr = m_ClientsToAdd.begin(), end = m_ClientsToAdd.end(); itr != end; ++itr)
{
- if (std::find(m_Clients.begin(), m_Clients.end(), *itr) != m_Clients.end())
- {
- ASSERT(!"Adding a client that is already in the clientlist");
- continue;
- }
+ ASSERT(std::find(m_Clients.begin(), m_Clients.end(), *itr) == m_Clients.end());
m_Clients.push_back(*itr);
} // for itr - m_ClientsToRemove[]
m_ClientsToAdd.clear();
@@ -2378,8 +2467,10 @@ void cWorld::AddPlayer(cPlayer * a_Player)
void cWorld::RemovePlayer(cPlayer * a_Player)
{
-
- m_ChunkMap->RemoveEntity(a_Player);
+ if (!a_Player->IsWorldTravellingFrom(this))
+ {
+ m_ChunkMap->RemoveEntity(a_Player);
+ }
{
cCSLock Lock(m_CSPlayersToAdd);
m_PlayersToAdd.remove(a_Player);
@@ -2882,15 +2973,6 @@ bool cWorld::HasEntity(int a_UniqueID)
-void cWorld::RemoveEntity(cEntity * a_Entity)
-{
- m_ChunkMap->RemoveEntity(a_Entity);
-}
-
-
-
-
-
/*
unsigned int cWorld::GetNumPlayers(void)
{
@@ -3197,7 +3279,8 @@ void cWorld::AddQueuedPlayers(void)
cCSLock Lock(m_CSPlayers);
for (cPlayerList::iterator itr = PlayersToAdd.begin(), end = PlayersToAdd.end(); itr != end; ++itr)
{
- ASSERT(std::find(m_Players.begin(), m_Players.end(), *itr) == m_Players.end()); // Is it already in the list? HOW?
+ ASSERT(std::find(m_Players.begin(), m_Players.end(), *itr) == m_Players.end()); // Is it already in the list? HOW?
+ LOGD("Adding player %s to world \"%s\".", (*itr)->GetName().c_str(), m_WorldName.c_str());
m_Players.push_back(*itr);
(*itr)->SetWorld(this);
@@ -3227,6 +3310,9 @@ void cWorld::AddQueuedPlayers(void)
if (Client != NULL)
{
Client->StreamChunks();
+ Client->SendPlayerMoveLook();
+ Client->SendHealth();
+ Client->SendWholeInventory(*(*itr)->GetWindow());
}
} // for itr - PlayersToAdd[]
}
diff --git a/src/World.h b/src/World.h
index 9658178ae..4bf5a9d64 100644
--- a/src/World.h
+++ b/src/World.h
@@ -185,7 +185,7 @@ public:
virtual eDimension GetDimension(void) const { return m_Dimension; }
/** Returns the world height at the specified coords; waits for the chunk to get loaded / generated */
- int GetHeight(int a_BlockX, int a_BlockZ);
+ virtual int GetHeight(int a_BlockX, int a_BlockZ);
// tolua_end
@@ -304,9 +304,6 @@ public:
bool HasEntity(int a_UniqueID);
- /** Removes the entity, the entity ptr ownership is assumed taken by the caller */
- void RemoveEntity(cEntity * a_Entity);
-
/** Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true */
bool ForEachEntity(cEntityCallback & a_Callback); // Exported in ManualBindings.cpp
@@ -622,6 +619,15 @@ public:
bool ShouldUseChatPrefixes(void) const { return m_bUseChatPrefixes; }
void SetShouldUseChatPrefixes(bool a_Flag) { m_bUseChatPrefixes = a_Flag; }
+
+ AString GetNetherWorldName(void) const { return m_NetherWorldName; }
+ void SetNetherWorldName(const AString & a_Name) { m_NetherWorldName = a_Name; }
+
+ AString GetEndWorldName(void) const { return m_EndWorldName; }
+ void SetEndWorldName(const AString & a_Name) { m_EndWorldName = a_Name; }
+
+ AString GetLinkedOverworldName(void) const { return m_OverworldName; }
+ void SetLinkedOverworldName(const AString & a_Name) { m_OverworldName = a_Name; }
// tolua_end
@@ -705,7 +711,7 @@ public:
/** Returns true if the current weather is stormy */
bool IsWeatherStorm(void) const { return (m_Weather == wStorm); }
-
+
/** Returns true if the weather is stormy at the specified location. This takes into account biomes. */
bool IsWeatherStormAt(int a_BlockX, int a_BlockZ)
{
@@ -716,10 +722,11 @@ public:
bool IsWeatherWet(void) const { return !IsWeatherSunny(); }
/** Returns true if it is raining, stormy or snowing at the specified location. This takes into account biomes. */
- bool IsWeatherWetAt(int a_BlockX, int a_BlockZ)
+ virtual bool IsWeatherWetAt(int a_BlockX, int a_BlockZ)
{
return (IsWeatherWet() && !IsBiomeNoDownfall(GetBiomeAt(a_BlockX, a_BlockZ)));
}
+
// tolua_end
cChunkGenerator & GetGenerator(void) { return m_Generator; }
@@ -824,6 +831,12 @@ private:
AString m_WorldName;
+
+ /** The name of the world that a portal in this world should link to
+ Only has effect if this world is a nether or end world, as it is used by entities to see which world to teleport to when in a portal
+ */
+ AString m_OverworldName;
+
AString m_IniFileName;
/** Name of the storage schema used to load and save chunks */
@@ -908,6 +921,12 @@ private:
See the eShrapnelLevel enumeration for details
*/
eShrapnelLevel m_TNTShrapnelLevel;
+
+ /** Name of the nether world */
+ AString m_NetherWorldName;
+
+ /** Name of the end world */
+ AString m_EndWorldName;
cChunkGenerator m_Generator;
@@ -967,7 +986,7 @@ private:
cSetChunkDataPtrs m_SetChunkDataQueue;
- cWorld(const AString & a_WorldName);
+ cWorld(const AString & a_WorldName, eDimension a_Dimension = dimOverworld, const AString & a_OverworldName = "");
virtual ~cWorld();
void Tick(float a_Dt, int a_LastTickDurationMSec);
@@ -1008,9 +1027,16 @@ private:
Assumes it is called from the Tick thread. */
void AddQueuedPlayers(void);
+ /** Sets generator values to dimension specific defaults, if those values do not exist */
+ void InitialiseGeneratorDefaults(cIniFile & a_IniFile);
+
+ /** Sets mob spawning values if nonexistant to their dimension specific defaults */
+ void InitialiseAndLoadMobSpawningValues(cIniFile & a_IniFile);
+
/** Sets the specified chunk data into the chunkmap. Called in the tick thread.
Modifies the a_SetChunkData - moves the entities contained in it into the chunk. */
void SetChunkData(cSetChunkData & a_SetChunkData);
+
}; // tolua_export
diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp
index 4857da1b6..b7a3d40ce 100644
--- a/src/WorldStorage/NBTChunkSerializer.cpp
+++ b/src/WorldStorage/NBTChunkSerializer.cpp
@@ -486,6 +486,7 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster)
m_Writer.AddFloat("", a_Monster->GetDropChanceBoots());
m_Writer.EndList();
m_Writer.AddByte("CanPickUpLoot", (char)a_Monster->CanPickUpLoot());
+ m_Writer.AddShort("Health", (short)a_Monster->GetHealth());
switch (a_Monster->GetMobType())
{
case cMonster::mtBat:
diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp
index 023813769..6134e2c0a 100644
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -2499,8 +2499,16 @@ bool cWSSAnvil::LoadMonsterBaseFromNBT(cMonster & a_Monster, const cParsedNBT &
a_Monster.SetDropChanceChestplate(DropChance[2]);
a_Monster.SetDropChanceLeggings(DropChance[3]);
a_Monster.SetDropChanceBoots(DropChance[4]);
- bool CanPickUpLoot = (a_NBT.GetByte(a_NBT.FindChildByName(a_TagIdx, "CanPickUpLoot")) == 1);
- a_Monster.SetCanPickUpLoot(CanPickUpLoot);
+
+ int LootTag = a_NBT.FindChildByName(a_TagIdx, "CanPickUpLoot");
+ if (LootTag > 0)
+ {
+ bool CanPickUpLoot = (a_NBT.GetByte(LootTag) == 1);
+ a_Monster.SetCanPickUpLoot(CanPickUpLoot);
+ }
+
+ int HealthTag = a_NBT.FindChildByName(a_TagIdx, "Health");
+ a_Monster.SetHealth(HealthTag > 0 ? a_NBT.GetShort(HealthTag) : a_Monster.GetMaxHealth());
return true;
}