diff options
45 files changed, 1019 insertions, 124 deletions
diff --git a/COMPILING.md b/COMPILING.md index d3c896bdd..139f1a0ee 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -69,7 +69,7 @@ Assuming you are in the MCServer folder created in the initial setup step, you n ``` mkdir Release cd Release -cmake . -DCMAKE_BUILD_TYPE=RELEASE .. && make +cmake -DCMAKE_BUILD_TYPE=RELEASE .. && make ``` The executable will be built in the `MCServer/MCServer` folder and will be named `MCServer`. @@ -81,7 +81,7 @@ Assuming you are in the MCServer folder created in the Getting the sources step, ``` mkdir Debug cd Debug -cmake . -DCMAKE_BUILD_TYPE=DEBUG && make` +cmake -DCMAKE_BUILD_TYPE=DEBUG .. && make ``` The executable will be built in the `MCServer/MCServer` folder and will be named `MCServer_debug`. diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index e877ec446..34aff5ddb 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -2031,6 +2031,7 @@ end BroadcastChatInfo = { Params = "Message, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Prepends Yellow [INFO] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For informational messages, such as command usage." }, BroadcastChatSuccess = { Params = "Message, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Prepends Green [INFO] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For success messages." }, BroadcastChatWarning = { Params = "Message, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Prepends Rose [WARN] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For concerning events, such as plugin reload etc." }, + BroadcastParticleEffect = { Params = "ParticleName, X, Y, Z, OffSetX, OffSetY, OffSetZ, ParticleData, ParticleAmmount, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Spawns the specified particles to all players in the world exept the optional ExeptClient. A list of available particles by thinkofdeath can be found {{https://gist.github.com/thinkofdeath/5110835|Here}}" }, BroadcastSoundEffect = { Params = "SoundName, X, Y, Z, Volume, Pitch, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Sends the specified sound effect to all players in this world, except the optional ExceptClient" }, BroadcastSoundParticleEffect = { Params = "EffectID, X, Y, Z, EffectData, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Sends the specified effect to all players in this world, except the optional ExceptClient" }, CastThunderbolt = { Params = "X, Y, Z", Return = "", Notes = "Creates a thunderbolt at the specified coords" }, diff --git a/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html b/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html index 1eec4842a..35c880b00 100644 --- a/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html +++ b/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html @@ -20,13 +20,7 @@ <p> Let us begin. In order to begin development, we must firstly obtain a compiled copy of MCServer, and make sure that the Core plugin is within the Plugins folder, and activated. - Core handles much of the MCServer end-user experience and is a necessary component of - plugin development, as necessary plugin components depend on sone of its functions. - </p> - <p> - Next, we must obtain a copy of CoreMessaging.lua. This can be found - <a href="https://gist.github.com/bearbin/8715888">here.</a> - This is used to provide messaging support that is compliant with MCServer standards. + Core handles much of the MCServer end-user experience and gameplay will be very bland without it. </p> <h2>Creating the basic template</h2> <p> @@ -41,7 +35,11 @@ function Initialize(Plugin) Plugin:SetName("NewPlugin") Plugin:SetVersion(1) - PLUGIN = Plugin + -- Hooks + + PLUGIN = Plugin -- NOTE: only needed if you want OnDisable() to use GetName() or something like that + + -- Command Bindings LOG("Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion()) return true @@ -58,7 +56,8 @@ end <li><b>Plugin:SetName</b> sets the name of the plugin.</li> <li><b>Plugin:SetVersion</b> sets the revision number of the plugin. This must be an integer.</li> <li><b>LOG</b> logs to console a message, in this case, it prints that the plugin was initialised.</li> - <li>The <b>PLUGIN</b> variable just stores this plugin's object, so GetName() can be called in OnDisable (as no Plugin parameter is passed there, contrary to Initialize).</li> + <li>The <b>PLUGIN</b> variable just stores this plugin's object, so GetName() can be called in OnDisable (as no Plugin parameter is passed there, contrary to Initialize). + This global variable is only needed if you want to know the plugin details (name, etc.) when shutting down.</li> <li><b>function OnDisable</b> is called when the plugin is disabled, commonly when the server is shutting down. Perform cleanup and logging here.</li> </ul> Be sure to return true for this function, else MCS thinks you plugin had failed to initialise and prints a stacktrace with an error message. @@ -159,21 +158,23 @@ cPluginManager.BindCommand("/commandname", "permissionnode", FunctionToCall, " ~ a message. Again, see the API documentation for fuller details. But, you ask, how <i>do</i> we send a message to the client? </p> <p> - Remember that copy of CoreMessaging.lua that we downloaded earlier? Make sure that file is in your plugin folder, along with the main.lua file you are typing - your code in. Since MCS brings all the files together on JIT compile, we don't need to worry about requiring any files or such. Simply follow the below examples: + There are dedicated functions used for sending a player formatted messages. By format, I refer to coloured prefixes/coloured text (depending on configuration) + that clearly categorise what type of message a player is being sent. For example, an informational message has a yellow coloured [INFO] prefix, and a warning message + has a rose coloured [WARNING] prefix. A few of the most used functions are listed here, but see the API docs for more details. Look in the cRoot, cWorld, and cPlayer sections + for functions that broadcast to the entire server, the whole world, and a single player, respectively. </p> <pre class="prettyprint lang-lua"> -- Format: §yellow[INFO] §white%text% (yellow [INFO], white text following it) -- Use: Informational message, such as instructions for usage of a command -SendMessage(Player, "Usage: /explode [player]") +Player:SendMessageInfo("Usage: /explode [player]") -- Format: §green[INFO] §white%text% (green [INFO] etc.) -- Use: Success message, like when a command executes successfully -SendMessageSuccess(Player, "Notch was blown up!") +Player:SendMessageSuccess("Notch was blown up!") -- Format: §rose[INFO] §white%text% (rose coloured [INFO] etc.) -- Use: Failure message, like when a command was entered correctly but failed to run, such as when the destination player wasn't found in a /tp command -SendMessageFailure(Player, "Player Salted was not found") +Player:SendMessageFailure("Player Salted was not found") </pre> <p> Those are the basics. If you want to output text to the player for a reason other than the three listed above, and you want to colour the text, simply concatenate diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua index 8345e2169..60e84c947 100644 --- a/MCServer/Plugins/Debuggers/Debuggers.lua +++ b/MCServer/Plugins/Debuggers/Debuggers.lua @@ -19,18 +19,18 @@ function Initialize(Plugin) cPluginManager.AddHook(cPluginManager.HOOK_TICK, OnTick2); --]] - cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_USING_BLOCK, OnPlayerUsingBlock); - cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_USING_ITEM, OnPlayerUsingItem); - cPluginManager:AddHook(cPluginManager.HOOK_TAKE_DAMAGE, OnTakeDamage); - cPluginManager:AddHook(cPluginManager.HOOK_TICK, OnTick); - cPluginManager:AddHook(cPluginManager.HOOK_CHAT, OnChat); - cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_RIGHT_CLICKING_ENTITY, OnPlayerRightClickingEntity); - cPluginManager:AddHook(cPluginManager.HOOK_WORLD_TICK, OnWorldTick); - cPluginManager:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated); - cPluginManager:AddHook(cPluginManager.HOOK_PLUGINS_LOADED, OnPluginsLoaded); - cPluginManager:AddHook(cPluginManager.HOOK_PLUGIN_MESSAGE, OnPluginMessage); - - PM = cRoot:Get():GetPluginManager(); + local PM = cPluginManager; + PM:AddHook(cPluginManager.HOOK_PLAYER_USING_BLOCK, OnPlayerUsingBlock); + PM:AddHook(cPluginManager.HOOK_PLAYER_USING_ITEM, OnPlayerUsingItem); + PM:AddHook(cPluginManager.HOOK_TAKE_DAMAGE, OnTakeDamage); + PM:AddHook(cPluginManager.HOOK_TICK, OnTick); + PM:AddHook(cPluginManager.HOOK_CHAT, OnChat); + PM:AddHook(cPluginManager.HOOK_PLAYER_RIGHT_CLICKING_ENTITY, OnPlayerRightClickingEntity); + PM:AddHook(cPluginManager.HOOK_WORLD_TICK, OnWorldTick); + PM:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated); + PM:AddHook(cPluginManager.HOOK_PLUGINS_LOADED, OnPluginsLoaded); + PM:AddHook(cPluginManager.HOOK_PLUGIN_MESSAGE, OnPluginMessage); + PM:BindCommand("/le", "debuggers", HandleListEntitiesCmd, "- Shows a list of all the loaded entities"); PM:BindCommand("/ke", "debuggers", HandleKillEntitiesCmd, "- Kills all the loaded entities"); PM:BindCommand("/wool", "debuggers", HandleWoolCmd, "- Sets all your armor to blue wool"); @@ -54,8 +54,10 @@ function Initialize(Plugin) PM:BindCommand("/ff", "debuggers", HandleFurnaceFuel, "- Shows how long the currently held item would burn in a furnace"); PM:BindCommand("/sched", "debuggers", HandleSched, "- Schedules a simple countdown using cWorld:ScheduleTask()"); PM:BindCommand("/cs", "debuggers", HandleChunkStay, "- Tests the ChunkStay Lua integration for the specified chunk coords"); + PM:BindCommand("/compo", "debuggers", HandleCompo, "- Tests the cCompositeChat bindings") - Plugin:AddWebTab("Debuggers", HandleRequest_Debuggers); + Plugin:AddWebTab("Debuggers", HandleRequest_Debuggers) + Plugin:AddWebTab("StressTest", HandleRequest_StressTest) -- Enable the following line for BlockArea / Generator interface testing: -- PluginManager:AddHook(Plugin, cPluginManager.HOOK_CHUNK_GENERATED); @@ -1038,6 +1040,68 @@ end +local g_Counter = 0 +local g_JavaScript = +[[ +<script> +function createXHR() +{ + var request = false; + try { + request = new ActiveXObject('Msxml2.XMLHTTP'); + } + catch (err2) + { + try + { + request = new ActiveXObject('Microsoft.XMLHTTP'); + } + catch (err3) + { + try + { + request = new XMLHttpRequest(); + } + catch (err1) + { + request = false; + } + } + } + return request; +} + +function RefreshCounter() +{ + var xhr = createXHR(); + xhr.onreadystatechange = function() + { + if (xhr.readyState == 4) + { + document.getElementById("cnt").innerHTML = xhr.responseText; + } + }; + xhr.open("POST", "/~webadmin/Debuggers/StressTest", true); + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + xhr.send("counter=true"); +} + +setInterval(RefreshCounter, 10) +</script> +]] + +function HandleRequest_StressTest(a_Request) + if (a_Request.PostParams["counter"]) then + g_Counter = g_Counter + 1 + return tostring(g_Counter) + end + return g_JavaScript .. "<p>The counter below should be reloading as fast as possible</p><div id='cnt'>0</div>" +end + + + + + function OnPluginMessage(a_Client, a_Channel, a_Message) LOGINFO("Received a plugin message from client " .. a_Client:GetUsername() .. ": channel '" .. a_Channel .. "', message '" .. a_Message .. "'"); @@ -1132,3 +1196,25 @@ end + +function HandleCompo(a_Split, a_Player) + -- Send one composite message to self: + local msg = cCompositeChat() + msg:AddTextPart("Hello! ", "b@e") -- bold yellow + msg:AddUrlPart("MCServer", "http://mc-server.org") + msg:AddTextPart(" rules! ") + msg:AddRunCommandPart("Set morning", "/time set 0") + a_Player:SendMessage(msg) + + -- Broadcast another one to the world: + local msg2 = cCompositeChat() + msg2:AddSuggestCommandPart(a_Player:GetName(), "/tell " .. a_Player:GetName() .. " ") + msg2:AddTextPart(" knows how to use cCompositeChat!"); + a_Player:GetWorld():BroadcastChat(msg2) + + return true +end + + + + 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/BlockEntities/BlockEntityWithItems.h b/src/BlockEntities/BlockEntityWithItems.h index bf6289a2f..918781a00 100644 --- a/src/BlockEntities/BlockEntityWithItems.h +++ b/src/BlockEntities/BlockEntityWithItems.h @@ -11,6 +11,7 @@ #include "BlockEntity.h" #include "../ItemGrid.h" +#include "../UI/WindowOwner.h" @@ -22,6 +23,7 @@ class cBlockEntityWithItems : // tolua_end // tolua doesn't seem to support multiple inheritance? , public cItemGrid::cListener + , public cBlockEntityWindowOwner // tolua_begin { typedef cBlockEntity super; @@ -77,6 +79,11 @@ protected: ASSERT(a_Grid == &m_Contents); if (m_World != NULL) { + if (GetWindow() != NULL) + { + GetWindow()->BroadcastWholeWindow(); + } + m_World->MarkChunkDirty(GetChunkX(), GetChunkZ()); } } diff --git a/src/BlockEntities/ChestEntity.h b/src/BlockEntities/ChestEntity.h index 4110de1f3..ce16f84d7 100644 --- a/src/BlockEntities/ChestEntity.h +++ b/src/BlockEntities/ChestEntity.h @@ -2,7 +2,6 @@ #pragma once #include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" @@ -23,8 +22,7 @@ class cNBTData; // tolua_begin class cChestEntity : - public cBlockEntityWithItems, - public cBlockEntityWindowOwner + public cBlockEntityWithItems { typedef cBlockEntityWithItems super; diff --git a/src/BlockEntities/DropSpenserEntity.cpp b/src/BlockEntities/DropSpenserEntity.cpp index 7c9a40ce6..81df0fc8c 100644 --- a/src/BlockEntities/DropSpenserEntity.cpp +++ b/src/BlockEntities/DropSpenserEntity.cpp @@ -99,13 +99,6 @@ void cDropSpenserEntity::DropSpense(cChunk & a_Chunk) } m_World->BroadcastSoundParticleEffect(2000, m_PosX, m_PosY, m_PosZ, SmokeDir); m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.0f); - - // Update the UI window, if open: - cWindow * Window = GetWindow(); - if (Window != NULL) - { - Window->BroadcastWholeWindow(); - } } diff --git a/src/BlockEntities/DropSpenserEntity.h b/src/BlockEntities/DropSpenserEntity.h index f2f1eba36..47d3bd492 100644 --- a/src/BlockEntities/DropSpenserEntity.h +++ b/src/BlockEntities/DropSpenserEntity.h @@ -11,7 +11,6 @@ #pragma once #include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" @@ -31,8 +30,7 @@ class cServer; // tolua_begin class cDropSpenserEntity : - public cBlockEntityWithItems, - public cBlockEntityWindowOwner + public cBlockEntityWithItems { typedef cBlockEntityWithItems super; diff --git a/src/BlockEntities/EnderChestEntity.h b/src/BlockEntities/EnderChestEntity.h index 0ee3cab3b..45beee45f 100644 --- a/src/BlockEntities/EnderChestEntity.h +++ b/src/BlockEntities/EnderChestEntity.h @@ -2,7 +2,6 @@ #pragma once #include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" @@ -23,8 +22,7 @@ class cNBTData; // tolua_begin class cEnderChestEntity : - public cBlockEntityWithItems, - public cBlockEntityWindowOwner + public cBlockEntityWithItems { typedef cBlockEntityWithItems super; diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h index b08187300..5e08ae37a 100644 --- a/src/BlockEntities/FurnaceEntity.h +++ b/src/BlockEntities/FurnaceEntity.h @@ -2,7 +2,6 @@ #pragma once #include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" #include "../FurnaceRecipe.h" @@ -23,8 +22,7 @@ class cServer; // tolua_begin class cFurnaceEntity : - public cBlockEntityWithItems, - public cBlockEntityWindowOwner + public cBlockEntityWithItems { typedef cBlockEntityWithItems super; diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp index 2255cad64..31b23ac99 100644 --- a/src/BlockEntities/HopperEntity.cpp +++ b/src/BlockEntities/HopperEntity.cpp @@ -7,10 +7,12 @@ #include "HopperEntity.h" #include "../Chunk.h" #include "../Entities/Player.h" +#include "../Entities/Pickup.h" #include "../Bindings/PluginManager.h" #include "ChestEntity.h" #include "DropSpenserEntity.h" #include "FurnaceEntity.h" +#include "../BoundingBox.h" @@ -190,8 +192,87 @@ bool cHopperEntity::MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick) /// Moves pickups from above this hopper into it. Returns true if the contents have changed. bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick) { - // TODO - return false; + UNUSED(a_CurrentTick); + + class cHopperPickupSearchCallback : + public cEntityCallback + { + public: + cHopperPickupSearchCallback(const Vector3i & a_Pos, cItemGrid & a_Contents) : + m_Pos(a_Pos), + m_bFoundPickupsAbove(false), + m_Contents(a_Contents) + { + } + + virtual bool Item(cEntity * a_Entity) override + { + ASSERT(a_Entity != NULL); + + if (!a_Entity->IsPickup() || a_Entity->IsDestroyed()) + { + return false; + } + + Vector3f EntityPos = a_Entity->GetPosition(); + Vector3f BlockPos(m_Pos.x + 0.5f, (float)m_Pos.y + 1, m_Pos.z + 0.5f); // One block above hopper, and search from center outwards + float Distance = (EntityPos - BlockPos).Length(); + + if (Distance < 0.5) + { + if (TrySuckPickupIn((cPickup *)a_Entity)) + { + return false; + } + } + + return false; + } + + bool TrySuckPickupIn(cPickup * a_Pickup) + { + for (int i = 0; i < ContentsWidth * ContentsHeight; i++) + { + if (m_Contents.IsSlotEmpty(i)) + { + m_bFoundPickupsAbove = true; + m_Contents.SetSlot(i, a_Pickup->GetItem()); + a_Pickup->Destroy(); // Kill pickup + + return true; + } + else if (m_Contents.GetSlot(i).IsEqual(a_Pickup->GetItem()) && !m_Contents.GetSlot(i).IsFullStack()) + { + m_bFoundPickupsAbove = true; + + int PreviousCount = m_Contents.GetSlot(i).m_ItemCount; + a_Pickup->GetItem().m_ItemCount -= m_Contents.ChangeSlotCount(i, a_Pickup->GetItem().m_ItemCount) - PreviousCount; // Set count to however many items were added + + if (a_Pickup->GetItem().IsEmpty()) + { + a_Pickup->Destroy(); // Kill pickup if all items were added + } + return true; + } + } + return false; + } + + bool FoundPickupsAbove(void) const + { + return m_bFoundPickupsAbove; + } + + protected: + Vector3i m_Pos; + bool m_bFoundPickupsAbove; + cItemGrid & m_Contents; + }; + + cHopperPickupSearchCallback HopperPickupSearchCallback(Vector3i(GetPosX(), GetPosY(), GetPosZ()), m_Contents); + a_Chunk.ForEachEntity(HopperPickupSearchCallback); + + return HopperPickupSearchCallback.FoundPickupsAbove(); } diff --git a/src/BlockEntities/HopperEntity.h b/src/BlockEntities/HopperEntity.h index 2c8b301fe..6ef98f43a 100644 --- a/src/BlockEntities/HopperEntity.h +++ b/src/BlockEntities/HopperEntity.h @@ -10,7 +10,6 @@ #pragma once #include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" @@ -18,8 +17,7 @@ // tolua_begin class cHopperEntity : - public cBlockEntityWithItems, - public cBlockEntityWindowOwner + public cBlockEntityWithItems { typedef cBlockEntityWithItems super; diff --git a/src/BlockEntities/JukeboxEntity.h b/src/BlockEntities/JukeboxEntity.h index 996de965b..734d7bb66 100644 --- a/src/BlockEntities/JukeboxEntity.h +++ b/src/BlockEntities/JukeboxEntity.h @@ -43,6 +43,8 @@ public: void EjectRecord(void); // tolua_end + + static const char * GetClassStatic(void) { return "cJukeboxEntity"; } virtual void UsedBy(cPlayer * a_Player) override; virtual void SendTo(cClientHandle &) override { }; diff --git a/src/BlockEntities/NoteEntity.h b/src/BlockEntities/NoteEntity.h index cf78aeac6..b698899c0 100644 --- a/src/BlockEntities/NoteEntity.h +++ b/src/BlockEntities/NoteEntity.h @@ -54,6 +54,8 @@ public: virtual void UsedBy(cPlayer * a_Player) override; virtual void SendTo(cClientHandle &) override { }; + static const char * GetClassStatic(void) { return "cNoteEntity"; } + private: char m_Pitch; } ; // tolua_export diff --git a/src/BlockEntities/SignEntity.h b/src/BlockEntities/SignEntity.h index d998ff1e8..80c7bbfdf 100644 --- a/src/BlockEntities/SignEntity.h +++ b/src/BlockEntities/SignEntity.h @@ -1,4 +1,3 @@ - // SignEntity.h // Declares the cSignEntity class representing a single sign in the world @@ -56,6 +55,8 @@ public: virtual void UsedBy(cPlayer * a_Player) override; virtual void SendTo(cClientHandle & a_Client) override; + + static const char * GetClassStatic(void) { return "cSignEntity"; } private: diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 1b3ebc3d4..b46bcfd47 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); @@ -1729,7 +1731,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 +1842,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..16ae58f56 --- /dev/null +++ b/src/CompositeChat.cpp @@ -0,0 +1,206 @@ + +// CompositeChat.cpp + +// Implements the cCompositeChat class used to wrap a chat message with multiple parts (text, url, cmd) + +#include "Globals.h" +#include "CompositeChat.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// 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) +{ + // TODO +} + + + + + +void cCompositeChat::SetMessageType(eMessageType a_MessageType) +{ + m_MessageType = a_MessageType; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// 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..e220f6345 --- /dev/null +++ b/src/CompositeChat.h @@ -0,0 +1,172 @@ + +// 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 = ""); + } ; + + 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; } + + // 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; +} ; // 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 286d43cf6..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 ! @@ -1122,8 +1128,9 @@ void cPlayer::SetIP(const AString & a_IP) void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) { - SetPosition( a_PosX, a_PosY, a_PosZ ); + SetPosition(a_PosX, a_PosY, a_PosZ); m_LastGroundHeight = (float)a_PosY; + m_LastJumpHeight = (float)a_PosY; m_World->BroadcastTeleportEntity(*this, GetClientHandle()); m_ClientHandle->SendPlayerMoveLook(); @@ -1757,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; @@ -1774,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..53e4b56db 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; } 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/ItemGrid.cpp b/src/ItemGrid.cpp index e8b58695f..34a267bab 100644 --- a/src/ItemGrid.cpp +++ b/src/ItemGrid.cpp @@ -369,6 +369,13 @@ int cItemGrid::ChangeSlotCount(int a_SlotNum, int a_AddToCount) } m_Slots[a_SlotNum].m_ItemCount += a_AddToCount; + + cItemHandler * Handler = cItemHandler::GetItemHandler(m_Slots[a_SlotNum].m_ItemType); + if (m_Slots[a_SlotNum].m_ItemCount > Handler->GetMaxStackSize()) + { + m_Slots[a_SlotNum].m_ItemCount = Handler->GetMaxStackSize(); + } + TriggerListeners(a_SlotNum); return m_Slots[a_SlotNum].m_ItemCount; } diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp index f2f0c404c..0901f85a9 100644 --- a/src/Mobs/AggressiveMonster.cpp +++ b/src/Mobs/AggressiveMonster.cpp @@ -5,7 +5,7 @@ #include "../World.h" #include "../Entities/Player.h" -#include "../MersenneTwister.h" +#include "../Tracer.h" @@ -73,6 +73,18 @@ void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk) { CheckEventSeePlayer(); } + + if (m_Target == NULL) + return; + + cTracer LineOfSight(GetWorld()); + Vector3d AttackDirection(m_Target->GetPosition() - GetPosition()); + + if (ReachedFinalDestination() && !LineOfSight.Trace(GetPosition(), AttackDirection, (int)AttackDirection.Length())) + { + // Attack if reached destination, target isn't null, and have a clear line of sight to target (so won't attack through walls) + Attack(a_Dt / 1000); + } } @@ -81,7 +93,7 @@ void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk) void cAggressiveMonster::Attack(float a_Dt) { - super::Attack(a_Dt); + m_AttackInterval += a_Dt * m_AttackRate; if ((m_Target != NULL) && (m_AttackInterval > 3.0)) { diff --git a/src/Mobs/AggressiveMonster.h b/src/Mobs/AggressiveMonster.h index 9cee4e7a7..152260f95 100644 --- a/src/Mobs/AggressiveMonster.h +++ b/src/Mobs/AggressiveMonster.h @@ -20,7 +20,7 @@ public: virtual void InStateChasing(float a_Dt) override; virtual void EventSeePlayer(cEntity *) override; - virtual void Attack(float a_Dt) override; + virtual void Attack(float a_Dt); } ; diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index ad3a87725..9817901c9 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -311,9 +311,6 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk) } } - if (ReachedFinalDestination() && (m_Target != NULL)) - Attack(a_Dt); - SetPitchAndYawFromDestination(); HandleFalling(); @@ -657,17 +654,6 @@ void cMonster::InStateEscaping(float a_Dt) -// Do attack here -// a_Dt is passed so we can set attack rate -void cMonster::Attack(float a_Dt) -{ - m_AttackInterval += a_Dt * m_AttackRate; -} - - - - - void cMonster::GetMonsterConfig(const AString & a_Name) { cRoot::Get()->GetMonsterConfig()->AssignAttributes(this, a_Name); diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index 714feddb9..4d2e099c5 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -112,8 +112,6 @@ public: virtual void InStateChasing (float a_Dt); virtual void InStateEscaping(float a_Dt); - virtual void Attack(float a_Dt); - int GetAttackRate() { return (int)m_AttackRate; } void SetAttackRate(float a_AttackRate) { m_AttackRate = a_AttackRate; } void SetAttackRange(int a_AttackRange) { m_AttackRange = a_AttackRange; } 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 5dba69455..985fdee27 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.cpp +++ b/src/Simulator/IncrementalRedstoneSimulator.cpp @@ -8,6 +8,7 @@ #include "../Entities/TNTEntity.h" #include "../Blocks/BlockTorch.h" #include "../Blocks/BlockDoor.h" +#include "../Blocks/BlockFenceGate.h" #include "../Piston.h" @@ -221,6 +222,7 @@ void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int { case E_BLOCK_BLOCK_OF_REDSTONE: HandleRedstoneBlock(a_X, dataitr->y, a_Z); break; case E_BLOCK_LEVER: HandleRedstoneLever(a_X, dataitr->y, a_Z); break; + case E_BLOCK_FENCE_GATE: HandleFenceGate(a_X, dataitr->y, a_Z); break; case E_BLOCK_TNT: HandleTNT(a_X, dataitr->y, a_Z); break; case E_BLOCK_TRAPDOOR: HandleTrapdoor(a_X, dataitr->y, a_Z); break; case E_BLOCK_REDSTONE_WIRE: HandleRedstoneWire(a_X, dataitr->y, a_Z); break; @@ -316,6 +318,22 @@ 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; } @@ -402,6 +420,35 @@ void cIncrementalRedstoneSimulator::HandleRedstoneLever(int a_BlockX, int a_Bloc +void cIncrementalRedstoneSimulator::HandleFenceGate(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + 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)) + { + 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 + { + if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, false)) + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, MetaData & 0xFFFFFFFB); + m_World.BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0); + SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, false); + } + } +} + + + + + void cIncrementalRedstoneSimulator::HandleRedstoneButton(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType) { if (IsButtonOn(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ))) @@ -737,7 +784,7 @@ void cIncrementalRedstoneSimulator::HandleTNT(int a_BlockX, int a_BlockY, int a_ { if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) { - m_World.BroadcastSoundEffect("random.fuse", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); + m_World.BroadcastSoundEffect("game.tnt.primed", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); m_World.SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 4); // 4 seconds to boom m_World.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); } @@ -755,6 +802,7 @@ void cIncrementalRedstoneSimulator::HandleDoor(int a_BlockX, int a_BlockY, int a { cChunkInterface ChunkInterface(m_World.GetChunkMap()); cBlockDoorHandler::ChangeDoor(ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); + m_World.BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0); SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, true); } } @@ -764,6 +812,7 @@ void cIncrementalRedstoneSimulator::HandleDoor(int a_BlockX, int a_BlockY, int a { cChunkInterface ChunkInterface(m_World.GetChunkMap()); cBlockDoorHandler::ChangeDoor(ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); + m_World.BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0); SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, false); } } @@ -836,6 +885,7 @@ void cIncrementalRedstoneSimulator::HandleTrapdoor(int a_BlockX, int a_BlockY, i if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, true)) { m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) | 0x4); + m_World.BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0); SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, true); } } @@ -844,6 +894,7 @@ void cIncrementalRedstoneSimulator::HandleTrapdoor(int a_BlockX, int a_BlockY, i if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, false)) { m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0xB); // Take into account that the fourth bit is needed for trapdoors too + m_World.BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0); SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, false); } } @@ -960,7 +1011,7 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_BlockX, int a_Bloc Vector3f BlockPos(m_X + 0.5f, (float)m_Y, m_Z + 0.5f); float Distance = (EntityPos - BlockPos).Length(); - if (Distance < 0.5) + if (Distance <= 0.7) { m_Entity = a_Entity; return true; // Break out, we only need to know for wooden plates that at least one entity is on top diff --git a/src/Simulator/IncrementalRedstoneSimulator.h b/src/Simulator/IncrementalRedstoneSimulator.h index 3397e143c..bcf89bb82 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.h +++ b/src/Simulator/IncrementalRedstoneSimulator.h @@ -84,6 +84,8 @@ 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 */ @@ -207,6 +209,10 @@ private: case E_BLOCK_REDSTONE_REPEATER_ON: case E_BLOCK_BLOCK_OF_REDSTONE: case E_BLOCK_ACTIVE_COMPARATOR: + case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE: + case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: + case E_BLOCK_STONE_PRESSURE_PLATE: + case E_BLOCK_WOODEN_PRESSURE_PLATE: { return true; } 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 de4fa6ea6..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); @@ -192,7 +192,7 @@ public: void BroadcastEntityStatus (const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL); void BroadcastEntityVelocity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); void BroadcastEntityAnimation (const cEntity & a_Entity, char a_Animation, const cClientHandle * a_Exclude = NULL); - void BroadcastParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount, cClientHandle * a_Exclude = NULL); + void BroadcastParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount, cClientHandle * a_Exclude = NULL); // tolua_export void BroadcastPlayerListItem (const cPlayer & a_Player, bool a_IsOnline, const cClientHandle * a_Exclude = NULL); void BroadcastRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID, const cClientHandle * a_Exclude = NULL); void BroadcastScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode); |