diff options
Diffstat (limited to 'src')
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); |