summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Bindings/AllToLua.pkg1
-rw-r--r--src/Bindings/LuaState.cpp26
-rw-r--r--src/Bindings/LuaState.h3
-rw-r--r--src/Bindings/PluginLua.cpp8
-rw-r--r--src/Bindings/PluginManager.cpp2
-rw-r--r--src/Bindings/PluginManager.h4
-rw-r--r--src/BoundingBox.cpp20
-rw-r--r--src/Chunk.h14
-rw-r--r--src/ClientHandle.cpp35
-rw-r--r--src/ClientHandle.h11
-rw-r--r--src/CompositeChat.cpp426
-rw-r--r--src/CompositeChat.h183
-rw-r--r--src/Defines.h9
-rw-r--r--src/Entities/ExpOrb.cpp7
-rw-r--r--src/Entities/Player.cpp14
-rw-r--r--src/Entities/Player.h4
-rw-r--r--src/Entities/ProjectileEntity.cpp1
-rw-r--r--src/Items/ItemLighter.h2
-rw-r--r--src/Mobs/Creeper.cpp2
-rw-r--r--src/Mobs/Monster.cpp4
-rw-r--r--src/Mobs/Wolf.cpp45
-rw-r--r--src/Protocol/Protocol.h2
-rw-r--r--src/Protocol/Protocol125.cpp38
-rw-r--r--src/Protocol/Protocol125.h1
-rw-r--r--src/Protocol/Protocol132.cpp2
-rw-r--r--src/Protocol/Protocol14x.cpp2
-rw-r--r--src/Protocol/Protocol17x.cpp157
-rw-r--r--src/Protocol/Protocol17x.h38
-rw-r--r--src/Protocol/ProtocolRecognizer.cpp10
-rw-r--r--src/Protocol/ProtocolRecognizer.h1
-rw-r--r--src/Root.cpp16
-rw-r--r--src/Root.h24
-rw-r--r--src/Simulator/IncrementalRedstoneSimulator.cpp205
-rw-r--r--src/Simulator/IncrementalRedstoneSimulator.h38
-rw-r--r--src/World.cpp20
-rw-r--r--src/World.h18
36 files changed, 1200 insertions, 193 deletions
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg
index f65aed9bb..6c295102f 100644
--- a/src/Bindings/AllToLua.pkg
+++ b/src/Bindings/AllToLua.pkg
@@ -70,6 +70,7 @@ $cfile "../Generating/ChunkDesc.h"
$cfile "../CraftingRecipes.h"
$cfile "../UI/Window.h"
$cfile "../Mobs/Monster.h"
+$cfile "../CompositeChat.h"
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp
index c6be7be3c..ca7f6b255 100644
--- a/src/Bindings/LuaState.cpp
+++ b/src/Bindings/LuaState.cpp
@@ -173,6 +173,31 @@ void cLuaState::Detach(void)
+void cLuaState::AddPackagePath(const AString & a_PathVariable, const AString & a_Path)
+{
+ // Get the current path:
+ lua_getfield(m_LuaState, LUA_GLOBALSINDEX, "package"); // Stk: <package>
+ lua_getfield(m_LuaState, -1, a_PathVariable.c_str()); // Stk: <package> <package.path>
+ size_t len = 0;
+ const char * PackagePath = lua_tolstring(m_LuaState, -1, &len);
+
+ // Append the new path:
+ AString NewPackagePath(PackagePath, len);
+ NewPackagePath.append(LUA_PATHSEP);
+ NewPackagePath.append(a_Path);
+
+ // Set the new path to the environment:
+ lua_pop(m_LuaState, 1); // Stk: <package>
+ lua_pushlstring(m_LuaState, NewPackagePath.c_str(), NewPackagePath.length()); // Stk: <package> <NewPackagePath>
+ lua_setfield(m_LuaState, -2, a_PathVariable.c_str()); // Stk: <package>
+ lua_pop(m_LuaState, 1);
+ lua_pop(m_LuaState, 1); // Stk: -
+}
+
+
+
+
+
bool cLuaState::LoadFile(const AString & a_FileName)
{
ASSERT(IsValid());
@@ -1251,6 +1276,7 @@ void cLuaState::LogStack(lua_State * a_LuaState, const char * a_Header)
case LUA_TLIGHTUSERDATA: Printf(Value, "%p", lua_touserdata(a_LuaState, i)); break;
case LUA_TNUMBER: Printf(Value, "%f", (double)lua_tonumber(a_LuaState, i)); break;
case LUA_TSTRING: Printf(Value, "%s", lua_tostring(a_LuaState, i)); break;
+ case LUA_TTABLE: Printf(Value, "%p", lua_topointer(a_LuaState, i)); break;
default: break;
}
LOGD(" Idx %d: type %d (%s) %s", i, Type, lua_typename(a_LuaState, Type), Value.c_str());
diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h
index b9bf10142..dcb660c3f 100644
--- a/src/Bindings/LuaState.h
+++ b/src/Bindings/LuaState.h
@@ -154,6 +154,9 @@ public:
/** Returns true if the m_LuaState is valid */
bool IsValid(void) const { return (m_LuaState != NULL); }
+ /** Adds the specified path to package.<a_PathVariable> */
+ void AddPackagePath(const AString & a_PathVariable, const AString & a_Path);
+
/** Loads the specified file
Returns false and logs a warning to the console if not successful (but the LuaState is kept open).
m_SubsystemName is displayed in the warning log message.
diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp
index 773d522c4..45c8216be 100644
--- a/src/Bindings/PluginLua.cpp
+++ b/src/Bindings/PluginLua.cpp
@@ -82,6 +82,14 @@ bool cPluginLua::Initialize(void)
lua_pushstring(m_LuaState, GetName().c_str());
lua_setglobal(m_LuaState, LUA_PLUGIN_NAME_VAR_NAME);
+ // Add the plugin's folder to the package.path and package.cpath variables (#693):
+ m_LuaState.AddPackagePath("path", FILE_IO_PREFIX + GetLocalFolder() + "/?.lua");
+ #ifdef _WIN32
+ m_LuaState.AddPackagePath("cpath", GetLocalFolder() + "\\?.dll");
+ #else
+ m_LuaState.AddPackagePath("cpath", FILE_IO_PREFIX + GetLocalFolder() + "/?.so");
+ #endif
+
tolua_pushusertype(m_LuaState, this, "cPluginLua");
lua_setglobal(m_LuaState, "g_Plugin");
}
diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp
index c6c8c081e..c7df6357e 100644
--- a/src/Bindings/PluginManager.cpp
+++ b/src/Bindings/PluginManager.cpp
@@ -56,7 +56,7 @@ void cPluginManager::ReloadPlugins(void)
void cPluginManager::FindPlugins(void)
{
- AString PluginsPath = FILE_IO_PREFIX + AString( "Plugins/" );
+ AString PluginsPath = GetPluginsPath() + "/";
// 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();)
diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h
index c78bceda1..44bc5a8d7 100644
--- a/src/Bindings/PluginManager.h
+++ b/src/Bindings/PluginManager.h
@@ -266,6 +266,10 @@ public: // tolua_export
Returns false if plugin not found, and the value that the callback has returned otherwise. */
bool DoWithPlugin(const AString & a_PluginName, cPluginCallback & a_Callback);
+ /** Returns the path where individual plugins' folders are expected.
+ The path doesn't end in a slash. */
+ static AString GetPluginsPath(void) { return FILE_IO_PREFIX + AString("Plugins"); } // tolua_export
+
private:
friend class cRoot;
diff --git a/src/BoundingBox.cpp b/src/BoundingBox.cpp
index 47b135688..aab51c539 100644
--- a/src/BoundingBox.cpp
+++ b/src/BoundingBox.cpp
@@ -12,25 +12,25 @@
#if SELF_TEST
-/// A simple self-test that is executed on program start, used to verify bbox functionality
-class SelfTest
+/** A simple self-test that is executed on program start, used to verify bbox functionality */
+static class SelfTest_BoundingBox
{
public:
- SelfTest(void)
+ SelfTest_BoundingBox(void)
{
Vector3d Min(1, 1, 1);
Vector3d Max(2, 2, 2);
Vector3d LineDefs[] =
{
- Vector3d(1.5, 4, 1.5), Vector3d(1.5, 3, 1.5), // Should intersect at 2, face 1 (YP)
+ Vector3d(1.5, 4, 1.5), Vector3d(1.5, 3, 1.5), // Should intersect at 2, face 1 (YP)
Vector3d(1.5, 0, 1.5), Vector3d(1.5, 4, 1.5), // Should intersect at 0.25, face 0 (YM)
Vector3d(0, 0, 0), Vector3d(2, 2, 2), // Should intersect at 0.5, face 0, 3 or 5 (anyM)
Vector3d(0.999, 0, 1.5), Vector3d(0.999, 4, 1.5), // Should not intersect
Vector3d(1.999, 0, 1.5), Vector3d(1.999, 4, 1.5), // Should intersect at 0.25, face 0 (YM)
Vector3d(2.001, 0, 1.5), Vector3d(2.001, 4, 1.5), // Should not intersect
} ;
- bool Results[] = {true,true,true,false,true,false};
- double LineCoeffs[] = {2,0.25,0.5,0,0.25,0};
+ bool Results[] = {true, true, true, false, true, false};
+ double LineCoeffs[] = {2, 0.25, 0.5, 0, 0.25, 0};
for (size_t i = 0; i < ARRAYCOUNT(LineDefs) / 2; i++)
{
@@ -41,7 +41,7 @@ public:
bool res = cBoundingBox::CalcLineIntersection(Min, Max, Line1, Line2, LineCoeff, Face);
if (res != Results[i])
{
- fprintf(stderr,"LineIntersection({%.02f, %.02f, %.02f}, {%.02f, %.02f, %.02f}) -> %d, %.05f, %d\n",
+ fprintf(stderr, "LineIntersection({%.02f, %.02f, %.02f}, {%.02f, %.02f, %.02f}) -> %d, %.05f, %d\n",
Line1.x, Line1.y, Line1.z,
Line2.x, Line2.y, Line2.z,
res ? 1 : 0, LineCoeff, Face
@@ -52,7 +52,7 @@ public:
{
if (LineCoeff != LineCoeffs[i])
{
- fprintf(stderr,"LineIntersection({%.02f, %.02f, %.02f}, {%.02f, %.02f, %.02f}) -> %d, %.05f, %d\n",
+ fprintf(stderr, "LineIntersection({%.02f, %.02f, %.02f}, {%.02f, %.02f, %.02f}) -> %d, %.05f, %d\n",
Line1.x, Line1.y, Line1.z,
Line2.x, Line2.y, Line2.z,
res ? 1 : 0, LineCoeff, Face
@@ -61,9 +61,9 @@ public:
}
}
} // for i - LineDefs[]
- fprintf(stderr,"BoundingBox selftest complete.");
+ fprintf(stderr, "BoundingBox selftest complete.\n");
}
-} Test;
+} gTest;
#endif
diff --git a/src/Chunk.h b/src/Chunk.h
index 93eba217e..696690068 100644
--- a/src/Chunk.h
+++ b/src/Chunk.h
@@ -343,12 +343,17 @@ public:
NIBBLETYPE GetTimeAlteredLight(NIBBLETYPE a_Skylight) const;
- // Simulator data:
+ // Per-chunk simulator data:
cFireSimulatorChunkData & GetFireSimulatorData (void) { return m_FireSimulatorData; }
cFluidSimulatorData * GetWaterSimulatorData(void) { return m_WaterSimulatorData; }
cFluidSimulatorData * GetLavaSimulatorData (void) { return m_LavaSimulatorData; }
cSandSimulatorChunkData & GetSandSimulatorData (void) { return m_SandSimulatorData; }
- cRedstoneSimulatorChunkData & GetRedstoneSimulatorData(void) { return m_RedstoneSimulatorData; }
+
+ cRedstoneSimulatorChunkData * GetRedstoneSimulatorData(void) { return &m_RedstoneSimulatorData; }
+ cIncrementalRedstoneSimulator::PoweredBlocksList * GetRedstoneSimulatorPoweredBlocksList(void) { return &m_RedstoneSimulatorPoweredBlocksList; }
+ cIncrementalRedstoneSimulator::LinkedBlocksList * GetRedstoneSimulatorLinkedBlocksList(void) { return &m_RedstoneSimulatorLinkedBlocksList; };
+ cIncrementalRedstoneSimulator::SimulatedPlayerToggleableList * GetRedstoneSimulatorSimulatedPlayerToggleableList(void) { return &m_RedstoneSimulatorSimulatedPlayerToggleableList; };
+ cIncrementalRedstoneSimulator::RepeatersDelayList * GetRedstoneSimulatorRepeatersDelayList(void) { return &m_RedstoneSimulatorRepeatersDelayList; };
cBlockEntity * GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ);
cBlockEntity * GetBlockEntity(const Vector3i & a_BlockPos) { return GetBlockEntity(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z); }
@@ -419,7 +424,12 @@ private:
cFluidSimulatorData * m_WaterSimulatorData;
cFluidSimulatorData * m_LavaSimulatorData;
cSandSimulatorChunkData m_SandSimulatorData;
+
cRedstoneSimulatorChunkData m_RedstoneSimulatorData;
+ cIncrementalRedstoneSimulator::PoweredBlocksList m_RedstoneSimulatorPoweredBlocksList;
+ cIncrementalRedstoneSimulator::LinkedBlocksList m_RedstoneSimulatorLinkedBlocksList;
+ cIncrementalRedstoneSimulator::SimulatedPlayerToggleableList m_RedstoneSimulatorSimulatedPlayerToggleableList;
+ cIncrementalRedstoneSimulator::RepeatersDelayList m_RedstoneSimulatorRepeatersDelayList;
// pick up a random block of this chunk
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 1b3ebc3d4..c91a0c01b 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -31,6 +31,7 @@
#include "MersenneTwister.h"
#include "Protocol/ProtocolRecognizer.h"
+#include "CompositeChat.h"
@@ -94,6 +95,7 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) :
m_ShouldCheckDownloaded(false),
m_NumExplosionsThisTick(0),
m_UniqueID(0),
+ m_Locale("en_GB"),
m_HasSentPlayerChunk(false)
{
m_Protocol = new cProtocolRecognizer(this);
@@ -1087,14 +1089,20 @@ void cClientHandle::HandleChat(const AString & a_Message)
return;
}
- // Not a command, broadcast as a simple message:
- AString Msg;
- Printf(Msg, "%s<%s>%s %s",
- m_Player->GetColor().c_str(),
- m_Player->GetName().c_str(),
- cChatColor::White.c_str(),
- Message.c_str()
- );
+ // Not a command, broadcast as a message:
+ cCompositeChat Msg;
+ AString Color = m_Player->GetColor();
+ if (Color.length() == 3)
+ {
+ Color = AString("@") + Color[2];
+ }
+ else
+ {
+ Color.empty();
+ }
+ Msg.AddTextPart(AString("<") + m_Player->GetName() + "> ", Color);
+ Msg.ParseText(a_Message);
+ Msg.UnderlineUrls();
m_Player->GetWorld()->BroadcastChat(Msg);
}
@@ -1729,7 +1737,7 @@ void cClientHandle::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlock
-void cClientHandle::SendChat(const AString & a_Message, ChatPrefixCodes a_ChatPrefix, const AString & a_AdditionalData)
+void cClientHandle::SendChat(const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData)
{
bool ShouldAppendChatPrefixes = true;
@@ -1840,6 +1848,15 @@ void cClientHandle::SendChat(const AString & a_Message, ChatPrefixCodes a_ChatPr
+void cClientHandle::SendChat(const cCompositeChat & a_Message)
+{
+ m_Protocol->SendChat(a_Message);
+}
+
+
+
+
+
void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
{
ASSERT(m_Player != NULL);
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index d9a86d983..5faa94004 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -34,6 +34,7 @@ class cWindow;
class cFallingBlock;
class cItemHandler;
class cWorld;
+class cCompositeChat;
@@ -89,7 +90,8 @@ public:
void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage);
void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); // tolua_export
void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes);
- void SendChat (const AString & a_Message, ChatPrefixCodes a_ChatPrefix, const AString & a_AdditionalData = "");
+ void SendChat (const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData = "");
+ void SendChat (const cCompositeChat & a_Message);
void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer);
void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player);
void SendDestroyEntity (const cEntity & a_Entity);
@@ -153,6 +155,9 @@ public:
void SetViewDistance(int a_ViewDistance); // tolua_export
int GetViewDistance(void) const { return m_ViewDistance; } // tolua_export
+
+ void SetLocale(AString & a_Locale) { m_Locale = a_Locale; } // tolua_export
+ AString GetLocale(void) const { return m_Locale; } // tolua_export
int GetUniqueID() const { return m_UniqueID; } // tolua_export
@@ -306,7 +311,9 @@ private:
/// Set to true when the chunk where the player is is sent to the client. Used for spawning the player
bool m_HasSentPlayerChunk;
-
+
+ /// Client Settings
+ AString m_Locale;
/// Returns true if the rate block interactions is within a reasonable limit (bot protection)
diff --git a/src/CompositeChat.cpp b/src/CompositeChat.cpp
new file mode 100644
index 000000000..3eec35657
--- /dev/null
+++ b/src/CompositeChat.cpp
@@ -0,0 +1,426 @@
+
+// CompositeChat.cpp
+
+// Implements the cCompositeChat class used to wrap a chat message with multiple parts (text, url, cmd)
+
+#include "Globals.h"
+#include "CompositeChat.h"
+
+
+
+
+
+#if SELF_TEST
+
+/** A simple self-test that verifies that the composite chat parser is working properly. */
+class SelfTest_CompositeChat
+{
+public:
+ SelfTest_CompositeChat(void)
+ {
+ fprintf(stderr, "cCompositeChat self test...\n");
+ TestParser1();
+ TestParser2();
+ TestParser3();
+ TestParser4();
+ TestParser5();
+ fprintf(stderr, "cCompositeChat self test finished.\n");
+ }
+
+ void TestParser1(void)
+ {
+ cCompositeChat Msg;
+ Msg.ParseText("Testing @2color codes and http://links parser");
+ const cCompositeChat::cParts & Parts = Msg.GetParts();
+ assert(Parts.size() == 4);
+ assert(Parts[0]->m_PartType == cCompositeChat::ptText);
+ assert(Parts[1]->m_PartType == cCompositeChat::ptText);
+ assert(Parts[2]->m_PartType == cCompositeChat::ptUrl);
+ assert(Parts[3]->m_PartType == cCompositeChat::ptText);
+ assert(Parts[0]->m_Style == "");
+ assert(Parts[1]->m_Style == "@2");
+ assert(Parts[2]->m_Style == "@2");
+ assert(Parts[3]->m_Style == "@2");
+ }
+
+ void TestParser2(void)
+ {
+ cCompositeChat Msg;
+ Msg.ParseText("@3Advanced stuff: @5overriding color codes and http://links.with/@4color-in-them handling");
+ const cCompositeChat::cParts & Parts = Msg.GetParts();
+ assert(Parts.size() == 4);
+ assert(Parts[0]->m_PartType == cCompositeChat::ptText);
+ assert(Parts[1]->m_PartType == cCompositeChat::ptText);
+ assert(Parts[2]->m_PartType == cCompositeChat::ptUrl);
+ assert(Parts[3]->m_PartType == cCompositeChat::ptText);
+ assert(Parts[0]->m_Style == "@3");
+ assert(Parts[1]->m_Style == "@5");
+ assert(Parts[2]->m_Style == "@5");
+ assert(Parts[3]->m_Style == "@5");
+ }
+
+ void TestParser3(void)
+ {
+ cCompositeChat Msg;
+ Msg.ParseText("http://links.starting the text");
+ const cCompositeChat::cParts & Parts = Msg.GetParts();
+ assert(Parts.size() == 2);
+ assert(Parts[0]->m_PartType == cCompositeChat::ptUrl);
+ assert(Parts[1]->m_PartType == cCompositeChat::ptText);
+ assert(Parts[0]->m_Style == "");
+ assert(Parts[1]->m_Style == "");
+ }
+
+ void TestParser4(void)
+ {
+ cCompositeChat Msg;
+ Msg.ParseText("links finishing the text: http://some.server");
+ const cCompositeChat::cParts & Parts = Msg.GetParts();
+ assert(Parts.size() == 2);
+ assert(Parts[0]->m_PartType == cCompositeChat::ptText);
+ assert(Parts[1]->m_PartType == cCompositeChat::ptUrl);
+ assert(Parts[0]->m_Style == "");
+ assert(Parts[1]->m_Style == "");
+ }
+
+ void TestParser5(void)
+ {
+ cCompositeChat Msg;
+ Msg.ParseText("http://only.links");
+ const cCompositeChat::cParts & Parts = Msg.GetParts();
+ assert(Parts.size() == 1);
+ assert(Parts[0]->m_PartType == cCompositeChat::ptUrl);
+ assert(Parts[0]->m_Style == "");
+ }
+
+} gTest;
+#endif // SELF_TEST
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCompositeChat:
+
+cCompositeChat::cCompositeChat(void) :
+ m_MessageType(mtCustom)
+{
+}
+
+
+
+
+
+cCompositeChat::cCompositeChat(const AString & a_ParseText) :
+ m_MessageType(mtCustom)
+{
+ ParseText(a_ParseText);
+}
+
+
+
+
+
+cCompositeChat::~cCompositeChat()
+{
+ Clear();
+}
+
+
+
+
+
+void cCompositeChat::Clear(void)
+{
+ for (cParts::iterator itr = m_Parts.begin(), end = m_Parts.end(); itr != end; ++itr)
+ {
+ delete *itr;
+ } // for itr - m_Parts[]
+ m_Parts.clear();
+}
+
+
+
+
+
+void cCompositeChat::AddTextPart(const AString & a_Message, const AString & a_Style)
+{
+ m_Parts.push_back(new cTextPart(a_Message, a_Style));
+}
+
+
+
+
+
+void cCompositeChat::AddClientTranslatedPart(const AString & a_TranslationID, const AStringVector & a_Parameters, const AString & a_Style)
+{
+ m_Parts.push_back(new cClientTranslatedPart(a_TranslationID, a_Parameters, a_Style));
+}
+
+
+
+
+
+void cCompositeChat::AddUrlPart(const AString & a_Text, const AString & a_Url, const AString & a_Style)
+{
+ m_Parts.push_back(new cUrlPart(a_Text, a_Url, a_Style));
+}
+
+
+
+
+
+void cCompositeChat::AddRunCommandPart(const AString & a_Text, const AString & a_Command, const AString & a_Style)
+{
+ m_Parts.push_back(new cRunCommandPart(a_Text, a_Command, a_Style));
+}
+
+
+
+
+
+void cCompositeChat::AddSuggestCommandPart(const AString & a_Text, const AString & a_SuggestedCommand, const AString & a_Style)
+{
+ m_Parts.push_back(new cSuggestCommandPart(a_Text, a_SuggestedCommand, a_Style));
+}
+
+
+
+
+
+void cCompositeChat::ParseText(const AString & a_ParseText)
+{
+ size_t len = a_ParseText.length();
+ size_t first = 0; // First character of the currently parsed block
+ AString CurrentStyle;
+ AString CurrentText;
+ for (size_t i = 0; i < len; i++)
+ {
+ switch (a_ParseText[i])
+ {
+ case '@':
+ {
+ // Color code
+ i++;
+ if (i >= len)
+ {
+ // Not enough following text
+ break;
+ }
+ if (a_ParseText[i] == '@')
+ {
+ // "@@" escape, just put a "@" into the current text and keep parsing as text
+ if (i > first + 1)
+ {
+ CurrentText.append(a_ParseText.c_str() + first, i - first - 1);
+ }
+ first = i + 1;
+ continue;
+ }
+ else
+ {
+ // True color code. Create a part for the CurrentText and start parsing anew:
+ if (i >= first)
+ {
+ CurrentText.append(a_ParseText.c_str() + first, i - first - 1);
+ first = i + 1;
+ }
+ if (!CurrentText.empty())
+ {
+ m_Parts.push_back(new cTextPart(CurrentText, CurrentStyle));
+ CurrentText.clear();
+ }
+ AddStyle(CurrentStyle, a_ParseText.substr(i - 1, 2));
+ }
+ break;
+ }
+
+ case ':':
+ {
+ const char * LinkPrefixes[] =
+ {
+ "http",
+ "https"
+ };
+ for (size_t Prefix = 0; Prefix < ARRAYCOUNT(LinkPrefixes); Prefix++)
+ {
+ size_t PrefixLen = strlen(LinkPrefixes[Prefix]);
+ if (
+ (i >= first + PrefixLen) && // There is enough space in front of the colon for the prefix
+ (strncmp(a_ParseText.c_str() + i - PrefixLen, LinkPrefixes[Prefix], PrefixLen) == 0) // the prefix matches
+ )
+ {
+ // Add everything before this as a text part:
+ if (i > first + PrefixLen)
+ {
+ CurrentText.append(a_ParseText.c_str() + first, i - first - PrefixLen);
+ first = i - PrefixLen;
+ }
+ if (!CurrentText.empty())
+ {
+ AddTextPart(CurrentText, CurrentStyle);
+ CurrentText.clear();
+ }
+
+ // Go till the last non-whitespace char in the text:
+ for (; i < len; i++)
+ {
+ if (isspace(a_ParseText[i]))
+ {
+ break;
+ }
+ }
+ AddUrlPart(a_ParseText.substr(first, i - first), a_ParseText.substr(first, i - first), CurrentStyle);
+ first = i;
+ break;
+ }
+ } // for Prefix - LinkPrefix[]
+ break;
+ } // case ':'
+ } // switch (a_ParseText[i])
+ } // for i - a_ParseText[]
+ if (first < len)
+ {
+ AddTextPart(a_ParseText.substr(first, len - first), CurrentStyle);
+ }
+}
+
+
+
+
+
+void cCompositeChat::SetMessageType(eMessageType a_MessageType)
+{
+ m_MessageType = a_MessageType;
+}
+
+
+
+
+
+void cCompositeChat::UnderlineUrls(void)
+{
+ for (cParts::iterator itr = m_Parts.begin(), end = m_Parts.end(); itr != end; ++itr)
+ {
+ if ((*itr)->m_PartType == ptUrl)
+ {
+ (*itr)->m_Style.append("u");
+ }
+ } // for itr - m_Parts[]
+}
+
+
+
+
+
+void cCompositeChat::AddStyle(AString & a_Style, const AString & a_AddStyle)
+{
+ if (a_AddStyle.empty())
+ {
+ return;
+ }
+ if (a_AddStyle[0] == '@')
+ {
+ size_t idx = a_Style.find('@');
+ if ((idx != AString::npos) && (idx != a_Style.length()))
+ {
+ a_Style.erase(idx, 2);
+ }
+ a_Style.append(a_AddStyle);
+ return;
+ }
+ a_Style.append(a_AddStyle);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCompositeChat::cBasePart:
+
+cCompositeChat::cBasePart::cBasePart(cCompositeChat::ePartType a_PartType, const AString & a_Text, const AString & a_Style) :
+ m_PartType(a_PartType),
+ m_Text(a_Text),
+ m_Style(a_Style)
+{
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCompositeChat::cTextPart:
+
+cCompositeChat::cTextPart::cTextPart(const AString & a_Text, const AString &a_Style) :
+ super(ptText, a_Text, a_Style)
+{
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCompositeChat::cClientTranslatedPart:
+
+cCompositeChat::cClientTranslatedPart::cClientTranslatedPart(const AString & a_TranslationID, const AStringVector & a_Parameters, const AString & a_Style) :
+ super(ptClientTranslated, a_TranslationID, a_Style),
+ m_Parameters(a_Parameters)
+{
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCompositeChat::cUrlPart:
+
+cCompositeChat::cUrlPart::cUrlPart(const AString & a_Text, const AString & a_Url, const AString & a_Style) :
+ super(ptUrl, a_Text, a_Style),
+ m_Url(a_Url)
+{
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCompositeChat::cCommandPart:
+
+cCompositeChat::cCommandPart::cCommandPart(ePartType a_PartType, const AString & a_Text, const AString & a_Command, const AString & a_Style) :
+ super(a_PartType, a_Text, a_Style),
+ m_Command(a_Command)
+{
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCompositeChat::cRunCommandPart:
+
+cCompositeChat::cRunCommandPart::cRunCommandPart(const AString & a_Text, const AString & a_Command, const AString & a_Style) :
+ super(ptRunCommand, a_Text, a_Command, a_Style)
+{
+}
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCompositeChat::cSuggestCommandPart:
+
+cCompositeChat::cSuggestCommandPart::cSuggestCommandPart(const AString & a_Text, const AString & a_Command, const AString & a_Style) :
+ super(ptSuggestCommand, a_Text, a_Command, a_Style)
+{
+}
+
+
+
+
diff --git a/src/CompositeChat.h b/src/CompositeChat.h
new file mode 100644
index 000000000..51600da4f
--- /dev/null
+++ b/src/CompositeChat.h
@@ -0,0 +1,183 @@
+
+// CompositeChat.h
+
+// Declares the cCompositeChat class used to wrap a chat message with multiple parts (text, url, cmd)
+
+#include "Defines.h"
+
+
+
+
+
+// tolua_begin
+/** Container for a single chat message composed of multiple functional parts.
+Each part corresponds roughly to the behavior supported by the client messaging:
+ - plain text, optionaly colorized / styled
+ - clickable URLs
+ - clickable commands (run)
+ - clickable commands (suggest)
+Each part has a text assigned to it that can be styled. The style is specified using a string,
+each character / character combination in the string specifies the style to use:
+ - b = bold
+ - i = italic
+ - u = underlined
+ - s = strikethrough
+ - o = obfuscated
+ - @X = color X (X is 0 - 9 or a - f, same as dye meta
+If the protocol version doesn't support all the features, it degrades gracefully.
+*/
+class cCompositeChat
+{
+public:
+ // tolua_end
+
+ enum ePartType
+ {
+ ptText,
+ ptClientTranslated,
+ ptUrl,
+ ptRunCommand,
+ ptSuggestCommand,
+ } ;
+
+ class cBasePart
+ {
+ public:
+ ePartType m_PartType;
+ AString m_Text;
+ AString m_Style;
+
+ cBasePart(ePartType a_PartType, const AString & a_Text, const AString & a_Style = "");
+
+ // Force a virtual destructor in descendants
+ virtual ~cBasePart() {}
+ } ;
+
+ class cTextPart :
+ public cBasePart
+ {
+ typedef cBasePart super;
+ public:
+ cTextPart(const AString & a_Text, const AString & a_Style = "");
+ } ;
+
+ class cClientTranslatedPart :
+ public cBasePart
+ {
+ typedef cBasePart super;
+ public:
+ AStringVector m_Parameters;
+
+ cClientTranslatedPart(const AString & a_TranslationID, const AStringVector & a_Parameters, const AString & a_Style = "");
+ } ;
+
+ class cUrlPart :
+ public cBasePart
+ {
+ typedef cBasePart super;
+ public:
+ AString m_Url;
+
+ cUrlPart(const AString & a_Text, const AString & a_Url, const AString & a_Style = "");
+ } ;
+
+ class cCommandPart :
+ public cBasePart
+ {
+ typedef cBasePart super;
+ public:
+ AString m_Command;
+
+ cCommandPart(ePartType a_PartType, const AString & a_Text, const AString & a_Command, const AString & a_Style = "");
+ } ;
+
+ class cRunCommandPart :
+ public cCommandPart
+ {
+ typedef cCommandPart super;
+ public:
+ cRunCommandPart(const AString & a_Text, const AString & a_Command, const AString & a_Style = "");
+ } ;
+
+ class cSuggestCommandPart :
+ public cCommandPart
+ {
+ typedef cCommandPart super;
+ public:
+ cSuggestCommandPart(const AString & a_Text, const AString & a_Command, const AString & a_Style = "");
+ } ;
+
+ typedef std::vector<cBasePart *> cParts;
+
+ // tolua_begin
+
+ /** Creates a new empty chat message */
+ cCompositeChat(void);
+
+ /** Creates a new chat message and parses the text into parts.
+ Recognizes "http:" and "https:" links and @color-codes.
+ Uses ParseText() for the actual parsing. */
+ cCompositeChat(const AString & a_ParseText);
+
+ ~cCompositeChat();
+
+ /** Removes all parts from the object. */
+ void Clear(void);
+
+ /** Adds a plain text part, with optional style.
+ The default style is plain white text. */
+ void AddTextPart(const AString & a_Message, const AString & a_Style = "");
+
+ // tolua_end
+
+ /** Adds a part that is translated client-side, with the formatting parameters and optional style.
+ Exported in ManualBindings due to AStringVector usage - Lua uses an array-table of strings. */
+ void AddClientTranslatedPart(const AString & a_TranslationID, const AStringVector & a_Parameters, const AString & a_Style = "");
+
+ // tolua_begin
+
+ /** Adds a part that opens an URL when clicked.
+ The default style is underlined light blue text. */
+ void AddUrlPart(const AString & a_Text, const AString & a_Url, const AString & a_Style = "u@c");
+
+ /** Adds a part that runs a command when clicked.
+ The default style is underlined light green text. */
+ void AddRunCommandPart(const AString & a_Text, const AString & a_Command, const AString & a_Style = "u@a");
+
+ /** Adds a part that suggests a command (enters it into the chat message area, but doesn't send) when clicked.
+ The default style is underlined yellow text. */
+ void AddSuggestCommandPart(const AString & a_Text, const AString & a_SuggestedCommand, const AString & a_Style = "u@b");
+
+ /** Parses text into various parts, adds those.
+ Recognizes "http:" and "https:" URLs and @color-codes. */
+ void ParseText(const AString & a_ParseText);
+
+ /** Sets the message type, which is indicated by prefixes added to the message when serializing. */
+ void SetMessageType(eMessageType a_MessageType);
+
+ /** Returns the message type set previously by SetMessageType(). */
+ eMessageType GetMessageType(void) const { return m_MessageType; }
+
+ /** Adds the "underline" style to each part that is an URL. */
+ void UnderlineUrls(void);
+
+ // tolua_end
+
+ const cParts & GetParts(void) const { return m_Parts; }
+
+protected:
+ /** All the parts that */
+ cParts m_Parts;
+
+ /** The message type, as indicated by prefixes. */
+ eMessageType m_MessageType;
+
+
+ /** Adds a_AddStyle to a_Style; overwrites the existing style if appropriate.
+ If the style already contains something that a_AddStyle overrides, it is erased first. */
+ void AddStyle(AString & a_Style, const AString & a_AddStyle);
+} ; // tolua_export
+
+
+
+
diff --git a/src/Defines.h b/src/Defines.h
index 290f862ef..f33d1ae56 100644
--- a/src/Defines.h
+++ b/src/Defines.h
@@ -441,7 +441,10 @@ inline float GetSpecialSignf( float a_Val )
-enum ChatPrefixCodes
+
+// tolua_begin
+
+enum eMessageType
{
// http://forum.mc-server.org/showthread.php?tid=1212
// MessageType...
@@ -458,7 +461,9 @@ enum ChatPrefixCodes
mtLeave, // A player has left the server
};
-// tolua_begin
+
+
+
/** Normalizes an angle in degrees to the [-180, +180) range: */
inline double NormalizeAngleDegrees(const double a_Degrees)
diff --git a/src/Entities/ExpOrb.cpp b/src/Entities/ExpOrb.cpp
index 04ee85823..3398f1c7b 100644
--- a/src/Entities/ExpOrb.cpp
+++ b/src/Entities/ExpOrb.cpp
@@ -51,7 +51,10 @@ void cExpOrb::Tick(float a_Dt, cChunk & a_Chunk)
{
LOGD("Player %s picked up an ExpOrb. His reward is %i", a_ClosestPlayer->GetName().c_str(), m_Reward);
a_ClosestPlayer->DeltaExperience(m_Reward);
- Destroy(true);
+
+ m_World->BroadcastSoundEffect("random.orb", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
+
+ Destroy();
}
a_Distance.Normalize();
a_Distance *= ((float) (5.5 - Distance));
@@ -61,4 +64,4 @@ void cExpOrb::Tick(float a_Dt, cChunk & a_Chunk)
BroadcastMovementUpdate();
}
HandlePhysics(a_Dt, a_Chunk);
-} \ No newline at end of file
+}
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 838bbd06c..70ddb3c98 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -839,6 +839,12 @@ void cPlayer::KilledBy(cEntity * a_Killer)
cItems Pickups;
m_Inventory.CopyToItems(Pickups);
m_Inventory.Clear();
+
+ if (GetName() == "Notch")
+ {
+ Pickups.Add(cItem(E_ITEM_RED_APPLE));
+ }
+
m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10);
SaveToDisk(); // Save it, yeah the world is a tough place !
@@ -1758,6 +1764,12 @@ void cPlayer::HandleFood(void)
{
// Ref.: http://www.minecraftwiki.net/wiki/Hunger
+ if (IsGameModeCreative())
+ {
+ // Hunger is disabled for Creative
+ return;
+ }
+
// Remember the food level before processing, for later comparison
int LastFoodLevel = m_FoodLevel;
@@ -1775,7 +1787,7 @@ void cPlayer::HandleFood(void)
Heal(1);
m_FoodExhaustionLevel += 3;
}
- else if (m_FoodLevel <= 0)
+ else if ((m_FoodLevel <= 0) && (m_Health > 1))
{
// Damage from starving
TakeDamage(dtStarving, NULL, 1, 1, 0);
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index 7db9544cb..a795bb9eb 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -203,6 +203,7 @@ public:
void SendMessageWarning (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtWarning); }
void SendMessageFatal (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtFailure); }
void SendMessagePrivateMsg(const AString & a_Message, const AString & a_Sender) { m_ClientHandle->SendChat(a_Message, mtPrivateMessage, a_Sender); }
+ void SendMessage (const cCompositeChat & a_Message) { m_ClientHandle->SendChat(a_Message); }
const AString & GetName(void) const { return m_PlayerName; }
void SetName(const AString & a_Name) { m_PlayerName = a_Name; }
@@ -225,7 +226,8 @@ public:
// tolua_begin
- /// Returns the full color code to use for this player, based on their primary group or set in m_Color
+ /** Returns the full color code to use for this player, based on their primary group or set in m_Color.
+ The returned value includes the cChatColor::Delimiter. */
AString GetColor(void) const;
/** tosses the item in the selected hotbar slot */
diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp
index a3fa9d557..ef82c6e94 100644
--- a/src/Entities/ProjectileEntity.cpp
+++ b/src/Entities/ProjectileEntity.cpp
@@ -680,6 +680,7 @@ super(pkExpBottle, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
void cExpBottleEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
// Spawn an experience orb with a reward between 3 and 11.
+ m_World->BroadcastSoundParticleEffect(2002, POSX_TOINT, POSY_TOINT, POSZ_TOINT, 0);
m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), 3 + m_World->GetTickRandomNumber(8));
Destroy();
diff --git a/src/Items/ItemLighter.h b/src/Items/ItemLighter.h
index 6681a08d4..cc7daeb08 100644
--- a/src/Items/ItemLighter.h
+++ b/src/Items/ItemLighter.h
@@ -33,7 +33,7 @@ public:
case E_BLOCK_TNT:
{
// Activate the TNT:
- a_World->BroadcastSoundEffect("random.fuse", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f);
+ a_World->BroadcastSoundEffect("game.tnt.primed", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f);
a_World->SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 4); // 4 seconds to boom
a_World->SetBlock(a_BlockX,a_BlockY,a_BlockZ, E_BLOCK_AIR, 0);
break;
diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp
index 2e1ece865..ff0abfdca 100644
--- a/src/Mobs/Creeper.cpp
+++ b/src/Mobs/Creeper.cpp
@@ -79,7 +79,7 @@ void cCreeper::Attack(float a_Dt)
if (!m_bIsBlowing)
{
- m_World->BroadcastSoundEffect("random.fuse", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 1.f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
+ m_World->BroadcastSoundEffect("game.tnt.primed", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 1.f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
m_bIsBlowing = true;
m_World->BroadcastEntityMetadata(*this);
}
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index 9817901c9..b5cf693cb 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -142,11 +142,11 @@ void cMonster::TickPathFinding()
BLOCKTYPE BlockAtYPP = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY + 2, gCrossCoords[i].z + PosZ);
BLOCKTYPE BlockAtYM = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY - 1, gCrossCoords[i].z + PosZ);
- if (!g_BlockIsSolid[BlockAtY] && !g_BlockIsSolid[BlockAtYP] && !IsBlockLava(BlockAtYM))
+ if ((!g_BlockIsSolid[BlockAtY]) && (!g_BlockIsSolid[BlockAtYP]) && (!IsBlockLava(BlockAtYM)) && (BlockAtY != E_BLOCK_FENCE) && (BlockAtY != E_BLOCK_FENCE_GATE))
{
m_PotentialCoordinates.push_back(Vector3d((gCrossCoords[i].x + PosX), PosY, gCrossCoords[i].z + PosZ));
}
- else if (g_BlockIsSolid[BlockAtY] && !g_BlockIsSolid[BlockAtYP] && !g_BlockIsSolid[BlockAtYPP] && !IsBlockLava(BlockAtYM))
+ else if ((g_BlockIsSolid[BlockAtY]) && (!g_BlockIsSolid[BlockAtYP]) && (!g_BlockIsSolid[BlockAtYPP]) && (!IsBlockLava(BlockAtYM)) && (BlockAtY != E_BLOCK_FENCE) && (BlockAtY != E_BLOCK_FENCE_GATE))
{
m_PotentialCoordinates.push_back(Vector3d((gCrossCoords[i].x + PosX), PosY + 1, gCrossCoords[i].z + PosZ));
}
diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp
index ff324d073..2736c3dd1 100644
--- a/src/Mobs/Wolf.cpp
+++ b/src/Mobs/Wolf.cpp
@@ -124,12 +124,6 @@ void cWolf::Tick(float a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
}
-
- // The wolf is sitting so don't move him at all.
- if (IsSitting())
- {
- m_bMovingToDestination = false;
- }
cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), (float)m_SightDistance);
if (a_Closest_Player != NULL)
@@ -148,18 +142,15 @@ void cWolf::Tick(float a_Dt, cChunk & a_Chunk)
SetIsBegging(true);
m_World->BroadcastEntityMetadata(*this);
}
+
+ m_FinalDestination = a_Closest_Player->GetPosition(); // So that we will look at a player holding food
+
// Don't move to the player if the wolf is sitting.
- if (IsSitting())
- {
- m_bMovingToDestination = false;
- }
- else
+ if (!IsSitting())
{
- m_bMovingToDestination = true;
+ MoveToPosition(a_Closest_Player->GetPosition());
}
- Vector3d PlayerPos = a_Closest_Player->GetPosition();
- PlayerPos.y++;
- m_FinalDestination = PlayerPos;
+
break;
}
default:
@@ -173,10 +164,14 @@ void cWolf::Tick(float a_Dt, cChunk & a_Chunk)
}
}
- if (IsTame())
+ if (IsTame() && !IsSitting())
{
TickFollowPlayer();
}
+ else if (IsSitting())
+ {
+ m_bMovingToDestination = false;
+ }
}
@@ -196,29 +191,19 @@ void cWolf::TickFollowPlayer()
public:
Vector3d OwnerPos;
} Callback;
+
if (m_World->DoWithPlayer(m_OwnerName, Callback))
{
// The player is present in the world, follow him:
double Distance = (Callback.OwnerPos - GetPosition()).Length();
if (Distance > 30)
{
- if (!IsSitting())
- {
- Callback.OwnerPos.y = FindFirstNonAirBlockPosition(Callback.OwnerPos.x, Callback.OwnerPos.z);
- TeleportToCoords(Callback.OwnerPos.x, Callback.OwnerPos.y, Callback.OwnerPos.z);
- }
+ Callback.OwnerPos.y = FindFirstNonAirBlockPosition(Callback.OwnerPos.x, Callback.OwnerPos.z);
+ TeleportToCoords(Callback.OwnerPos.x, Callback.OwnerPos.y, Callback.OwnerPos.z);
}
else
{
- m_FinalDestination = Callback.OwnerPos;
- if (IsSitting())
- {
- m_bMovingToDestination = false;
- }
- else
- {
- m_bMovingToDestination = true;
- }
+ MoveToPosition(Callback.OwnerPos);
}
}
}
diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h
index 791082537..f5b9fd403 100644
--- a/src/Protocol/Protocol.h
+++ b/src/Protocol/Protocol.h
@@ -28,6 +28,7 @@ class cWorld;
class cMonster;
class cChunkDataSerializer;
class cFallingBlock;
+class cCompositeChat;
@@ -58,6 +59,7 @@ public:
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) = 0;
virtual void SendChat (const AString & a_Message) = 0;
+ virtual void SendChat (const cCompositeChat & a_Message) = 0;
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0;
virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) = 0;
virtual void SendDestroyEntity (const cEntity & a_Entity) = 0;
diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp
index 73d21161c..7020699d1 100644
--- a/src/Protocol/Protocol125.cpp
+++ b/src/Protocol/Protocol125.cpp
@@ -32,6 +32,8 @@ Documentation:
#include "../Mobs/IncludeAllMonsters.h"
+#include "../CompositeChat.h"
+
@@ -233,6 +235,42 @@ void cProtocol125::SendChat(const AString & a_Message)
+void cProtocol125::SendChat(const cCompositeChat & a_Message)
+{
+ // This version doesn't support composite messages, just extract each part's text and use it:
+ AString Msg;
+ const cCompositeChat::cParts & Parts = a_Message.GetParts();
+ for (cCompositeChat::cParts::const_iterator itr = Parts.begin(), end = Parts.end(); itr != end; ++itr)
+ {
+ switch ((*itr)->m_PartType)
+ {
+ case cCompositeChat::ptText:
+ case cCompositeChat::ptClientTranslated:
+ case cCompositeChat::ptRunCommand:
+ case cCompositeChat::ptSuggestCommand:
+ {
+ Msg.append((*itr)->m_Text);
+ break;
+ }
+ case cCompositeChat::ptUrl:
+ {
+ Msg.append(((cCompositeChat::cUrlPart *)(*itr))->m_Url);
+ break;
+ }
+ } // switch (PartType)
+ } // for itr - Parts[]
+
+ // Send the message:
+ cCSLock Lock(m_CSPacket);
+ WriteByte (PACKET_CHAT);
+ WriteString(Msg);
+ Flush();
+}
+
+
+
+
+
void cProtocol125::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
{
cCSLock Lock(m_CSPacket);
diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h
index cd15ab518..1a3209333 100644
--- a/src/Protocol/Protocol125.h
+++ b/src/Protocol/Protocol125.h
@@ -33,6 +33,7 @@ public:
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
virtual void SendChat (const AString & a_Message) override;
+ virtual void SendChat (const cCompositeChat & a_Message) override;
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override;
virtual void SendDestroyEntity (const cEntity & a_Entity) override;
diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp
index 648e70151..1f9222a69 100644
--- a/src/Protocol/Protocol132.cpp
+++ b/src/Protocol/Protocol132.cpp
@@ -560,7 +560,7 @@ int cProtocol132::ParseLocaleViewDistance(void)
HANDLE_PACKET_READ(ReadChar, char, ViewDistance);
HANDLE_PACKET_READ(ReadChar, char, ChatFlags);
HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty);
- // TODO: m_Client->HandleLocale(Locale);
+ m_Client->SetLocale(Locale);
// TODO: m_Client->HandleViewDistance(ViewDistance);
// TODO: m_Client->HandleChatFlags(ChatFlags);
// Ignoring client difficulty
diff --git a/src/Protocol/Protocol14x.cpp b/src/Protocol/Protocol14x.cpp
index f82e6de45..232b2718e 100644
--- a/src/Protocol/Protocol14x.cpp
+++ b/src/Protocol/Protocol14x.cpp
@@ -85,7 +85,7 @@ int cProtocol142::ParseLocaleViewDistance(void)
HANDLE_PACKET_READ(ReadChar, char, ChatFlags);
HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty);
HANDLE_PACKET_READ(ReadChar, char, ShouldShowCape); // <-- new in 1.4.2
- // TODO: m_Client->HandleLocale(Locale);
+ m_Client->SetLocale(Locale);
// TODO: m_Client->HandleViewDistance(ViewDistance);
// TODO: m_Client->HandleChatFlags(ChatFlags);
// Ignoring client difficulty
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index 7eaf106cf..f7d13774d 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -8,6 +8,7 @@ Implements the 1.7.x protocol classes:
*/
#include "Globals.h"
+#include "json/json.h"
#include "Protocol17x.h"
#include "ChunkDataSerializer.h"
#include "../ClientHandle.h"
@@ -25,6 +26,7 @@ Implements the 1.7.x protocol classes:
#include "../Mobs/IncludeAllMonsters.h"
#include "../UI/Window.h"
#include "../BlockEntities/CommandBlockEntity.h"
+#include "../CompositeChat.h"
@@ -200,6 +202,78 @@ void cProtocol172::SendChat(const AString & a_Message)
+void cProtocol172::SendChat(const cCompositeChat & a_Message)
+{
+ // Compose the complete Json string to send:
+ Json::Value msg;
+ msg["text"] = ""; // The client crashes without this
+ const cCompositeChat::cParts & Parts = a_Message.GetParts();
+ for (cCompositeChat::cParts::const_iterator itr = Parts.begin(), end = Parts.end(); itr != end; ++itr)
+ {
+ Json::Value Part;
+ switch ((*itr)->m_PartType)
+ {
+ case cCompositeChat::ptText:
+ {
+ Part["text"] = (*itr)->m_Text;
+ AddChatPartStyle(Part, (*itr)->m_Style);
+ break;
+ }
+
+ case cCompositeChat::ptClientTranslated:
+ {
+ const cCompositeChat::cClientTranslatedPart & p = (const cCompositeChat::cClientTranslatedPart &)**itr;
+ Part["translate"] = p.m_Text;
+ Json::Value With;
+ for (AStringVector::const_iterator itrW = p.m_Parameters.begin(), endW = p.m_Parameters.end(); itrW != endW; ++itr)
+ {
+ With.append(*itrW);
+ }
+ if (!p.m_Parameters.empty())
+ {
+ Part["with"] = With;
+ }
+ AddChatPartStyle(Part, p.m_Style);
+ break;
+ }
+
+ case cCompositeChat::ptUrl:
+ {
+ const cCompositeChat::cUrlPart & p = (const cCompositeChat::cUrlPart &)**itr;
+ Part["text"] = p.m_Text;
+ Json::Value Url;
+ Url["action"] = "open_url";
+ Url["value"] = p.m_Url;
+ Part["clickEvent"] = Url;
+ AddChatPartStyle(Part, p.m_Style);
+ break;
+ }
+
+ case cCompositeChat::ptSuggestCommand:
+ case cCompositeChat::ptRunCommand:
+ {
+ const cCompositeChat::cCommandPart & p = (const cCompositeChat::cCommandPart &)**itr;
+ Part["text"] = p.m_Text;
+ Json::Value Cmd;
+ Cmd["action"] = (p.m_PartType == cCompositeChat::ptRunCommand) ? "run_command" : "suggest_command";
+ Cmd["value"] = p.m_Command;
+ Part["clickEvent"] = Cmd;
+ AddChatPartStyle(Part, p.m_Style);
+ break;
+ }
+ }
+ msg["extra"].append(Part);
+ } // for itr - Parts[]
+
+ // Send the message to the client:
+ cPacketizer Pkt(*this, 0x02);
+ Pkt.WriteString(msg.toStyledString());
+}
+
+
+
+
+
void cProtocol172::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
{
// Serialize first, before creating the Packetizer (the packetizer locks a CS)
@@ -476,7 +550,7 @@ void cProtocol172::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
Pkt.WriteByte((Byte)a_Player.GetEffectiveGameMode() | (cRoot::Get()->GetServer()->IsHardcore() ? 0x08 : 0)); // Hardcore flag bit 4
Pkt.WriteChar((char)a_World.GetDimension());
Pkt.WriteByte(2); // TODO: Difficulty (set to Normal)
- Pkt.WriteByte(cRoot::Get()->GetServer()->GetMaxPlayers());
+ Pkt.WriteByte(std::min(cRoot::Get()->GetServer()->GetMaxPlayers(), 60));
Pkt.WriteString("default"); // Level type - wtf?
}
@@ -1524,6 +1598,8 @@ void cProtocol172::HandlePacketClientSettings(cByteBuffer & a_ByteBuffer)
HANDLE_READ(a_ByteBuffer, ReadByte, Byte, ChatColors);
HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Difficulty);
HANDLE_READ(a_ByteBuffer, ReadByte, Byte, ShowCape);
+
+ m_Client->SetLocale(Locale);
// TODO: handle in m_Client
}
@@ -1979,6 +2055,85 @@ void cProtocol172::StartEncryption(const Byte * a_Key)
+void cProtocol172::AddChatPartStyle(Json::Value & a_Value, const AString & a_PartStyle)
+{
+ size_t len = a_PartStyle.length();
+ for (size_t i = 0; i < len; i++)
+ {
+ switch (a_PartStyle[i])
+ {
+ case 'b':
+ {
+ // bold
+ a_Value["bold"] = Json::Value(true);
+ break;
+ }
+
+ case 'i':
+ {
+ // italic
+ a_Value["italic"] = Json::Value(true);
+ break;
+ }
+
+ case 'u':
+ {
+ // Underlined
+ a_Value["underlined"] = Json::Value(true);
+ break;
+ }
+
+ case 's':
+ {
+ // strikethrough
+ a_Value["strikethrough"] = Json::Value(true);
+ break;
+ }
+
+ case 'o':
+ {
+ // obfuscated
+ a_Value["obfuscated"] = Json::Value(true);
+ break;
+ }
+
+ case '@':
+ {
+ // Color, specified by the next char:
+ i++;
+ if (i >= len)
+ {
+ // String too short, didn't contain a color
+ break;
+ }
+ switch (a_PartStyle[i])
+ {
+ case '0': a_Value["color"] = Json::Value("black"); break;
+ case '1': a_Value["color"] = Json::Value("dark_blue"); break;
+ case '2': a_Value["color"] = Json::Value("dark_green"); break;
+ case '3': a_Value["color"] = Json::Value("dark_aqua"); break;
+ case '4': a_Value["color"] = Json::Value("dark_red"); break;
+ case '5': a_Value["color"] = Json::Value("dark_purple"); break;
+ case '6': a_Value["color"] = Json::Value("gold"); break;
+ case '7': a_Value["color"] = Json::Value("gray"); break;
+ case '8': a_Value["color"] = Json::Value("dark_gray"); break;
+ case '9': a_Value["color"] = Json::Value("blue"); break;
+ case 'a': a_Value["color"] = Json::Value("green"); break;
+ case 'b': a_Value["color"] = Json::Value("aqua"); break;
+ case 'c': a_Value["color"] = Json::Value("red"); break;
+ case 'd': a_Value["color"] = Json::Value("light_purple"); break;
+ case 'e': a_Value["color"] = Json::Value("yellow"); break;
+ case 'f': a_Value["color"] = Json::Value("white"); break;
+ } // switch (color)
+ } // case '@'
+ } // switch (Style[i])
+ } // for i - a_PartStyle[]
+}
+
+
+
+
+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cProtocol172::cPacketizer:
diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h
index 6a75e41c8..d19be0f05 100644
--- a/src/Protocol/Protocol17x.h
+++ b/src/Protocol/Protocol17x.h
@@ -36,6 +36,16 @@ Declares the 1.7.x protocol classes:
+// fwd:
+namespace Json
+{
+ class Value;
+}
+
+
+
+
+
class cProtocol172 :
public cProtocol
{
@@ -45,16 +55,17 @@ public:
cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State);
- /// Called when client sends some data:
+ /** Called when client sends some data: */
virtual void DataReceived(const char * a_Data, int a_Size) override;
- /// Sending stuff to clients (alphabetically sorted):
+ /** Sending stuff to clients (alphabetically sorted): */
virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override;
virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override;
virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
virtual void SendChat (const AString & a_Message) override;
+ virtual void SendChat (const cCompositeChat & a_Message) override;
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override;
virtual void SendDestroyEntity (const cEntity & a_Entity) override;
@@ -117,7 +128,7 @@ public:
protected:
- /// Composes individual packets in the protocol's m_OutPacketBuffer; sends them upon being destructed
+ /** Composes individual packets in the protocol's m_OutPacketBuffer; sends them upon being destructed */
class cPacketizer
{
public:
@@ -206,16 +217,16 @@ protected:
AString m_AuthServerID;
- /// State of the protocol. 1 = status, 2 = login, 3 = game
+ /** State of the protocol. 1 = status, 2 = login, 3 = game */
UInt32 m_State;
- /// Buffer for the received data
+ /** Buffer for the received data */
cByteBuffer m_ReceivedData;
- /// Buffer for composing the outgoing packets, through cPacketizer
+ /** Buffer for composing the outgoing packets, through cPacketizer */
cByteBuffer m_OutPacketBuffer;
- /// Buffer for composing packet length (so that each cPacketizer instance doesn't allocate a new cPacketBuffer)
+ /** Buffer for composing packet length (so that each cPacketizer instance doesn't allocate a new cPacketBuffer) */
cByteBuffer m_OutPacketLenBuffer;
bool m_IsEncrypted;
@@ -227,7 +238,7 @@ protected:
cFile m_CommLogFile;
- /// Adds the received (unencrypted) data to m_ReceivedData, parses complete packets
+ /** Adds the received (unencrypted) data to m_ReceivedData, parses complete packets */
void AddReceivedData(const char * a_Data, int a_Size);
/** Reads and handles the packet. The packet length and type have already been read.
@@ -268,21 +279,24 @@ protected:
void HandlePacketWindowClose (cByteBuffer & a_ByteBuffer);
- /// Writes an entire packet into the output stream. a_Packet is expected to start with the packet type; data length is prepended here.
+ /** Writes an entire packet into the output stream. a_Packet is expected to start with the packet type; data length is prepended here. */
void WritePacket(cByteBuffer & a_Packet);
- /// Sends the data to the client, encrypting them if needed.
+ /** Sends the data to the client, encrypting them if needed. */
virtual void SendData(const char * a_Data, int a_Size) override;
void SendCompass(const cWorld & a_World);
- /// Reads an item out of the received data, sets a_Item to the values read. Returns false if not enough received data
+ /** Reads an item out of the received data, sets a_Item to the values read. Returns false if not enough received data */
bool ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item);
- /// Parses item metadata as read by ReadItem(), into the item enchantments.
+ /** Parses item metadata as read by ReadItem(), into the item enchantments. */
void ParseItemMetadata(cItem & a_Item, const AString & a_Metadata);
void StartEncryption(const Byte * a_Key);
+
+ /** Adds the chat part's style (represented by the part's stylestring) into the Json object. */
+ void AddChatPartStyle(Json::Value & a_Value, const AString & a_PartStyle);
} ;
diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp
index 32409c2aa..6e51ee9cd 100644
--- a/src/Protocol/ProtocolRecognizer.cpp
+++ b/src/Protocol/ProtocolRecognizer.cpp
@@ -159,6 +159,16 @@ void cProtocolRecognizer::SendChat(const AString & a_Message)
+void cProtocolRecognizer::SendChat(const cCompositeChat & a_Message)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendChat(a_Message);
+}
+
+
+
+
+
void cProtocolRecognizer::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
{
ASSERT(m_Protocol != NULL);
diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h
index f58c66d10..800163be6 100644
--- a/src/Protocol/ProtocolRecognizer.h
+++ b/src/Protocol/ProtocolRecognizer.h
@@ -68,6 +68,7 @@ public:
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
virtual void SendChat (const AString & a_Message) override;
+ virtual void SendChat (const cCompositeChat & a_Message) override;
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override;
virtual void SendDestroyEntity (const cEntity & a_Entity) override;
diff --git a/src/Root.cpp b/src/Root.cpp
index 749fbd288..206255916 100644
--- a/src/Root.cpp
+++ b/src/Root.cpp
@@ -543,11 +543,23 @@ void cRoot::ReloadGroups(void)
-void cRoot::LoopWorldsAndBroadcastChat(const AString & a_Message, ChatPrefixCodes a_ChatPrefix)
+void cRoot::BroadcastChat(const AString & a_Message, eMessageType a_ChatPrefix)
{
for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr)
{
- itr->second->LoopPlayersAndBroadcastChat(a_Message, a_ChatPrefix);
+ itr->second->BroadcastChat(a_Message, NULL, a_ChatPrefix);
+ } // for itr - m_WorldsByName[]
+}
+
+
+
+
+
+void cRoot::BroadcastChat(const cCompositeChat & a_Message)
+{
+ for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr)
+ {
+ itr->second->BroadcastChat(a_Message);
} // for itr - m_WorldsByName[]
}
diff --git a/src/Root.h b/src/Root.h
index 13e208b8d..4bbd7586f 100644
--- a/src/Root.h
+++ b/src/Root.h
@@ -20,7 +20,8 @@ class cPluginManager;
class cServer;
class cWorld;
class cPlayer;
-class cCommandOutputCallback ;
+class cCommandOutputCallback;
+class cCompositeChat;
typedef cItemCallback<cPlayer> cPlayerListCallback;
typedef cItemCallback<cWorld> cWorldListCallback;
@@ -108,20 +109,19 @@ public:
/// Finds a player from a partial or complete player name and calls the callback - case-insensitive
bool FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
- void LoopWorldsAndBroadcastChat(const AString & a_Message, ChatPrefixCodes a_ChatPrefix);
- void BroadcastChatJoin (const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtJoin); }
- void BroadcastChatLeave (const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtLeave); }
- void BroadcastChatDeath (const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtDeath); }
-
// tolua_begin
/// Sends a chat message to all connected clients (in all worlds)
- void BroadcastChat (const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtCustom); }
- void BroadcastChatInfo (const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtInformation); }
- void BroadcastChatFailure(const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtFailure); }
- void BroadcastChatSuccess(const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtSuccess); }
- void BroadcastChatWarning(const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtWarning); }
- void BroadcastChatFatal (const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtFailure); }
+ void BroadcastChat (const AString & a_Message, eMessageType a_ChatPrefix = mtCustom);
+ void BroadcastChatInfo (const AString & a_Message) { BroadcastChat(a_Message, mtInformation); }
+ void BroadcastChatFailure(const AString & a_Message) { BroadcastChat(a_Message, mtFailure); }
+ void BroadcastChatSuccess(const AString & a_Message) { BroadcastChat(a_Message, mtSuccess); }
+ void BroadcastChatWarning(const AString & a_Message) { BroadcastChat(a_Message, mtWarning); }
+ void BroadcastChatFatal (const AString & a_Message) { BroadcastChat(a_Message, mtFailure); }
+ void BroadcastChatJoin (const AString & a_Message) { BroadcastChat(a_Message, mtJoin); }
+ void BroadcastChatLeave (const AString & a_Message) { BroadcastChat(a_Message, mtLeave); }
+ void BroadcastChatDeath (const AString & a_Message) { BroadcastChat(a_Message, mtDeath); }
+ void BroadcastChat (const cCompositeChat & a_Message);
/// Returns the textual description of the protocol version: 49 -> "1.4.4". Provided specifically for Lua API
static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum);
diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp
index 985fdee27..60dabaf84 100644
--- a/src/Simulator/IncrementalRedstoneSimulator.cpp
+++ b/src/Simulator/IncrementalRedstoneSimulator.cpp
@@ -8,7 +8,6 @@
#include "../Entities/TNTEntity.h"
#include "../Blocks/BlockTorch.h"
#include "../Blocks/BlockDoor.h"
-#include "../Blocks/BlockFenceGate.h"
#include "../Piston.h"
@@ -32,7 +31,7 @@ cIncrementalRedstoneSimulator::~cIncrementalRedstoneSimulator()
-void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk)
+void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk, cChunk * a_OtherChunk)
{
if ((a_Chunk == NULL) || !a_Chunk->IsValid())
{
@@ -43,17 +42,33 @@ void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_B
return;
}
- int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width;
- int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width;
-
+ // We may be called with coordinates in a chunk that is not the first chunk parameter
+ // In that case, the actual chunk (which the coordinates are in), will be passed as the second parameter
+ // Use that Chunk pointer to get a relative position
+
+ int RelX = 0;
+ int RelZ = 0;
BLOCKTYPE Block;
NIBBLETYPE Meta;
- a_Chunk->GetBlockTypeMeta(RelX, a_BlockY, RelZ, Block, Meta);
+
+ if (a_OtherChunk != NULL)
+ {
+ RelX = a_BlockX - a_OtherChunk->GetPosX() * cChunkDef::Width;
+ RelZ = a_BlockZ - a_OtherChunk->GetPosZ() * cChunkDef::Width;
+ a_OtherChunk->GetBlockTypeMeta(RelX, a_BlockY, RelZ, Block, Meta);
+ }
+ else
+ {
+ RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width;
+ RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width;
+ a_Chunk->GetBlockTypeMeta(RelX, a_BlockY, RelZ, Block, Meta);
+ }
// Every time a block is changed (AddBlock called), we want to go through all lists and check to see if the coordiantes stored within are still valid
// Checking only when a block is changed, as opposed to every tick, also improves performance
- for (PoweredBlocksList::iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr)
+ PoweredBlocksList * PoweredBlocks = a_Chunk->GetRedstoneSimulatorPoweredBlocksList();
+ for (PoweredBlocksList::iterator itr = PoweredBlocks->begin(); itr != PoweredBlocks->end(); ++itr)
{
if (!itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
@@ -63,7 +78,7 @@ void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_B
if (!IsPotentialSource(Block))
{
LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from powered blocks list as it no longer connected to a source", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
- m_PoweredBlocks.erase(itr);
+ PoweredBlocks->erase(itr);
break;
}
else if (
@@ -76,7 +91,7 @@ void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_B
)
{
LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from powered blocks list due to present/past metadata mismatch", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
- m_PoweredBlocks.erase(itr);
+ PoweredBlocks->erase(itr);
break;
}
else if (Block == E_BLOCK_DAYLIGHT_SENSOR)
@@ -94,22 +109,24 @@ void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_B
if (a_Chunk->GetTimeAlteredLight(SkyLight) <= 8) // Could use SkyLight - m_World.GetSkyDarkness();
{
LOGD("cIncrementalRedstoneSimulator: Erased daylight sensor from powered blocks list due to insufficient light level");
- m_PoweredBlocks.erase(itr);
+ PoweredBlocks->erase(itr);
break;
}
}
}
}
- for (LinkedBlocksList::iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr)
+ LinkedBlocksList * LinkedPoweredBlocks = a_Chunk->GetRedstoneSimulatorLinkedBlocksList();
+ // We loop through all values (insteading of breaking out at the first) to make sure everything is gone, as there can be multiple SourceBlock entries for one AddBlock coordinate
+ for (LinkedBlocksList::iterator itr = LinkedPoweredBlocks->begin(); itr != LinkedPoweredBlocks->end();)
{
if (itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
if (!IsPotentialSource(Block))
{
LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list as it is no longer connected to a source", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
- m_LinkedPoweredBlocks.erase(itr);
- break;
+ itr = LinkedPoweredBlocks->erase(itr);
+ continue;
}
else if (
// Things that can send power through a block but which depends on meta
@@ -119,8 +136,8 @@ void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_B
)
{
LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list due to present/past metadata mismatch", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
- m_LinkedPoweredBlocks.erase(itr);
- break;
+ itr = LinkedPoweredBlocks->erase(itr);
+ continue;
}
}
else if (itr->a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
@@ -128,13 +145,15 @@ void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_B
if (!IsViableMiddleBlock(Block))
{
LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list as it is no longer powered through a valid middle block", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
- m_LinkedPoweredBlocks.erase(itr);
- break;
+ itr = LinkedPoweredBlocks->erase(itr);
+ continue;
}
}
+ ++itr;
}
- for (SimulatedPlayerToggleableList::iterator itr = m_SimulatedPlayerToggleableBlocks.begin(); itr != m_SimulatedPlayerToggleableBlocks.end(); ++itr)
+ SimulatedPlayerToggleableList * SimulatedPlayerToggleableBlocks = a_Chunk->GetRedstoneSimulatorSimulatedPlayerToggleableList();
+ for (SimulatedPlayerToggleableList::iterator itr = SimulatedPlayerToggleableBlocks->begin(); itr != SimulatedPlayerToggleableBlocks->end(); ++itr)
{
if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
@@ -144,12 +163,13 @@ void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_B
if (!IsAllowedBlock(Block))
{
LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from toggleable simulated list as it is no longer redstone", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
- m_SimulatedPlayerToggleableBlocks.erase(itr);
+ SimulatedPlayerToggleableBlocks->erase(itr);
break;
}
}
- for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end(); ++itr)
+ RepeatersDelayList * RepeatersDelayList = a_Chunk->GetRedstoneSimulatorRepeatersDelayList();
+ for (RepeatersDelayList::iterator itr = RepeatersDelayList->begin(); itr != RepeatersDelayList->end(); ++itr)
{
if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
@@ -158,13 +178,19 @@ void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_B
if ((Block != E_BLOCK_REDSTONE_REPEATER_ON) && (Block != E_BLOCK_REDSTONE_REPEATER_OFF))
{
- m_RepeatersDelayList.erase(itr);
+ RepeatersDelayList->erase(itr);
break;
}
}
- cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData();
- for (cRedstoneSimulatorChunkData::iterator itr = ChunkData.begin(); itr != ChunkData.end(); ++itr)
+ if (a_OtherChunk != NULL)
+ {
+ // DO NOT touch our chunk's data structure if we are being called with coordinates from another chunk - this one caused me massive grief :P
+ return;
+ }
+
+ cRedstoneSimulatorChunkData * RedstoneSimulatorChunkData = a_Chunk->GetRedstoneSimulatorData();
+ for (cRedstoneSimulatorChunkData::iterator itr = RedstoneSimulatorChunkData->begin(); itr != RedstoneSimulatorChunkData->end(); ++itr)
{
if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ)) // We are at an entry matching the current (changed) block
{
@@ -185,7 +211,7 @@ void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_B
return;
}
- ChunkData.push_back(cCoordWithBlockAndBool(RelX, a_BlockY, RelZ, Block, false));
+ RedstoneSimulatorChunkData->push_back(cCoordWithBlockAndBool(RelX, a_BlockY, RelZ, Block, false));
}
@@ -199,20 +225,26 @@ void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int
// The easiest way to make this more efficient is probably just to reduce code within the handlers that put too much strain on server, like getting or setting blocks
// A marking dirty system might be a TODO for later on, perhaps
- cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData();
- if (ChunkData.empty())
+ m_RedstoneSimulatorChunkData = a_Chunk->GetRedstoneSimulatorData();
+ if (m_RedstoneSimulatorChunkData->empty())
{
return;
}
+ m_PoweredBlocks = a_Chunk->GetRedstoneSimulatorPoweredBlocksList();
+ m_RepeatersDelayList = a_Chunk->GetRedstoneSimulatorRepeatersDelayList();
+ m_SimulatedPlayerToggleableBlocks = a_Chunk->GetRedstoneSimulatorSimulatedPlayerToggleableList();
+ m_LinkedPoweredBlocks = a_Chunk->GetRedstoneSimulatorLinkedBlocksList();
+ m_Chunk = a_Chunk;
+
int BaseX = a_Chunk->GetPosX() * cChunkDef::Width;
int BaseZ = a_Chunk->GetPosZ() * cChunkDef::Width;
- for (cRedstoneSimulatorChunkData::iterator dataitr = ChunkData.begin(); dataitr != ChunkData.end();)
+ for (cRedstoneSimulatorChunkData::iterator dataitr = m_RedstoneSimulatorChunkData->begin(); dataitr != m_RedstoneSimulatorChunkData->end();)
{
if (dataitr->DataTwo)
{
- dataitr = ChunkData.erase(dataitr);
+ dataitr = m_RedstoneSimulatorChunkData->erase(dataitr);
continue;
}
@@ -285,6 +317,7 @@ void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int
HandlePressurePlate(a_X, dataitr->y, a_Z, dataitr->Data);
break;
}
+ default: LOGD("Unhandled block (!) or unimplemented redstone block: %s", ItemToString(dataitr->Data).c_str()); break;
}
++dataitr;
}
@@ -293,6 +326,35 @@ void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int
+void cIncrementalRedstoneSimulator::WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk)
+{
+ if (
+ ((a_BlockX % cChunkDef::Width) <= 1) ||
+ ((a_BlockX % cChunkDef::Width) >= 14) ||
+ ((a_BlockZ % cChunkDef::Width) <= 1) ||
+ ((a_BlockZ % cChunkDef::Width) >= 14)
+ ) // Are we on a chunk boundary? ± 2 because of LinkedPowered blocks
+ {
+ // On a chunk boundary, alert all four sides (i.e. at least one neighbouring chunk)
+ AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk);
+
+ // Pass the original coordinates, because when adding things to our simulator lists, we get the chunk that they are in, and therefore any updates need to preseve their position
+ // RedstoneAddBlock to pass both the neighbouring chunk and the chunk which the coordiantes are in and ± 2 in GetNeighbour() to accomodate for LinkedPowered blocks being 2 away from chunk boundaries
+ RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX - 2, a_BlockZ), a_Chunk);
+ RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX + 2, a_BlockZ), a_Chunk);
+ RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ - 2), a_Chunk);
+ RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ + 2), a_Chunk);
+
+ return;
+ }
+
+ // Not on boundary, just alert this chunk for speed
+ AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk);
+}
+
+
+
+
void cIncrementalRedstoneSimulator::HandleRedstoneTorch(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState)
{
@@ -318,22 +380,6 @@ void cIncrementalRedstoneSimulator::HandleRedstoneTorch(int a_BlockX, int a_Bloc
{
// There was a match, torch goes off
m_World.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_OFF, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ));
-
- float Pitch = (((1.0F-0.0F)*((float)rand()/RAND_MAX)) - ((1.0F-0.0F)*((float)rand()/RAND_MAX))) * 0.8F;
- m_World.BroadcastSoundEffect("random.fizz",
- ((int) (a_BlockX + 0.5F) * 8.0),
- ((int) (a_BlockY + 0.5F) * 8.0),
- ((int) (a_BlockZ + 0.5F) * 8.0),
- 0.5F,
- 2.6F + Pitch);
-
- for (int l = 0; l < 5; ++l) {
- float d0 = a_BlockX + (double(rand())/RAND_MAX) * 0.6F + 0.2F;
- float d1 = a_BlockY + (double(rand())/RAND_MAX) * 0.6F + 0.2F;
- float d2 = a_BlockZ + (double(rand())/RAND_MAX) * 0.6F + 0.2F;
- m_World.BroadcastParticleEffect("smoke", d0, d1, d2, 0.0F, 0.0F, 0.0F, 0.01F, 1);
- }
-
return;
}
@@ -424,7 +470,7 @@ void cIncrementalRedstoneSimulator::HandleFenceGate(int a_BlockX, int a_BlockY,
{
cChunkInterface ChunkInterface(m_World.GetChunkMap());
NIBBLETYPE MetaData = ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
-
+
if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ))
{
if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, true))
@@ -432,7 +478,7 @@ void cIncrementalRedstoneSimulator::HandleFenceGate(int a_BlockX, int a_BlockY,
m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, MetaData | 0x4);
m_World.BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0);
SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, true);
- }
+ }
}
else
{
@@ -554,7 +600,7 @@ void cIncrementalRedstoneSimulator::HandleRedstoneWire(int a_BlockX, int a_Block
}
}
- if (TimesMetaSmaller == TimesFoundAWire)
+ if ((TimesMetaSmaller == TimesFoundAWire) && (MyMeta != 0))
{
// All surrounding metas were smaller - self must have been a wire that was
// transferring power to other wires around.
@@ -563,7 +609,7 @@ void cIncrementalRedstoneSimulator::HandleRedstoneWire(int a_BlockX, int a_Block
m_World.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE, 0); // SetMeta & WakeUpSims doesn't seem to work here, so SetBlock
return; // No need to process block power sets because self not powered
}
- else
+ else if (MyMeta != MetaToSet)
{
m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, MetaToSet);
}
@@ -587,12 +633,15 @@ void cIncrementalRedstoneSimulator::HandleRedstoneWire(int a_BlockX, int a_Block
{
case REDSTONE_NONE:
{
- SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE);
+ SetBlockPowered(a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE);
+ SetBlockPowered(a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE);
+ SetBlockPowered(a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE);
+ SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE);
+ SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE);
SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, E_BLOCK_REDSTONE_WIRE);
SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, E_BLOCK_REDSTONE_WIRE);
SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YM, E_BLOCK_REDSTONE_WIRE);
- SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YP, E_BLOCK_REDSTONE_WIRE);
SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZM, E_BLOCK_REDSTONE_WIRE);
SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, E_BLOCK_REDSTONE_WIRE);
break;
@@ -638,14 +687,14 @@ void cIncrementalRedstoneSimulator::HandleRedstoneRepeater(int a_BlockX, int a_B
if (IsSelfPowered && !IsOn) // Queue a power change if I am receiving power but not on
{
- QueueRepeaterPowerChange(a_BlockX, a_BlockY, a_BlockZ, a_Meta, 0, true);
+ QueueRepeaterPowerChange(a_BlockX, a_BlockY, a_BlockZ, a_Meta, true);
}
else if (!IsSelfPowered && IsOn) // Queue a power change if I am not receiving power but on
{
- QueueRepeaterPowerChange(a_BlockX, a_BlockY, a_BlockZ, a_Meta, 0, false);
+ QueueRepeaterPowerChange(a_BlockX, a_BlockY, a_BlockZ, a_Meta, false);
}
- for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end(); ++itr)
+ for (RepeatersDelayList::iterator itr = m_RepeatersDelayList->begin(); itr != m_RepeatersDelayList->end(); ++itr)
{
if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
@@ -699,7 +748,7 @@ void cIncrementalRedstoneSimulator::HandleRedstoneRepeater(int a_BlockX, int a_B
{
m_World.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_OFF, a_Meta);
}
- m_RepeatersDelayList.erase(itr); // We can remove off repeaters which don't need further updating
+ m_RepeatersDelayList->erase(itr); // We can remove off repeaters which don't need further updating
return;
}
}
@@ -1060,7 +1109,7 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_BlockX, int a_Bloc
bool cIncrementalRedstoneSimulator::AreCoordsDirectlyPowered(int a_BlockX, int a_BlockY, int a_BlockZ)
{
- for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr) // Check powered list
+ for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks->begin(); itr != m_PoweredBlocks->end(); ++itr) // Check powered list
{
if (itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
@@ -1076,7 +1125,7 @@ bool cIncrementalRedstoneSimulator::AreCoordsDirectlyPowered(int a_BlockX, int a
bool cIncrementalRedstoneSimulator::AreCoordsLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ)
{
- for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr) // Check linked powered list
+ for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks->begin(); itr != m_LinkedPoweredBlocks->end(); ++itr) // Check linked powered list
{
if (itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
@@ -1094,7 +1143,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterPowered(int a_BlockX, int a_BlockY
{
// Repeaters cannot be powered by any face except their back; verify that this is true for a source
- for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr)
+ for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks->begin(); itr != m_PoweredBlocks->end(); ++itr)
{
if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; }
@@ -1124,7 +1173,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterPowered(int a_BlockX, int a_BlockY
}
}
- for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr)
+ for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks->begin(); itr != m_LinkedPoweredBlocks->end(); ++itr)
{
if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; }
@@ -1165,7 +1214,7 @@ bool cIncrementalRedstoneSimulator::IsPistonPowered(int a_BlockX, int a_BlockY,
int OldX = a_BlockX, OldY = a_BlockY, OldZ = a_BlockZ;
eBlockFace Face = cPiston::MetaDataToDirection(a_Meta);
- for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr)
+ for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks->begin(); itr != m_PoweredBlocks->end(); ++itr)
{
if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; }
@@ -1181,7 +1230,7 @@ bool cIncrementalRedstoneSimulator::IsPistonPowered(int a_BlockX, int a_BlockY,
a_BlockZ = OldZ;
}
- for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr)
+ for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks->begin(); itr != m_LinkedPoweredBlocks->end(); ++itr)
{
if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; }
@@ -1204,7 +1253,7 @@ bool cIncrementalRedstoneSimulator::IsPistonPowered(int a_BlockX, int a_BlockY,
bool cIncrementalRedstoneSimulator::IsWirePowered(int a_BlockX, int a_BlockY, int a_BlockZ)
{
- for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr)
+ for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks->begin(); itr != m_PoweredBlocks->end(); ++itr)
{
if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; }
@@ -1214,7 +1263,7 @@ bool cIncrementalRedstoneSimulator::IsWirePowered(int a_BlockX, int a_BlockY, in
}
}
- for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr)
+ for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks->begin(); itr != m_LinkedPoweredBlocks->end(); ++itr)
{
if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; }
@@ -1232,7 +1281,7 @@ bool cIncrementalRedstoneSimulator::IsWirePowered(int a_BlockX, int a_BlockY, in
bool cIncrementalRedstoneSimulator::AreCoordsSimulated(int a_BlockX, int a_BlockY, int a_BlockZ, bool IsCurrentStatePowered)
{
- for (SimulatedPlayerToggleableList::const_iterator itr = m_SimulatedPlayerToggleableBlocks.begin(); itr != m_SimulatedPlayerToggleableBlocks.end(); ++itr)
+ for (SimulatedPlayerToggleableList::const_iterator itr = m_SimulatedPlayerToggleableBlocks->begin(); itr != m_SimulatedPlayerToggleableBlocks->end(); ++itr)
{
if (itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
@@ -1375,7 +1424,9 @@ void cIncrementalRedstoneSimulator::SetBlockPowered(int a_BlockX, int a_BlockY,
return;
}
- for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr) // Check powered list
+ PoweredBlocksList * Powered = m_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ)->GetRedstoneSimulatorPoweredBlocksList();
+
+ for (PoweredBlocksList::const_iterator itr = Powered->begin(); itr != Powered->end(); ++itr) // Check powered list
{
if (
itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)) &&
@@ -1390,7 +1441,7 @@ void cIncrementalRedstoneSimulator::SetBlockPowered(int a_BlockX, int a_BlockY,
sPoweredBlocks RC;
RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ);
RC.a_SourcePos = Vector3i(a_SourceX, a_SourceY, a_SourceZ);
- m_PoweredBlocks.push_back(RC);
+ Powered->push_back(RC);
}
@@ -1415,7 +1466,9 @@ void cIncrementalRedstoneSimulator::SetBlockLinkedPowered(
return;
}
- for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr) // Check linked powered list
+ LinkedBlocksList * Linked = m_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ)->GetRedstoneSimulatorLinkedBlocksList();
+
+ for (LinkedBlocksList::const_iterator itr = Linked->begin(); itr != Linked->end(); ++itr) // Check linked powered list
{
if (
itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)) &&
@@ -1432,7 +1485,7 @@ void cIncrementalRedstoneSimulator::SetBlockLinkedPowered(
RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ);
RC.a_MiddlePos = Vector3i(a_MiddleX, a_MiddleY, a_MiddleZ);
RC.a_SourcePos = Vector3i(a_SourceX, a_SourceY, a_SourceZ);
- m_LinkedPoweredBlocks.push_back(RC);
+ Linked->push_back(RC);
}
@@ -1441,7 +1494,7 @@ void cIncrementalRedstoneSimulator::SetBlockLinkedPowered(
void cIncrementalRedstoneSimulator::SetPlayerToggleableBlockAsSimulated(int a_BlockX, int a_BlockY, int a_BlockZ, bool WasLastStatePowered)
{
- for (SimulatedPlayerToggleableList::iterator itr = m_SimulatedPlayerToggleableBlocks.begin(); itr != m_SimulatedPlayerToggleableBlocks.end(); ++itr)
+ for (SimulatedPlayerToggleableList::iterator itr = m_SimulatedPlayerToggleableBlocks->begin(); itr != m_SimulatedPlayerToggleableBlocks->end(); ++itr)
{
if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
@@ -1465,16 +1518,16 @@ void cIncrementalRedstoneSimulator::SetPlayerToggleableBlockAsSimulated(int a_Bl
sSimulatedPlayerToggleableList RC;
RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ);
RC.WasLastStatePowered = WasLastStatePowered;
- m_SimulatedPlayerToggleableBlocks.push_back(RC);
+ m_SimulatedPlayerToggleableBlocks->push_back(RC);
}
-void cIncrementalRedstoneSimulator::QueueRepeaterPowerChange(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta, short a_ElapsedTicks, bool ShouldPowerOn)
+void cIncrementalRedstoneSimulator::QueueRepeaterPowerChange(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta, bool ShouldPowerOn)
{
- for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end(); ++itr)
+ for (RepeatersDelayList::iterator itr = m_RepeatersDelayList->begin(); itr != m_RepeatersDelayList->end(); ++itr)
{
if (itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
@@ -1484,7 +1537,7 @@ void cIncrementalRedstoneSimulator::QueueRepeaterPowerChange(int a_BlockX, int a
}
// Already in here (normal to allow repeater to continue on powering and updating blocks in front) - just update info and quit
- itr->a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + (ShouldPowerOn ? 1 : 0)) * 2; // See below for description
+ itr->a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + 1) * 2; // See below for description
itr->a_ElapsedTicks = 0;
itr->ShouldPowerOn = ShouldPowerOn;
return;
@@ -1492,18 +1545,16 @@ void cIncrementalRedstoneSimulator::QueueRepeaterPowerChange(int a_BlockX, int a
}
// Self not in list, add self to list
- sRepeatersDelayList RC;
+ sRepeatersDelayList RC;
RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ);
// Gets the top two bits (delay time), shifts them into the lower two bits, and adds one (meta 0 = 1 tick; 1 = 2 etc.)
// * 2 because apparently, MCS ticks are way faster than vanilla ticks, so repeater aren't noticeably delayed
- // We don't +1 when powering off because everything seems to already delay a tick when powering off, why? No idea :P
- RC.a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + (ShouldPowerOn ? 1 : 0)) * 2;
-
+ RC.a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + 1) * 2;
RC.a_ElapsedTicks = 0;
RC.ShouldPowerOn = ShouldPowerOn;
- m_RepeatersDelayList.push_back(RC);
+ m_RepeatersDelayList->push_back(RC);
return;
}
diff --git a/src/Simulator/IncrementalRedstoneSimulator.h b/src/Simulator/IncrementalRedstoneSimulator.h
index bcf89bb82..e6bc28621 100644
--- a/src/Simulator/IncrementalRedstoneSimulator.h
+++ b/src/Simulator/IncrementalRedstoneSimulator.h
@@ -3,7 +3,7 @@
#include "RedstoneSimulator.h"
-/// Per-chunk data for the simulator, specified individual chunks to simulate; 'Data' is not used
+/// Per-chunk data for the simulator, specified individual chunks to simulate
typedef cCoordWithBlockAndBoolVector cRedstoneSimulatorChunkData;
@@ -21,7 +21,8 @@ public:
virtual void Simulate(float a_Dt) override { UNUSED(a_Dt);} // not used
virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override;
- virtual bool IsAllowedBlock( BLOCKTYPE a_BlockType ) override { return IsRedstone(a_BlockType); }
+ virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override { return IsRedstone(a_BlockType); }
+ virtual void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override;
enum eRedstoneDirection
{
@@ -57,22 +58,29 @@ private:
struct sRepeatersDelayList
{
Vector3i a_BlockPos;
- short a_DelayTicks;
- short a_ElapsedTicks;
+ unsigned char a_DelayTicks;
+ unsigned char a_ElapsedTicks;
bool ShouldPowerOn;
};
-
+
+public:
+
typedef std::vector <sPoweredBlocks> PoweredBlocksList;
typedef std::vector <sLinkedPoweredBlocks> LinkedBlocksList;
typedef std::vector <sSimulatedPlayerToggleableList> SimulatedPlayerToggleableList;
typedef std::vector <sRepeatersDelayList> RepeatersDelayList;
- PoweredBlocksList m_PoweredBlocks;
- LinkedBlocksList m_LinkedPoweredBlocks;
- SimulatedPlayerToggleableList m_SimulatedPlayerToggleableBlocks;
- RepeatersDelayList m_RepeatersDelayList;
+private:
+
+ cRedstoneSimulatorChunkData * m_RedstoneSimulatorChunkData;
+ PoweredBlocksList * m_PoweredBlocks;
+ LinkedBlocksList * m_LinkedPoweredBlocks;
+ SimulatedPlayerToggleableList * m_SimulatedPlayerToggleableBlocks;
+ RepeatersDelayList * m_RepeatersDelayList;
- virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override;
+ virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override { RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk); }
+ void RedstoneAddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk, cChunk * a_OtherChunk = NULL);
+ cChunk * m_Chunk;
// We want a_MyState for devices needing a full FastSetBlock (as opposed to meta) because with our simulation model, we cannot keep setting the block if it is already set correctly
// In addition to being non-performant, it would stop the player from actually breaking said device
@@ -84,8 +92,6 @@ private:
void HandleRedstoneBlock(int a_BlockX, int a_BlockY, int a_BlockZ);
/** Handles levers */
void HandleRedstoneLever(int a_BlockX, int a_BlockY, int a_BlockZ);
- /** Handles Fence Gates */
- void HandleFenceGate(int a_BlockX, int a_BlockY, int a_BlockZ);
/** Handles buttons */
void HandleRedstoneButton(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType);
/** Handles daylight sensors */
@@ -118,6 +124,8 @@ private:
void HandleRail(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType);
/** Handles trapdoors */
void HandleTrapdoor(int a_BlockX, int a_BlockY, int a_BlockZ);
+ /** Handles fence gates */
+ void HandleFenceGate(int a_BlockX, int a_BlockY, int a_BlockZ);
/** Handles noteblocks */
void HandleNoteBlock(int a_BlockX, int a_BlockY, int a_BlockZ);
/* ===================== */
@@ -134,7 +142,7 @@ private:
/** Marks all blocks immediately surrounding a coordinate as powered */
void SetAllDirsAsPowered(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_SourceBlock);
/** Queues a repeater to be powered or unpowered */
- void QueueRepeaterPowerChange(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta, short a_ElapsedTicks, bool ShouldPowerOn);
+ void QueueRepeaterPowerChange(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta, bool ShouldPowerOn);
/** Returns if a coordinate is powered or linked powered */
bool AreCoordsPowered(int a_BlockX, int a_BlockY, int a_BlockZ) { return AreCoordsDirectlyPowered(a_BlockX, a_BlockY, a_BlockZ) || AreCoordsLinkedPowered(a_BlockX, a_BlockY, a_BlockZ); }
@@ -267,3 +275,7 @@ private:
}
}
};
+
+
+
+
diff --git a/src/World.cpp b/src/World.cpp
index cb07caa5d..d67ad36d1 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -1747,7 +1747,7 @@ void cWorld::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cons
-void cWorld::LoopPlayersAndBroadcastChat(const AString & a_Message, ChatPrefixCodes a_ChatPrefix, const cClientHandle * a_Exclude)
+void cWorld::BroadcastChat(const AString & a_Message, const cClientHandle * a_Exclude, eMessageType a_ChatPrefix)
{
cCSLock Lock(m_CSPlayers);
for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
@@ -1765,6 +1765,24 @@ void cWorld::LoopPlayersAndBroadcastChat(const AString & a_Message, ChatPrefixCo
+void cWorld::BroadcastChat(const cCompositeChat & a_Message, const cClientHandle * a_Exclude)
+{
+ cCSLock Lock(m_CSPlayers);
+ for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ cClientHandle * ch = (*itr)->GetClientHandle();
+ if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed())
+ {
+ continue;
+ }
+ ch->SendChat(a_Message);
+ }
+}
+
+
+
+
+
void cWorld::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude)
{
m_ChunkMap->BroadcastChunkData(a_ChunkX, a_ChunkZ, a_Serializer, a_Exclude);
diff --git a/src/World.h b/src/World.h
index ca1b7dcc5..97358b88a 100644
--- a/src/World.h
+++ b/src/World.h
@@ -46,6 +46,7 @@ class cDispenserEntity;
class cFurnaceEntity;
class cNoteEntity;
class cMobCensus;
+class cCompositeChat;
typedef std::list< cPlayer * > cPlayerList;
@@ -167,16 +168,15 @@ public:
void BroadcastBlockBreakAnimation(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = NULL);
void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); ///< If there is a block entity at the specified coods, sends it to all clients except a_Exclude
- void LoopPlayersAndBroadcastChat(const AString & a_Message, ChatPrefixCodes a_ChatPrefix, const cClientHandle * a_Exclude = NULL);
- void BroadcastChatDeath (const AString & a_Message, const cClientHandle * a_Exclude = NULL) { LoopPlayersAndBroadcastChat(a_Message, mtDeath, a_Exclude); }
-
// tolua_begin
- void BroadcastChat (const AString & a_Message, const cClientHandle * a_Exclude = NULL) { LoopPlayersAndBroadcastChat(a_Message, mtCustom, a_Exclude); }
- void BroadcastChatInfo (const AString & a_Message, const cClientHandle * a_Exclude = NULL) { LoopPlayersAndBroadcastChat(a_Message, mtInformation, a_Exclude); }
- void BroadcastChatFailure(const AString & a_Message, const cClientHandle * a_Exclude = NULL) { LoopPlayersAndBroadcastChat(a_Message, mtFailure, a_Exclude); }
- void BroadcastChatSuccess(const AString & a_Message, const cClientHandle * a_Exclude = NULL) { LoopPlayersAndBroadcastChat(a_Message, mtSuccess, a_Exclude); }
- void BroadcastChatWarning(const AString & a_Message, const cClientHandle * a_Exclude = NULL) { LoopPlayersAndBroadcastChat(a_Message, mtWarning, a_Exclude); }
- void BroadcastChatFatal (const AString & a_Message, const cClientHandle * a_Exclude = NULL) { LoopPlayersAndBroadcastChat(a_Message, mtFailure, a_Exclude); }
+ void BroadcastChat (const AString & a_Message, const cClientHandle * a_Exclude = NULL, eMessageType a_ChatPrefix = mtCustom);
+ void BroadcastChatInfo (const AString & a_Message, const cClientHandle * a_Exclude = NULL) { BroadcastChat(a_Message, a_Exclude, mtInformation); }
+ void BroadcastChatFailure(const AString & a_Message, const cClientHandle * a_Exclude = NULL) { BroadcastChat(a_Message, a_Exclude, mtFailure); }
+ void BroadcastChatSuccess(const AString & a_Message, const cClientHandle * a_Exclude = NULL) { BroadcastChat(a_Message, a_Exclude, mtSuccess); }
+ void BroadcastChatWarning(const AString & a_Message, const cClientHandle * a_Exclude = NULL) { BroadcastChat(a_Message, a_Exclude, mtWarning); }
+ void BroadcastChatFatal (const AString & a_Message, const cClientHandle * a_Exclude = NULL) { BroadcastChat(a_Message, a_Exclude, mtFailure); }
+ void BroadcastChatDeath (const AString & a_Message, const cClientHandle * a_Exclude = NULL) { BroadcastChat(a_Message, a_Exclude, mtDeath); }
+ void BroadcastChat (const cCompositeChat & a_Message, const cClientHandle * a_Exclude = NULL);
// tolua_end
void BroadcastChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL);