diff options
Diffstat (limited to 'src')
33 files changed, 2087 insertions, 1897 deletions
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 9ebdc4b22..841ec5cf2 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -906,8 +906,12 @@ static int tolua_cWorld_TryGetHeight(lua_State * tolua_S) { int Height = 0; bool res = self->TryGetHeight(BlockX, BlockZ, Height); - tolua_pushnumber(tolua_S, Height); tolua_pushboolean(tolua_S, res ? 1 : 0); + if (res) + { + tolua_pushnumber(tolua_S, Height); + return 2; + } } } return 1; @@ -1106,6 +1110,16 @@ static int tolua_cPluginManager_GetCurrentPlugin(lua_State * S) +static int tolua_cPluginManager_LogStackTrace(lua_State * S) +{ + cLuaState::LogStackTrace(S); + return 0; +} + + + + + static int tolua_cPluginManager_AddHook_FnRef(cPluginManager * a_PluginManager, cLuaState & S, int a_ParamIdx) { // Helper function for cPluginmanager:AddHook() binding @@ -2386,6 +2400,7 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "ForEachConsoleCommand", tolua_cPluginManager_ForEachConsoleCommand); tolua_function(tolua_S, "GetAllPlugins", tolua_cPluginManager_GetAllPlugins); tolua_function(tolua_S, "GetCurrentPlugin", tolua_cPluginManager_GetCurrentPlugin); + tolua_function(tolua_S, "LogStackTrace", tolua_cPluginManager_LogStackTrace); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cPlayer"); diff --git a/src/BlockID.cpp b/src/BlockID.cpp index de56d4625..c38db0bfe 100644 --- a/src/BlockID.cpp +++ b/src/BlockID.cpp @@ -380,7 +380,7 @@ AString DamageTypeToString(eDamageType a_DamageType) case dtRangedAttack: return "dtRangedAttack"; case dtStarving: return "dtStarving"; case dtSuffocating: return "dtSuffocation"; - + case dtExplosion: return "dtExplosion"; } // Unknown damage type: @@ -426,6 +426,7 @@ eDamageType StringToDamageType(const AString & a_DamageTypeString) { dtInVoid, "dtInVoid"}, { dtPotionOfHarming, "dtPotionOfHarming"}, { dtAdmin, "dtAdmin"}, + { dtExplosion, "dtExplosion"}, // Common synonyms: { dtAttack, "dtPawnAttack"}, diff --git a/src/BoundingBox.cpp b/src/BoundingBox.cpp index 86d4546c7..47b135688 100644 --- a/src/BoundingBox.cpp +++ b/src/BoundingBox.cpp @@ -1,4 +1,3 @@ - // BoundingBox.cpp // Implements the cBoundingBox class representing an axis-aligned bounding box with floatingpoint coords @@ -11,7 +10,7 @@ -#if 0 +#if SELF_TEST /// A simple self-test that is executed on program start, used to verify bbox functionality class SelfTest @@ -30,20 +29,39 @@ public: 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}; + for (size_t i = 0; i < ARRAYCOUNT(LineDefs) / 2; i++) { double LineCoeff; - char Face; + eBlockFace Face; Vector3d Line1 = LineDefs[2 * i]; Vector3d Line2 = LineDefs[2 * i + 1]; bool res = cBoundingBox::CalcLineIntersection(Min, Max, Line1, Line2, LineCoeff, Face); - printf("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 - ); + if (res != Results[i]) + { + 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 + ); + abort(); + } + if (res) + { + if (LineCoeff != LineCoeffs[i]) + { + 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 + ); + abort(); + } + } } // for i - LineDefs[] - printf("BoundingBox selftest complete."); + fprintf(stderr,"BoundingBox selftest complete."); } } Test; diff --git a/src/Chunk.h b/src/Chunk.h index 83d69d12c..3dc83b157 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -6,7 +6,7 @@ #include "Simulator/FireSimulator.h" #include "Simulator/SandSimulator.h" -#include "Simulator/RedstoneSimulator.h" +#include "Simulator/IncrementalRedstoneSimulator.h" diff --git a/src/Crypto.cpp b/src/Crypto.cpp index 7a06d7fa3..26500f263 100644 --- a/src/Crypto.cpp +++ b/src/Crypto.cpp @@ -308,8 +308,8 @@ void cPublicKey::InitRnd(void) // cAESCFBDecryptor: cAESCFBDecryptor::cAESCFBDecryptor(void) : - m_IsValid(false), - m_IVOffset(0) + m_IVOffset(0), + m_IsValid(false) { } @@ -366,8 +366,8 @@ void cAESCFBDecryptor::ProcessData(Byte * a_DecryptedOut, const Byte * a_Encrypt // cAESCFBEncryptor: cAESCFBEncryptor::cAESCFBEncryptor(void) : - m_IsValid(false), - m_IVOffset(0) + m_IVOffset(0), + m_IsValid(false) { } diff --git a/src/Crypto.h b/src/Crypto.h index d68f7ec24..a9ec2c6d4 100644 --- a/src/Crypto.h +++ b/src/Crypto.h @@ -132,8 +132,6 @@ protected: class cAESCFBEncryptor { public: - Byte test; - cAESCFBEncryptor(void); ~cAESCFBEncryptor(); diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 08780ca8b..8554ab2a5 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -50,6 +50,8 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d , m_TicksSinceLastFireDamage(0) , m_TicksLeftBurning(0) , m_TicksSinceLastVoidDamage(0) + , m_IsSwimming(false) + , m_IsSubmerged(false) , m_HeadYaw( 0.0 ) , m_Rot(0.0, 0.0, 0.0) , m_Pos(a_X, a_Y, a_Z) @@ -57,8 +59,6 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d , m_Mass (0.001) // Default 1g , m_Width(a_Width) , m_Height(a_Height) - , m_IsSubmerged(false) - , m_IsSwimming(false) { cCSLock Lock(m_CSCount); m_EntityCount++; diff --git a/src/Entities/Floater.cpp b/src/Entities/Floater.cpp index 38160a30e..b910c3769 100644 --- a/src/Entities/Floater.cpp +++ b/src/Entities/Floater.cpp @@ -103,10 +103,10 @@ protected: cFloater::cFloater(double a_X, double a_Y, double a_Z, Vector3d a_Speed, int a_PlayerID, int a_CountDownTime) : cEntity(etFloater, a_X, a_Y, a_Z, 0.2, 0.2), - m_PickupCountDown(0), - m_PlayerID(a_PlayerID), m_CanPickupItem(false), + m_PickupCountDown(0), m_CountDownTime(a_CountDownTime), + m_PlayerID(a_PlayerID), m_AttachedMobID(-1) { SetSpeed(a_Speed); diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp index a650927b1..d854906b7 100644 --- a/src/Entities/Minecart.cpp +++ b/src/Entities/Minecart.cpp @@ -24,11 +24,11 @@ class cMinecartCollisionCallback : { public: cMinecartCollisionCallback(Vector3d a_Pos, double a_Height, double a_Width, int a_UniqueID, int a_AttacheeUniqueID) : + m_DoesInteserct(false), + m_CollidedEntityPos(0, 0, 0), m_Pos(a_Pos), m_Height(a_Height), m_Width(a_Width), - m_DoesInteserct(false), - m_CollidedEntityPos(0, 0, 0), m_UniqueID(a_UniqueID), m_AttacheeUniqueID(a_AttacheeUniqueID) { @@ -1057,8 +1057,8 @@ void cMinecartWithChest::OnRightClicked(cPlayer & a_Player) cMinecartWithFurnace::cMinecartWithFurnace(double a_X, double a_Y, double a_Z) : super(mpFurnace, a_X, a_Y, a_Z), - m_IsFueled(false), - m_FueledTimeLeft(-1) + m_FueledTimeLeft(-1), + m_IsFueled(false) { } @@ -1137,4 +1137,4 @@ cMinecartWithHopper::cMinecartWithHopper(double a_X, double a_Y, double a_Z) : } // TODO: Make it suck up blocks and travel further than any other cart and physics and put and take blocks -// AND AVARYTHING!!
\ No newline at end of file +// AND AVARYTHING!! diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp index bfe162b69..c5503c16a 100644 --- a/src/Entities/Pickup.cpp +++ b/src/Entities/Pickup.cpp @@ -22,9 +22,9 @@ class cPickupCombiningCallback : { public: cPickupCombiningCallback(Vector3d a_Position, cPickup * a_Pickup) : + m_FoundMatchingPickup(false), m_Position(a_Position), - m_Pickup(a_Pickup), - m_FoundMatchingPickup(false) + m_Pickup(a_Pickup) { } diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index eef6b8e69..286d43cf6 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -1261,19 +1261,6 @@ void cPlayer::RemoveFromGroup( const AString & a_GroupName ) -bool cPlayer::CanUseCommand( const AString & a_Command ) -{ - for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr ) - { - if( (*itr)->HasCommand( a_Command ) ) return true; - } - return false; -} - - - - - bool cPlayer::HasPermission(const AString & a_Permission) { if (a_Permission.empty()) diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 869e67775..7db9544cb 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -218,7 +218,6 @@ public: /// Removes a player from the group, resolves permissions and group inheritance (case sensitive) void RemoveFromGroup( const AString & a_GroupName ); // tolua_export - bool CanUseCommand( const AString & a_Command ); // tolua_export bool HasPermission( const AString & a_Permission ); // tolua_export const GroupList & GetGroups() { return m_Groups; } // >> EXPORTED IN MANUALBINDINGS << StringList GetResolvedPermissions(); // >> EXPORTED IN MANUALBINDINGS << diff --git a/src/Group.cpp b/src/Group.cpp index 448d29d87..5f1f25782 100644 --- a/src/Group.cpp +++ b/src/Group.cpp @@ -3,35 +3,39 @@ #include "Group.h" -void cGroup::AddCommand( std::string a_Command ) + + + + +void cGroup::AddCommand( AString a_Command ) { m_Commands[ a_Command ] = true; } -void cGroup::AddPermission( std::string a_Permission ) + + + + +void cGroup::AddPermission( AString a_Permission ) { m_Permissions[ a_Permission ] = true; } -bool cGroup::HasCommand( std::string a_Command ) -{ - if( m_Commands.find("*") != m_Commands.end() ) return true; - - CommandMap::iterator itr = m_Commands.find( a_Command ); - if( itr != m_Commands.end() ) - { - if( itr->second ) return true; - } - - for( GroupList::iterator itr = m_Inherits.begin(); itr != m_Inherits.end(); ++itr ) - { - if( (*itr)->HasCommand( a_Command ) ) return true; - } - return false; -} + + + void cGroup::InheritFrom( cGroup* a_Group ) { m_Inherits.remove( a_Group ); m_Inherits.push_back( a_Group ); +} + + + + + +void cGroup::ClearPermission() +{ + m_Permissions.clear(); }
\ No newline at end of file diff --git a/src/Group.h b/src/Group.h index 65ee1a60a..8bee6f7ed 100644 --- a/src/Group.h +++ b/src/Group.h @@ -11,19 +11,19 @@ public: // tolua_export cGroup() {} ~cGroup() {} - void SetName( std::string a_Name ) { m_Name = a_Name; } // tolua_export - const std::string & GetName() const { return m_Name; } // tolua_export - void SetColor( std::string a_Color ) { m_Color = a_Color; } // tolua_export - void AddCommand( std::string a_Command ); // tolua_export - void AddPermission( std::string a_Permission ); // tolua_export - void InheritFrom( cGroup* a_Group ); // tolua_export - - bool HasCommand( std::string a_Command ); // tolua_export - - typedef std::map< std::string, bool > PermissionMap; + void SetName( AString a_Name ) { m_Name = a_Name; } // tolua_export + const AString & GetName() const { return m_Name; } // tolua_export + void SetColor( AString a_Color ) { m_Color = a_Color; } // tolua_export + void AddCommand( AString a_Command ); // tolua_export + void AddPermission( AString a_Permission ); // tolua_export + void InheritFrom( cGroup* a_Group ); // tolua_export + + typedef std::map< AString, bool > PermissionMap; const PermissionMap & GetPermissions() const { return m_Permissions; } - typedef std::map< std::string, bool > CommandMap; + void ClearPermission(void); + + typedef std::map< AString, bool > CommandMap; const CommandMap & GetCommands() const { return m_Commands; } const AString & GetColor() const { return m_Color; } // tolua_export @@ -31,8 +31,8 @@ public: // tolua_export typedef std::list< cGroup* > GroupList; const GroupList & GetInherits() const { return m_Inherits; } private: - std::string m_Name; - std::string m_Color; + AString m_Name; + AString m_Color; PermissionMap m_Permissions; CommandMap m_Commands; diff --git a/src/GroupManager.cpp b/src/GroupManager.cpp index d5567d91e..723b86f94 100644 --- a/src/GroupManager.cpp +++ b/src/GroupManager.cpp @@ -44,6 +44,18 @@ cGroupManager::cGroupManager() : m_pState( new sGroupManagerState ) { LOGD("-- Loading Groups --"); + + LoadGroups(); + + LOGD("-- Groups Successfully Loaded --"); +} + + + + + +void cGroupManager::LoadGroups() +{ cIniFile IniFile; if (!IniFile.ReadFile("groups.ini")) { @@ -71,8 +83,10 @@ cGroupManager::cGroupManager() unsigned int NumKeys = IniFile.GetNumKeys(); for (size_t i = 0; i < NumKeys; i++) { - std::string KeyName = IniFile.GetKeyName( i ); + AString KeyName = IniFile.GetKeyName( i ); cGroup* Group = GetGroup( KeyName.c_str() ); + + Group->ClearPermission(); // Needed in case the groups are reloaded. LOGD("Loading group: %s", KeyName.c_str() ); @@ -107,7 +121,7 @@ cGroupManager::cGroupManager() } } - std::string Groups = IniFile.GetValue(KeyName, "Inherits", ""); + AString Groups = IniFile.GetValue(KeyName, "Inherits", ""); if (!Groups.empty()) { AStringVector Split = StringSplitAndTrim(Groups, ","); @@ -117,7 +131,6 @@ cGroupManager::cGroupManager() } } } - LOGD("-- Groups Successfully Loaded --"); } diff --git a/src/GroupManager.h b/src/GroupManager.h index d911f976c..02a58fe4e 100644 --- a/src/GroupManager.h +++ b/src/GroupManager.h @@ -15,6 +15,7 @@ class cGroupManager { public: cGroup * GetGroup(const AString & a_Name); + void LoadGroups(void); private: friend class cRoot; diff --git a/src/Item.h b/src/Item.h index ca9ec5a2b..4782f31c1 100644 --- a/src/Item.h +++ b/src/Item.h @@ -55,9 +55,9 @@ public: m_ItemType (a_ItemType), m_ItemCount (a_ItemCount), m_ItemDamage (a_ItemDamage), - m_Enchantments(a_Enchantments), m_CustomName (a_CustomName), - m_Lore (a_Lore) + m_Lore (a_Lore), + m_Enchantments(a_Enchantments) { if (!IsValidItem(m_ItemType)) { @@ -75,9 +75,9 @@ public: m_ItemType (a_CopyFrom.m_ItemType), m_ItemCount (a_CopyFrom.m_ItemCount), m_ItemDamage (a_CopyFrom.m_ItemDamage), - m_Enchantments(a_CopyFrom.m_Enchantments), m_CustomName (a_CopyFrom.m_CustomName), - m_Lore (a_CopyFrom.m_Lore) + m_Lore (a_CopyFrom.m_Lore), + m_Enchantments(a_CopyFrom.m_Enchantments) { } diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 86ff522d8..ad3a87725 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -69,20 +69,20 @@ cMonster::cMonster(const AString & a_ConfigName, eType a_MobType, const AString : super(etMonster, a_Width, a_Height) , m_EMState(IDLE) , m_EMPersonality(AGGRESSIVE) - , m_SightDistance(25) , m_Target(NULL) - , m_AttackRate(3) - , m_IdleInterval(0) , m_bMovingToDestination(false) + , m_LastGroundHeight(POSY_TOINT) + , m_IdleInterval(0) , m_DestroyTimer(0) , m_MobType(a_MobType) , m_SoundHurt(a_SoundHurt) , m_SoundDeath(a_SoundDeath) + , m_AttackRate(3) , m_AttackDamage(1) , m_AttackRange(2) , m_AttackInterval(0) + , m_SightDistance(25) , m_BurnsInDaylight(false) - , m_LastGroundHeight(POSY_TOINT) { if (!a_ConfigName.empty()) { diff --git a/src/Mobs/Villager.cpp b/src/Mobs/Villager.cpp index 08e5e4315..09a6e2d09 100644 --- a/src/Mobs/Villager.cpp +++ b/src/Mobs/Villager.cpp @@ -13,9 +13,9 @@ cVillager::cVillager(eVillagerType VillagerType) : super("Villager", mtVillager, "", "", 0.6, 1.8), + m_ActionCountDown(-1), m_Type(VillagerType), - m_VillagerAction(false), - m_ActionCountDown(-1) + m_VillagerAction(false) { } diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 262ee4c0e..7eaf106cf 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -99,7 +99,7 @@ void cProtocol172::DataReceived(const char * a_Data, int a_Size) Byte Decrypted[512]; while (a_Size > 0) { - int NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size; + size_t NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size; m_Decryptor.ProcessData(Decrypted, (Byte *)a_Data, NumBytes); AddReceivedData((const char *)Decrypted, NumBytes); a_Size -= NumBytes; @@ -1836,7 +1836,7 @@ void cProtocol172::SendData(const char * a_Data, int a_Size) Byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks) while (a_Size > 0) { - int NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size; + size_t NumBytes = ((size_t)a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : (size_t)a_Size; m_Encryptor.ProcessData(Encrypted, (Byte *)a_Data, NumBytes); m_Client->SendData((const char *)Encrypted, NumBytes); a_Size -= NumBytes; diff --git a/src/Root.cpp b/src/Root.cpp index 3e1898c42..749fbd288 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -132,7 +132,7 @@ void cRoot::Start(void) LOGWARN("Regenerating settings.ini, all settings will be reset"); IniFile.AddHeaderComment(" This is the main server configuration"); IniFile.AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini"); - IniFile.AddHeaderComment(" See: http://www.mc-server.org/wiki/doku.php?id=configure:settings.ini for further configuration help"); + IniFile.AddHeaderComment(" See: http://wiki.mc-server.org/doku.php?id=configure:settings.ini for further configuration help"); } m_PrimaryServerVersion = IniFile.GetValueI("Server", "PrimaryServerVersion", 0); @@ -219,16 +219,14 @@ void cRoot::Start(void) delete m_InputThread; m_InputThread = NULL; #endif - // Deallocate stuffs + // Stop the server: + m_WebAdmin->Stop(); LOG("Shutting down server..."); m_Server->Shutdown(); - LOGD("Shutting down deadlock detector..."); dd.Stop(); - LOGD("Stopping world threads..."); StopWorlds(); - LOGD("Stopping authenticator..."); m_Authenticator.Stop(); @@ -536,6 +534,15 @@ void cRoot::SaveAllChunks(void) +void cRoot::ReloadGroups(void) +{ + m_GroupManager->LoadGroups(); +} + + + + + void cRoot::LoopWorldsAndBroadcastChat(const AString & a_Message, ChatPrefixCodes a_ChatPrefix) { for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr) diff --git a/src/Root.h b/src/Root.h index 4cb77a79d..13e208b8d 100644 --- a/src/Root.h +++ b/src/Root.h @@ -99,6 +99,9 @@ public: /// Saves all chunks in all worlds void SaveAllChunks(void); // tolua_export + /// Reloads all the groups + void ReloadGroups(void); // tolua_export + /// Calls the callback for each player in all worlds bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << diff --git a/src/Scoreboard.cpp b/src/Scoreboard.cpp index b2edd613b..61ecac5b7 100644 --- a/src/Scoreboard.cpp +++ b/src/Scoreboard.cpp @@ -197,8 +197,8 @@ cTeam::cTeam(const AString & a_Name, const AString & a_DisplayName, const AString & a_Prefix, const AString & a_Suffix) : m_AllowsFriendlyFire(true) , m_CanSeeFriendlyInvisible(false) - , m_Name(a_Name) , m_DisplayName(a_DisplayName) + , m_Name(a_Name) , m_Prefix(a_Prefix) , m_Suffix(a_Suffix) {} diff --git a/src/Server.cpp b/src/Server.cpp index ba2b46d55..ab1458da4 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -459,6 +459,17 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac if (split[0] == "reload") { cPluginManager::Get()->ReloadPlugins(); + cRoot::Get()->ReloadGroups(); + return; + } + if (split[0] == "reloadplugins") + { + cPluginManager::Get()->ReloadPlugins(); + return; + } + if (split[0] == "reloadgroups") + { + cRoot::Get()->ReloadGroups(); return; } diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp new file mode 100644 index 000000000..5dba69455 --- /dev/null +++ b/src/Simulator/IncrementalRedstoneSimulator.cpp @@ -0,0 +1,1534 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "IncrementalRedstoneSimulator.h" +#include "../BlockEntities/DropSpenserEntity.h" +#include "../BlockEntities/NoteEntity.h" +#include "../BlockEntities/CommandBlockEntity.h" +#include "../Entities/TNTEntity.h" +#include "../Blocks/BlockTorch.h" +#include "../Blocks/BlockDoor.h" +#include "../Piston.h" + + + + + +cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulator(cWorld & a_World) + : super(a_World) +{ +} + + + + + +cIncrementalRedstoneSimulator::~cIncrementalRedstoneSimulator() +{ +} + + + + + +void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) +{ + if ((a_Chunk == NULL) || !a_Chunk->IsValid()) + { + return; + } + else if ((a_BlockY < 0) || (a_BlockY > cChunkDef::Height)) + { + return; + } + + int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width; + int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width; + + BLOCKTYPE Block; + NIBBLETYPE Meta; + 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) + { + if (!itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) + { + continue; + } + + 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); + break; + } + else if ( + // Changeable sources + ((Block == E_BLOCK_REDSTONE_WIRE) && (Meta == 0)) || + ((Block == E_BLOCK_LEVER) && !IsLeverOn(Meta)) || + ((Block == E_BLOCK_DETECTOR_RAIL) && (Meta & 0x08) == 0) || + (((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta))) || + (((Block == E_BLOCK_STONE_PRESSURE_PLATE) || (Block == E_BLOCK_WOODEN_PRESSURE_PLATE)) && (Meta == 0)) + ) + { + 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); + break; + } + else if (Block == E_BLOCK_DAYLIGHT_SENSOR) + { + if (!a_Chunk->IsLightValid()) + { + m_World.QueueLightChunk(a_Chunk->GetPosX(), a_Chunk->GetPosZ()); + break; + } + else + { + NIBBLETYPE SkyLight; + a_Chunk->UnboundedRelGetBlockSkyLight(RelX, itr->a_SourcePos.y + 1, RelZ, SkyLight); + + 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); + break; + } + } + } + } + + for (LinkedBlocksList::iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr) + { + 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; + } + else if ( + // Things that can send power through a block but which depends on meta + ((Block == E_BLOCK_REDSTONE_WIRE) && (Meta == 0)) || + ((Block == E_BLOCK_LEVER) && !IsLeverOn(Meta)) || + (((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta))) + ) + { + 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; + } + } + else if (itr->a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) + { + 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; + } + } + } + + for (SimulatedPlayerToggleableList::iterator itr = m_SimulatedPlayerToggleableBlocks.begin(); itr != m_SimulatedPlayerToggleableBlocks.end(); ++itr) + { + if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) + { + continue; + } + + 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); + break; + } + } + + for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end(); ++itr) + { + if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) + { + continue; + } + + if ((Block != E_BLOCK_REDSTONE_REPEATER_ON) && (Block != E_BLOCK_REDSTONE_REPEATER_OFF)) + { + m_RepeatersDelayList.erase(itr); + break; + } + } + + cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData(); + for (cRedstoneSimulatorChunkData::iterator itr = ChunkData.begin(); itr != ChunkData.end(); ++itr) + { + if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ)) // We are at an entry matching the current (changed) block + { + if (!IsAllowedBlock(Block)) + { + itr->DataTwo = true; // The new blocktype is not redstone; it must be queued to be removed from this list + } + else + { + itr->Data = Block; // Update block information + } + return; + } + } + + if (!IsAllowedBlock(Block)) + { + return; + } + + ChunkData.push_back(cCoordWithBlockAndBool(RelX, a_BlockY, RelZ, Block, false)); +} + + + + + +void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) +{ + // We still attempt to simulate all blocks in the chunk every tick, because of outside influence that needs to be taken into account + // For example, repeaters need to be ticked, pressure plates checked for entities, daylight sensor checked for light changes, etc. + // 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()) + { + return; + } + + int BaseX = a_Chunk->GetPosX() * cChunkDef::Width; + int BaseZ = a_Chunk->GetPosZ() * cChunkDef::Width; + + for (cRedstoneSimulatorChunkData::iterator dataitr = ChunkData.begin(); dataitr != ChunkData.end();) + { + if (dataitr->DataTwo) + { + dataitr = ChunkData.erase(dataitr); + continue; + } + + int a_X = BaseX + dataitr->x; + int a_Z = BaseZ + dataitr->z; + switch (dataitr->Data) + { + 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_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; + case E_BLOCK_NOTE_BLOCK: HandleNoteBlock(a_X, dataitr->y, a_Z); break; + case E_BLOCK_DAYLIGHT_SENSOR: HandleDaylightSensor(a_X, dataitr->y, a_Z); break; + case E_BLOCK_COMMAND_BLOCK: HandleCommandBlock(a_X, dataitr->y, a_Z); break; + + case E_BLOCK_REDSTONE_TORCH_OFF: + case E_BLOCK_REDSTONE_TORCH_ON: + { + HandleRedstoneTorch(a_X, dataitr->y, a_Z, dataitr->Data); + break; + } + case E_BLOCK_STONE_BUTTON: + case E_BLOCK_WOODEN_BUTTON: + { + HandleRedstoneButton(a_X, dataitr->y, a_Z, dataitr->Data); + break; + } + case E_BLOCK_REDSTONE_REPEATER_OFF: + case E_BLOCK_REDSTONE_REPEATER_ON: + { + HandleRedstoneRepeater(a_X, dataitr->y, a_Z, dataitr->Data); + break; + } + case E_BLOCK_PISTON: + case E_BLOCK_STICKY_PISTON: + { + HandlePiston(a_X, dataitr->y, a_Z); + break; + } + case E_BLOCK_REDSTONE_LAMP_OFF: + case E_BLOCK_REDSTONE_LAMP_ON: + { + HandleRedstoneLamp(a_X, dataitr->y, a_Z, dataitr->Data); + break; + } + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: + { + HandleDropSpenser(a_X, dataitr->y, a_Z); + break; + } + case E_BLOCK_WOODEN_DOOR: + case E_BLOCK_IRON_DOOR: + { + HandleDoor(a_X, dataitr->y, a_Z); + break; + } + case E_BLOCK_ACTIVATOR_RAIL: + case E_BLOCK_DETECTOR_RAIL: + case E_BLOCK_POWERED_RAIL: + { + HandleRail(a_X, dataitr->y, a_Z, dataitr->Data); + break; + } + case E_BLOCK_WOODEN_PRESSURE_PLATE: + case E_BLOCK_STONE_PRESSURE_PLATE: + { + HandlePressurePlate(a_X, dataitr->y, a_Z, dataitr->Data); + break; + } + } + ++dataitr; + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleRedstoneTorch(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState) +{ + static const struct // Define which directions the torch can power + { + int x, y, z; + } gCrossCoords[] = + { + { 1, 0, 0}, + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, + { 0, 1, 0}, + } ; + + if (a_MyState == E_BLOCK_REDSTONE_TORCH_ON) + { + // Check if the block the torch is on is powered + int X = a_BlockX; int Y = a_BlockY; int Z = a_BlockZ; + AddFaceDirection(X, Y, Z, cBlockTorchHandler::MetaDataToDirection(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ)), true); // Inverse true to get the block torch is on + + if (AreCoordsDirectlyPowered(X, Y, Z)) + { + // 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)); + return; + } + + // Torch still on, make all 4(X, Z) + 1(Y) sides powered + for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) + { + BLOCKTYPE Type = m_World.GetBlock(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z); + if (i + 1 < ARRAYCOUNT(gCrossCoords)) // Sides of torch, not top (top is last) + { + if ( + ((IsMechanism(Type)) || (Type == E_BLOCK_REDSTONE_WIRE)) && // Is it a mechanism or wire? Not block/other torch etc. + (!Vector3i(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z).Equals(Vector3i(X, Y, Z))) // CAN'T power block is that it is on + ) + { + SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_ON); + } + } + else + { + // Top side, power whatever is there, including blocks + SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_ON); + // Power all blocks surrounding block above torch + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YP, E_BLOCK_REDSTONE_TORCH_ON); + } + } + + if (m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) != 0x5) // Is torch standing on ground? If NOT (i.e. on wall), power block beneath + { + BLOCKTYPE Type = m_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); + + if ((IsMechanism(Type)) || (Type == E_BLOCK_REDSTONE_WIRE)) // Still can't make a normal block powered though! + { + SetBlockPowered(a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_ON); + } + } + } + else + { + // Check if the block the torch is on is powered + int X = a_BlockX; int Y = a_BlockY; int Z = a_BlockZ; + AddFaceDirection(X, Y, Z, cBlockTorchHandler::MetaDataToDirection(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ)), true); // Inverse true to get the block torch is on + + // See if off state torch can be turned on again + if (AreCoordsDirectlyPowered(X, Y, Z)) + { + return; // Something matches, torch still powered + } + + // Block torch on not powered, can be turned on again! + m_World.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_ON, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ)); + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleRedstoneBlock(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BLOCK_OF_REDSTONE); + SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BLOCK_OF_REDSTONE); // Set self as powered +} + + + + + +void cIncrementalRedstoneSimulator::HandleRedstoneLever(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + if (IsLeverOn(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ))) + { + SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_LEVER); + + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, E_BLOCK_LEVER); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, E_BLOCK_LEVER); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YM, E_BLOCK_LEVER); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YP, E_BLOCK_LEVER); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZM, E_BLOCK_LEVER); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, E_BLOCK_LEVER); + } +} + + + + + +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))) + { + SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, a_BlockType); + + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, a_BlockType); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, a_BlockType); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YM, a_BlockType); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YP, a_BlockType); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZM, a_BlockType); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, a_BlockType); + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleRedstoneWire(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + static const struct // Define which directions the wire can receive power from + { + int x, y, z; + } gCrossCoords[] = + { + { 1, 0, 0}, /* Wires on same level start */ + {-1, 0, 0}, + { 0, 0, 1}, + { 0, 0, -1}, /* Wires on same level stop */ + { 1, 1, 0}, /* Wires one higher, surrounding self start */ + {-1, 1, 0}, + { 0, 1, 1}, + { 0, 1, -1}, /* Wires one higher, surrounding self stop */ + { 1,-1, 0}, /* Wires one lower, surrounding self start */ + {-1,-1, 0}, + { 0,-1, 1}, + { 0,-1, -1}, /* Wires one lower, surrounding self stop */ + } ; + + static const struct // Define which directions the wire will check for repeater prescence + { + int x, y, z; + } gSideCoords[] = + { + { 1, 0, 0 }, + {-1, 0, 0 }, + { 0, 0, 1 }, + { 0, 0,-1 }, + { 0, 1, 0 }, + }; + + // Check to see if directly beside a power source + if (IsWirePowered(a_BlockX, a_BlockY, a_BlockZ)) + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 15); // Maximum power + } + else + { + NIBBLETYPE MetaToSet = 0; + NIBBLETYPE MyMeta = m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + int TimesMetaSmaller = 0, TimesFoundAWire = 0; + + for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) // Loop through all directions to transfer or receive power + { + if ((i >= 4) && (i <= 7)) // If we are currently checking for wire surrounding ourself one block above... + { + if (g_BlockIsSolid[m_World.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)]) // If there is something solid above us (wire cut off)... + { + continue; // We don't receive power from that wire + } + } + else if ((i >= 8) && (i <= 11)) // See above, but this is for wire below us + { + if (g_BlockIsSolid[m_World.GetBlock(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y + 1, a_BlockZ + gCrossCoords[i].z)]) + { + continue; + } + } + + BLOCKTYPE SurroundType; + NIBBLETYPE SurroundMeta; + m_World.GetBlockTypeMeta(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, SurroundType, SurroundMeta); + + if (SurroundType == E_BLOCK_REDSTONE_WIRE) + { + TimesFoundAWire++; + + if (SurroundMeta > 1) // Wires of power 1 or 0 cannot transfer power TO ME, don't bother checking + { + // Does surrounding wire have a higher power level than self? + // >= to fix a bug where wires bordering each other with the same power level will appear (in terms of meta) to power each other, when they aren't actually in the powered list + if (SurroundMeta >= MyMeta) + { + MetaToSet = SurroundMeta - 1; // To improve performance + } + } + + if (SurroundMeta < MyMeta) // Go through all surroundings to see if self power is larger than everyone else's + { + TimesMetaSmaller++; + } + } + } + + if (TimesMetaSmaller == TimesFoundAWire) + { + // All surrounding metas were smaller - self must have been a wire that was + // transferring power to other wires around. + // However, self not directly powered anymore, so source must have been removed, + // therefore, self must be set to meta zero + 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 + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, MetaToSet); + } + } + + if (m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) != 0) // A powered wire + { + for (size_t i = 0; i < ARRAYCOUNT(gSideCoords); i++) // Look for repeaters immediately surrounding self and try to power them + { + if (m_World.GetBlock(a_BlockX + gSideCoords[i].x, a_BlockY + gSideCoords[i].y, a_BlockZ + gSideCoords[i].z) == E_BLOCK_REDSTONE_REPEATER_OFF) + { + SetBlockPowered(a_BlockX + gSideCoords[i].x, a_BlockY + gSideCoords[i].y, a_BlockZ + gSideCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); + } + } + + // Wire still powered, power blocks beneath + SetBlockPowered(a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YM, E_BLOCK_REDSTONE_WIRE); + + switch (GetWireDirection(a_BlockX, a_BlockY, a_BlockZ)) + { + case REDSTONE_NONE: + { + SetAllDirsAsPowered(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; + } + case REDSTONE_X_POS: + { + SetBlockPowered(a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, E_BLOCK_REDSTONE_WIRE); + break; + } + case REDSTONE_X_NEG: + { + SetBlockPowered(a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, E_BLOCK_REDSTONE_WIRE); + break; + } + case REDSTONE_Z_POS: + { + 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_ZP, E_BLOCK_REDSTONE_WIRE); + break; + } + case REDSTONE_Z_NEG: + { + 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_ZM, E_BLOCK_REDSTONE_WIRE); + break; + } + } + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleRedstoneRepeater(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState) +{ + NIBBLETYPE a_Meta = m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + + bool IsOn = ((a_MyState == E_BLOCK_REDSTONE_REPEATER_ON) ? true : false); // Cache if repeater is on + bool IsSelfPowered = IsRepeaterPowered(a_BlockX, a_BlockY, a_BlockZ, a_Meta & 0x3); // Cache if repeater is pwoered + + 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); + } + 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); + } + + for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end(); ++itr) + { + if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) + { + continue; + } + + if (itr->a_ElapsedTicks >= itr->a_DelayTicks) // Has the elapsed ticks reached the target ticks? + { + if (itr->ShouldPowerOn) + { + if (!IsOn) + { + m_World.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON, a_Meta); // For performance + } + + switch (a_Meta & 0x3) // We only want the direction (bottom) bits + { + case 0x0: + { + SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZM, E_BLOCK_REDSTONE_REPEATER_ON); + break; + } + case 0x1: + { + SetBlockPowered(a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, E_BLOCK_REDSTONE_REPEATER_ON); + break; + } + case 0x2: + { + SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, E_BLOCK_REDSTONE_REPEATER_ON); + break; + } + case 0x3: + { + SetBlockPowered(a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON); + SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, E_BLOCK_REDSTONE_REPEATER_ON); + break; + } + } + + // Removal of the data entry will be handled in SimChunk - we still want to continue trying to power blocks, even if our delay time has reached + // Otherwise, the power state of blocks in front won't update after we have powered on + return; + } + else + { + if (IsOn) + { + 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 + return; + } + } + else + { + // Apparently, incrementing ticks only works reliably here, and not in SimChunk; + // With a world with lots of redstone, the repeaters simply do not delay + // I am confounded to say why. Perhaps optimisation failure. + LOGD("Incremented a repeater @ {%i %i %i} | Elapsed ticks: %i | Target delay: %i", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z, itr->a_ElapsedTicks, itr->a_DelayTicks); + itr->a_ElapsedTicks++; + } + } +} + + + + + +void cIncrementalRedstoneSimulator::HandlePiston(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cPiston Piston(&m_World); + if (IsPistonPowered(a_BlockX, a_BlockY, a_BlockZ, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x7)) // We only want the bottom three bits (4th controls extended-ness) + { + Piston.ExtendPiston(a_BlockX, a_BlockY, a_BlockZ); + } + else + { + Piston.RetractPiston(a_BlockX, a_BlockY, a_BlockZ); + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleDropSpenser(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + class cSetPowerToDropSpenser : + public cDropSpenserCallback + { + bool m_IsPowered; + public: + cSetPowerToDropSpenser(bool a_IsPowered) : m_IsPowered(a_IsPowered) {} + + virtual bool Item(cDropSpenserEntity * a_DropSpenser) override + { + a_DropSpenser->SetRedstonePower(m_IsPowered); + return false; + } + } DrSpSP (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)); + + m_World.DoWithDropSpenserAt(a_BlockX, a_BlockY, a_BlockZ, DrSpSP); +} + + + + + +void cIncrementalRedstoneSimulator::HandleRedstoneLamp(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState) +{ + if (a_MyState == E_BLOCK_REDSTONE_LAMP_OFF) + { + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) + { + m_World.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_LAMP_ON, 0); + } + } + else + { + if (!AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) + { + m_World.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_LAMP_OFF, 0); + } + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleTNT(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + 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.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); + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleDoor(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) + { + if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, true)) + { + cChunkInterface ChunkInterface(m_World.GetChunkMap()); + cBlockDoorHandler::ChangeDoor(ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); + SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, true); + } + } + else + { + if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, false)) + { + cChunkInterface ChunkInterface(m_World.GetChunkMap()); + cBlockDoorHandler::ChangeDoor(ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); + SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, false); + } + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleCommandBlock(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + class cSetPowerToCommandBlock : + public cCommandBlockCallback + { + bool m_IsPowered; + public: + cSetPowerToCommandBlock(bool a_IsPowered) : m_IsPowered(a_IsPowered) {} + + virtual bool Item(cCommandBlockEntity * a_CommandBlock) override + { + a_CommandBlock->SetRedstonePower(m_IsPowered); + return false; + } + } CmdBlockSP (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)); + + m_World.DoWithCommandBlockAt(a_BlockX, a_BlockY, a_BlockZ, CmdBlockSP); +} + + + + + +void cIncrementalRedstoneSimulator::HandleRail(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType) +{ + switch (a_MyType) + { + case E_BLOCK_DETECTOR_RAIL: + { + if ((m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x08) == 0x08) + { + SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, a_MyType); + } + break; + } + case E_BLOCK_ACTIVATOR_RAIL: + case E_BLOCK_POWERED_RAIL: + { + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) | 0x08); + } + else + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x07); + } + break; + } + default: LOGD("Unhandled type of rail in %s", __FUNCTION__); + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleTrapdoor(int a_BlockX, int a_BlockY, int 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, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) | 0x4); + 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, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0xB); // Take into account that the fourth bit is needed for trapdoors too + SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, false); + } + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleNoteBlock(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + bool m_bAreCoordsPowered = AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ); + + if (m_bAreCoordsPowered) + { + if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, true)) + { + class cSetPowerToNoteBlock : + public cNoteBlockCallback + { + bool m_IsPowered; + public: + cSetPowerToNoteBlock(bool a_IsPowered) : m_IsPowered(a_IsPowered) {} + + virtual bool Item(cNoteEntity * a_NoteBlock) override + { + if (m_IsPowered) + { + a_NoteBlock->MakeSound(); + } + return false; + } + } NoteBlockSP(m_bAreCoordsPowered); + + m_World.DoWithNoteBlockAt(a_BlockX, a_BlockY, a_BlockZ, NoteBlockSP); + SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, true); + } + } + else + { + if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, false)) + { + SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, false); + } + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleDaylightSensor(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + int a_ChunkX, a_ChunkZ; + cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, a_ChunkX, a_ChunkZ); + + if (!m_World.IsChunkLighted(a_ChunkX, a_ChunkZ)) + { + m_World.QueueLightChunk(a_ChunkX, a_ChunkZ); + } + else + { + NIBBLETYPE SkyLight = m_World.GetBlockSkyLight(a_BlockX, a_BlockY + 1, a_BlockZ) - m_World.GetSkyDarkness(); + if (SkyLight > 8) + { + SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DAYLIGHT_SENSOR); + } + } +} + + + + + +void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType) +{ + switch (a_MyType) + { + case E_BLOCK_STONE_PRESSURE_PLATE: + { + // MCS feature - stone pressure plates can only be triggered by players :D + cPlayer * a_Player = m_World.FindClosestPlayer(Vector3f(a_BlockX + 0.5f, (float)a_BlockY, a_BlockZ + 0.5f), 0.5f, false); + + if (a_Player != NULL) + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0x1); + SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_STONE_PRESSURE_PLATE); + } + else + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0x0); + m_World.WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ); + } + break; + } + case E_BLOCK_WOODEN_PRESSURE_PLATE: + { + class cWoodenPressurePlateCallback : + public cEntityCallback + { + public: + cWoodenPressurePlateCallback(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : + m_Entity(NULL), + m_World(a_World), + m_X(a_BlockX), + m_Y(a_BlockY), + m_Z(a_BlockZ) + { + } + + virtual bool Item(cEntity * a_Entity) override + { + Vector3f EntityPos = a_Entity->GetPosition(); + Vector3f BlockPos(m_X + 0.5f, (float)m_Y, m_Z + 0.5f); + float Distance = (EntityPos - BlockPos).Length(); + + if (Distance < 0.5) + { + m_Entity = a_Entity; + return true; // Break out, we only need to know for wooden plates that at least one entity is on top + } + return false; + } + + bool FoundEntity(void) const + { + return m_Entity != NULL; + } + + protected: + cEntity * m_Entity; + cWorld * m_World; + + int m_X; + int m_Y; + int m_Z; + } ; + + cWoodenPressurePlateCallback WoodenPressurePlateCallback(a_BlockX, a_BlockY, a_BlockZ, &m_World); + m_World.ForEachEntity(WoodenPressurePlateCallback); + + if (WoodenPressurePlateCallback.FoundEntity()) + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0x1); + SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WOODEN_PRESSURE_PLATE); + } + else + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0x0); + m_World.WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ); + } + break; + } + default: + LOGD("Unimplemented pressure plate type %s in cRedstoneSimulator", ItemToFullString(a_MyType).c_str()); + break; + } +} + + + + + +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 + { + if (itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) + { + return true; + } + } + return false; +} + + + + + +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 + { + if (itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) + { + return true; + } + } + return false; +} + + + + + +bool cIncrementalRedstoneSimulator::IsRepeaterPowered(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta) +{ + // 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) + { + if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; } + + switch (a_Meta) + { + case 0x0: + { + // Flip the coords to check the back of the repeater + if (itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ + 1))) { return true; } + break; + } + case 0x1: + { + if (itr->a_SourcePos.Equals(Vector3i(a_BlockX - 1, a_BlockY, a_BlockZ))) { return true; } + break; + } + case 0x2: + { + if (itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ - 1))) { return true; } + break; + } + case 0x3: + { + if (itr->a_SourcePos.Equals(Vector3i(a_BlockX + 1, a_BlockY, a_BlockZ))) { return true; } + break; + } + } + } + + 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; } + + switch (a_Meta) + { + case 0x0: + { + if (itr->a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ + 1))) { return true; } + break; + } + case 0x1: + { + if (itr->a_MiddlePos.Equals(Vector3i(a_BlockX - 1, a_BlockY, a_BlockZ))) { return true; } + break; + } + case 0x2: + { + if (itr->a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ - 1))) { return true; } + break; + } + case 0x3: + { + if (itr->a_MiddlePos.Equals(Vector3i(a_BlockX + 1, a_BlockY, a_BlockZ))) { return true; } + break; + } + } + } + return false; // Couldn't find power source behind repeater +} + + + + +bool cIncrementalRedstoneSimulator::IsPistonPowered(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta) +{ + // Pistons cannot be powered through their front face; this function verifies that a source meets this requirement + + 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) + { + if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; } + + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, Face); + + if (!itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) + { + return true; + } + + a_BlockX = OldX; + a_BlockY = OldY; + a_BlockZ = OldZ; + } + + 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; } + + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, Face); + + if (!itr->a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) + { + return true; + } + + a_BlockX = OldX; + a_BlockY = OldY; + a_BlockZ = OldZ; + } + return false; // Source was in front of the piston's front face +} + + + + +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) + { + if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; } + + if (m_World.GetBlock(itr->a_SourcePos) != E_BLOCK_REDSTONE_WIRE) + { + return true; + } + } + + 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; } + + if (m_World.GetBlock(itr->a_SourcePos) != E_BLOCK_REDSTONE_WIRE) + { + return true; + } + } + return false; // Source was in front of the piston's front face +} + + + + + +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) + { + if (itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) + { + if (itr->WasLastStatePowered != IsCurrentStatePowered) // Was the last power state different to the current? + { + return false; // It was, coordinates are no longer simulated + } + else + { + return true; // It wasn't, don't resimulate block, and allow players to toggle + } + } + } + return false; // Block wasn't even in the list, not simulated +} + + + + + +void cIncrementalRedstoneSimulator::SetDirectionLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Direction, BLOCKTYPE a_SourceType) +{ + switch (a_Direction) + { + case BLOCK_FACE_XM: + { + BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ); + + SetBlockLinkedPowered(a_BlockX - 2, a_BlockY, a_BlockZ, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY + 1, a_BlockZ, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY - 1, a_BlockZ, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY, a_BlockZ + 1, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY, a_BlockZ - 1, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + + break; + } + case BLOCK_FACE_XP: + { + BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ); + + SetBlockLinkedPowered(a_BlockX + 2, a_BlockY, a_BlockZ, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY + 1, a_BlockZ, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY - 1, a_BlockZ, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY, a_BlockZ + 1, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY, a_BlockZ - 1, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + + break; + } + case BLOCK_FACE_YM: + { + BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); + + SetBlockLinkedPowered(a_BlockX, a_BlockY - 2, a_BlockZ, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY - 1, a_BlockZ + 1, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY - 1, a_BlockZ - 1, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + + break; + } + case BLOCK_FACE_YP: + { + BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ); + + SetBlockLinkedPowered(a_BlockX, a_BlockY + 2, a_BlockZ, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY + 1, a_BlockZ + 1, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY + 1, a_BlockZ - 1, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + + break; + } + case BLOCK_FACE_ZM: + { + BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1); + + SetBlockLinkedPowered(a_BlockX, a_BlockY, a_BlockZ - 2, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY + 1, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY - 1, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + + break; + } + case BLOCK_FACE_ZP: + { + BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1); + + SetBlockLinkedPowered(a_BlockX, a_BlockY, a_BlockZ + 2, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX + 1, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX - 1, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY + 1, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + SetBlockLinkedPowered(a_BlockX, a_BlockY - 1, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); + + break; + } + default: + { + ASSERT(!"Unhandled face direction when attempting to set blocks as linked powered!"); // Zombies, that wasn't supposed to happen... + break; + } + } +} + + + + + +void cIncrementalRedstoneSimulator::SetAllDirsAsPowered(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_SourceBlock) +{ + static const struct + { + int x, y, z; + } gCrossCoords[] = + { + { 1, 0, 0 }, + {-1, 0, 0 }, + { 0, 0, 1 }, + { 0, 0,-1 }, + { 0, 1, 0 }, + { 0,-1, 0 } + }; + + for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) // Loop through struct to power all directions + { + SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, a_SourceBlock); + } +} + + + + + +void cIncrementalRedstoneSimulator::SetBlockPowered(int a_BlockX, int a_BlockY, int a_BlockZ, int a_SourceX, int a_SourceY, int a_SourceZ, BLOCKTYPE a_SourceBlock) +{ + BLOCKTYPE Block = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ); + if (Block == E_BLOCK_AIR) + { + // Don't set air, fixes some bugs (wires powering themselves) + return; + } + + 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)) && + itr->a_SourcePos.Equals(Vector3i(a_SourceX, a_SourceY, a_SourceZ)) + ) + { + // Check for duplicates + return; + } + } + + 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); +} + + + + + +void cIncrementalRedstoneSimulator::SetBlockLinkedPowered( + int a_BlockX, int a_BlockY, int a_BlockZ, + int a_MiddleX, int a_MiddleY, int a_MiddleZ, + int a_SourceX, int a_SourceY, int a_SourceZ, + BLOCKTYPE a_SourceBlock, BLOCKTYPE a_MiddleBlock +) +{ + BLOCKTYPE DestBlock = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ); + if (DestBlock == E_BLOCK_AIR) + { + // Don't set air, fixes some bugs (wires powering themselves) + return; + } + if (!IsViableMiddleBlock(a_MiddleBlock)) + { + return; + } + + 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)) && + itr->a_MiddlePos.Equals(Vector3i(a_MiddleX, a_MiddleY, a_MiddleZ)) && + itr->a_SourcePos.Equals(Vector3i(a_SourceX, a_SourceY, a_SourceZ)) + ) + { + // Check for duplicates + return; + } + } + + sLinkedPoweredBlocks RC; + 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); +} + + + + + +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) + { + if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) + { + continue; + } + + if (itr->WasLastStatePowered != WasLastStatePowered) + { + // If power states different, update listing + itr->WasLastStatePowered = WasLastStatePowered; + return; + } + else + { + // If states the same, just ignore + return; + } + } + + // We have arrive here; no block must be in list - add one + sSimulatedPlayerToggleableList RC; + RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ); + RC.WasLastStatePowered = WasLastStatePowered; + 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) +{ + for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end(); ++itr) + { + if (itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) + { + if (ShouldPowerOn == itr->ShouldPowerOn) // We are queued already for the same thing, don't replace entry + { + return; + } + + // 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_ElapsedTicks = 0; + itr->ShouldPowerOn = ShouldPowerOn; + return; + } + } + + // Self not in list, add self to list + 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_ElapsedTicks = 0; + RC.ShouldPowerOn = ShouldPowerOn; + m_RepeatersDelayList.push_back(RC); + return; +} + + + + + +cIncrementalRedstoneSimulator::eRedstoneDirection cIncrementalRedstoneSimulator::GetWireDirection(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + int Dir = REDSTONE_NONE; + + BLOCKTYPE NegX = m_World.GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ); + if (IsPotentialSource(NegX)) + { + Dir |= (REDSTONE_X_POS); + } + + BLOCKTYPE PosX = m_World.GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ); + if (IsPotentialSource(PosX)) + { + Dir |= (REDSTONE_X_NEG); + } + + BLOCKTYPE NegZ = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1); + if (IsPotentialSource(NegZ)) + { + if ((Dir & REDSTONE_X_POS) && !(Dir & REDSTONE_X_NEG)) // corner + { + Dir ^= REDSTONE_X_POS; + Dir |= REDSTONE_X_NEG; + } + if ((Dir & REDSTONE_X_NEG) && !(Dir & REDSTONE_X_POS)) // corner + { + Dir ^= REDSTONE_X_NEG; + Dir |= REDSTONE_X_POS; + } + Dir |= REDSTONE_Z_POS; + } + + BLOCKTYPE PosZ = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1); + if (IsPotentialSource(PosZ)) + { + if ((Dir & REDSTONE_X_POS) && !(Dir & REDSTONE_X_NEG)) // corner + { + Dir ^= REDSTONE_X_POS; + Dir |= REDSTONE_X_NEG; + } + if ((Dir & REDSTONE_X_NEG) && !(Dir & REDSTONE_X_POS)) // corner + { + Dir ^= REDSTONE_X_NEG; + Dir |= REDSTONE_X_POS; + } + Dir |= REDSTONE_Z_NEG; + } + return (eRedstoneDirection)Dir; +} + + + + + +bool cIncrementalRedstoneSimulator::IsLeverOn(NIBBLETYPE a_BlockMeta) +{ + // Extract the ON bit from metadata and return if true if it is set: + return ((a_BlockMeta & 0x8) == 0x8); +} + + + + + +bool cIncrementalRedstoneSimulator::IsButtonOn(NIBBLETYPE a_BlockMeta) +{ + return IsLeverOn(a_BlockMeta); +} + + + + diff --git a/src/Simulator/IncrementalRedstoneSimulator.h b/src/Simulator/IncrementalRedstoneSimulator.h new file mode 100644 index 000000000..3397e143c --- /dev/null +++ b/src/Simulator/IncrementalRedstoneSimulator.h @@ -0,0 +1,263 @@ + +#pragma once + +#include "RedstoneSimulator.h" + +/// Per-chunk data for the simulator, specified individual chunks to simulate; 'Data' is not used +typedef cCoordWithBlockAndBoolVector cRedstoneSimulatorChunkData; + + + + + +class cIncrementalRedstoneSimulator : + public cRedstoneSimulator +{ + typedef cRedstoneSimulator super; +public: + + cIncrementalRedstoneSimulator(cWorld & a_World); + ~cIncrementalRedstoneSimulator(); + + 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); } + + enum eRedstoneDirection + { + REDSTONE_NONE = 0, + REDSTONE_X_POS = 0x1, + REDSTONE_X_NEG = 0x2, + REDSTONE_Z_POS = 0x4, + REDSTONE_Z_NEG = 0x8, + }; + eRedstoneDirection GetWireDirection(int a_BlockX, int a_BlockY, int a_BlockZ); + +private: + + struct sPoweredBlocks // Define structure of the directly powered blocks list + { + Vector3i a_BlockPos; // Position of powered block + Vector3i a_SourcePos; // Position of source powering the block at a_BlockPos + }; + + struct sLinkedPoweredBlocks // Define structure of the indirectly powered blocks list (i.e. repeaters powering through a block to the block at the other side) + { + Vector3i a_BlockPos; + Vector3i a_MiddlePos; + Vector3i a_SourcePos; + }; + + struct sSimulatedPlayerToggleableList + { + Vector3i a_BlockPos; + bool WasLastStatePowered; + }; + + struct sRepeatersDelayList + { + Vector3i a_BlockPos; + short a_DelayTicks; + short a_ElapsedTicks; + bool ShouldPowerOn; + }; + + 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; + + virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override; + + // 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 + + /* ====== SOURCES ====== */ + /** Handles the redstone torch */ + void HandleRedstoneTorch(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState); + /** Handles the redstone block */ + 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 buttons */ + void HandleRedstoneButton(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType); + /** Handles daylight sensors */ + void HandleDaylightSensor(int a_BlockX, int a_BlockY, int a_BlockZ); + /** Handles pressure plates */ + void HandlePressurePlate(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType); + /* ==================== */ + + /* ====== CARRIERS ====== */ + /** Handles redstone wire */ + void HandleRedstoneWire(int a_BlockX, int a_BlockY, int a_BlockZ); + /** Handles repeaters */ + void HandleRedstoneRepeater(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState); + /* ====================== */ + + /* ====== DEVICES ====== */ + /** Handles pistons */ + void HandlePiston(int a_BlockX, int a_BlockY, int a_BlockZ); + /** Handles dispensers and droppers */ + void HandleDropSpenser(int a_BlockX, int a_BlockY, int a_BlockZ); + /** Handles TNT (exploding) */ + void HandleTNT(int a_BlockX, int a_BlockY, int a_BlockZ); + /** Handles redstone lamps */ + void HandleRedstoneLamp(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState); + /** Handles doords */ + void HandleDoor(int a_BlockX, int a_BlockY, int a_BlockZ); + /** Handles command blocks */ + void HandleCommandBlock(int a_BlockX, int a_BlockY, int a_BlockZ); + /** Handles activator, detector, and powered rails */ + 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 noteblocks */ + void HandleNoteBlock(int a_BlockX, int a_BlockY, int a_BlockZ); + /* ===================== */ + + /* ====== Helper functions ====== */ + /** Marks a block as powered */ + void SetBlockPowered(int a_BlockX, int a_BlockY, int a_BlockZ, int a_SourceX, int a_SourceY, int a_SourceZ, BLOCKTYPE a_SourceBlock); + /** Marks a block as being powered through another block */ + void SetBlockLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, int a_MiddleX, int a_MiddleY, int a_MiddleZ, int a_SourceX, int a_SourceY, int a_SourceZ, BLOCKTYPE a_SourceBlock, BLOCKTYPE a_MiddeBlock); + /** Marks a block as simulated, who should not be simulated further unless their power state changes, to accomodate a player manually toggling the block without triggering the simulator toggling it back */ + void SetPlayerToggleableBlockAsSimulated(int a_BlockX, int a_BlockY, int a_BlockZ, bool WasLastStatePowered); + /** Marks the second block in a direction as linked powered */ + void SetDirectionLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Direction, BLOCKTYPE a_SourceBlock); + /** 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); + + /** 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); } + /** Returns if a coordinate is in the directly powered blocks list */ + bool AreCoordsDirectlyPowered(int a_BlockX, int a_BlockY, int a_BlockZ); + /** Returns if a coordinate is in the indirectly powered blocks list */ + bool AreCoordsLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ); + /** Returns if a coordinate was marked as simulated (for blocks toggleable by players) */ + bool AreCoordsSimulated(int a_BlockX, int a_BlockY, int a_BlockZ, bool IsCurrentStatePowered); + /** Returns if a repeater is powered */ + bool IsRepeaterPowered(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta); + /** Returns if a piston is powered */ + bool IsPistonPowered(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta); + /** Returns if a wire is powered + The only diffence between this and a normal AreCoordsPowered is that this function checks for a wire powering another wire + */ + bool IsWirePowered(int a_BlockX, int a_BlockY, int a_BlockZ); + + + /** Returns if lever metadata marks it as emitting power */ + bool IsLeverOn(NIBBLETYPE a_BlockMeta); + /** Returns if button metadata marks it as emitting power */ + bool IsButtonOn(NIBBLETYPE a_BlockMeta); + /* ============================== */ + + /* ====== Misc Functions ====== */ + /** Returns if a block is viable to be the MiddleBlock of a SetLinkedPowered operation */ + inline static bool IsViableMiddleBlock(BLOCKTYPE Block) { return g_BlockFullyOccupiesVoxel[Block]; } + + /** Returns if a block is a mechanism (something that accepts power and does something) */ + inline static bool IsMechanism(BLOCKTYPE Block) + { + switch (Block) + { + case E_BLOCK_ACTIVATOR_RAIL: + case E_BLOCK_COMMAND_BLOCK: + case E_BLOCK_PISTON: + case E_BLOCK_STICKY_PISTON: + case E_BLOCK_DISPENSER: + case E_BLOCK_DROPPER: + case E_BLOCK_FENCE_GATE: + case E_BLOCK_HOPPER: + case E_BLOCK_NOTE_BLOCK: + case E_BLOCK_TNT: + case E_BLOCK_TRAPDOOR: + case E_BLOCK_REDSTONE_LAMP_OFF: + case E_BLOCK_REDSTONE_LAMP_ON: + case E_BLOCK_WOODEN_DOOR: + case E_BLOCK_IRON_DOOR: + case E_BLOCK_REDSTONE_REPEATER_OFF: + case E_BLOCK_REDSTONE_REPEATER_ON: + case E_BLOCK_POWERED_RAIL: + { + return true; + } + default: return false; + } + } + + /** Returns if a block has the potential to output power */ + inline static bool IsPotentialSource(BLOCKTYPE Block) + { + switch (Block) + { + case E_BLOCK_DETECTOR_RAIL: + case E_BLOCK_DAYLIGHT_SENSOR: + case E_BLOCK_WOODEN_BUTTON: + case E_BLOCK_STONE_BUTTON: + case E_BLOCK_REDSTONE_WIRE: + case E_BLOCK_REDSTONE_TORCH_ON: + case E_BLOCK_LEVER: + case E_BLOCK_REDSTONE_REPEATER_ON: + case E_BLOCK_BLOCK_OF_REDSTONE: + case E_BLOCK_ACTIVE_COMPARATOR: + { + return true; + } + default: return false; + } + } + + /** Returns if a block is any sort of redstone device */ + inline static bool IsRedstone(BLOCKTYPE Block) + { + switch (Block) + { + // All redstone devices, please alpha sort + case E_BLOCK_ACTIVATOR_RAIL: + case E_BLOCK_ACTIVE_COMPARATOR: + case E_BLOCK_BLOCK_OF_REDSTONE: + case E_BLOCK_COMMAND_BLOCK: + case E_BLOCK_DETECTOR_RAIL: + case E_BLOCK_DISPENSER: + case E_BLOCK_DAYLIGHT_SENSOR: + case E_BLOCK_DROPPER: + case E_BLOCK_FENCE_GATE: + case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE: + case E_BLOCK_HOPPER: + case E_BLOCK_INACTIVE_COMPARATOR: + case E_BLOCK_IRON_DOOR: + case E_BLOCK_LEVER: + case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: + case E_BLOCK_NOTE_BLOCK: + case E_BLOCK_POWERED_RAIL: + case E_BLOCK_REDSTONE_LAMP_OFF: + case E_BLOCK_REDSTONE_LAMP_ON: + case E_BLOCK_REDSTONE_REPEATER_OFF: + case E_BLOCK_REDSTONE_REPEATER_ON: + case E_BLOCK_REDSTONE_TORCH_OFF: + case E_BLOCK_REDSTONE_TORCH_ON: + case E_BLOCK_REDSTONE_WIRE: + case E_BLOCK_STICKY_PISTON: + case E_BLOCK_STONE_BUTTON: + case E_BLOCK_STONE_PRESSURE_PLATE: + case E_BLOCK_TNT: + case E_BLOCK_TRAPDOOR: + case E_BLOCK_TRIPWIRE_HOOK: + case E_BLOCK_WOODEN_BUTTON: + case E_BLOCK_WOODEN_DOOR: + case E_BLOCK_WOODEN_PRESSURE_PLATE: + case E_BLOCK_PISTON: + { + return true; + } + default: return false; + } + } +}; diff --git a/src/Simulator/NoopRedstoneSimulator.h b/src/Simulator/NoopRedstoneSimulator.h new file mode 100644 index 000000000..fe3949198 --- /dev/null +++ b/src/Simulator/NoopRedstoneSimulator.h @@ -0,0 +1,40 @@ + +#pragma once + +#include "RedstoneSimulator.h" + + + + + +class cRedstoneNoopSimulator : + public cRedstoneSimulator +{ + typedef cRedstoneSimulator super; +public: + + cRedstoneNoopSimulator(cWorld & a_World) : + super(a_World) + { + } + + //~cRedstoneNoopSimulator(); + + 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 + { + UNUSED(a_Dt); + UNUSED(a_ChunkX); + UNUSED(a_ChunkZ); + UNUSED(a_Chunk); + } + virtual bool IsAllowedBlock( BLOCKTYPE a_BlockType ) override { return false; } + virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override + { + UNUSED(a_BlockX); + UNUSED(a_BlockY); + UNUSED(a_BlockZ); + UNUSED(a_Chunk); + } + +} ; diff --git a/src/Simulator/RedstoneSimulator.cpp b/src/Simulator/RedstoneSimulator.cpp index 6b7ae3196..a83f21106 100644 --- a/src/Simulator/RedstoneSimulator.cpp +++ b/src/Simulator/RedstoneSimulator.cpp @@ -1,21 +1,15 @@ -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules +#include "Globals.h" #include "RedstoneSimulator.h" -#include "../BlockEntities/DropSpenserEntity.h" -#include "../BlockEntities/NoteEntity.h" -#include "../BlockEntities/CommandBlockEntity.h" -#include "../Entities/TNTEntity.h" -#include "../Blocks/BlockTorch.h" -#include "../Blocks/BlockDoor.h" -#include "../Piston.h" +#include "../World.h" -cRedstoneSimulator::cRedstoneSimulator(cWorld & a_World) - : super(a_World) +cRedstoneSimulator::cRedstoneSimulator(cWorld & a_World) : + super(a_World) { } @@ -23,1512 +17,3 @@ cRedstoneSimulator::cRedstoneSimulator(cWorld & a_World) -cRedstoneSimulator::~cRedstoneSimulator() -{ -} - - - - - -void cRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) -{ - if ((a_Chunk == NULL) || !a_Chunk->IsValid()) - { - return; - } - else if ((a_BlockY < 0) || (a_BlockY > cChunkDef::Height)) - { - return; - } - - int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width; - int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width; - - BLOCKTYPE Block; - NIBBLETYPE Meta; - 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) - { - if (!itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) - { - continue; - } - - if (!IsPotentialSource(Block)) - { - LOGD("cRedstoneSimulator: 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); - break; - } - else if ( - // Changeable sources - ((Block == E_BLOCK_REDSTONE_WIRE) && (Meta == 0)) || - ((Block == E_BLOCK_LEVER) && !IsLeverOn(Meta)) || - ((Block == E_BLOCK_DETECTOR_RAIL) && (Meta & 0x08) == 0) || - (((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta))) || - (((Block == E_BLOCK_STONE_PRESSURE_PLATE) || (Block == E_BLOCK_WOODEN_PRESSURE_PLATE)) && (Meta == 0)) - ) - { - LOGD("cRedstoneSimulator: 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); - break; - } - else if (Block == E_BLOCK_DAYLIGHT_SENSOR) - { - if (!a_Chunk->IsLightValid()) - { - m_World.QueueLightChunk(a_Chunk->GetPosX(), a_Chunk->GetPosZ()); - break; - } - else - { - NIBBLETYPE SkyLight; - a_Chunk->UnboundedRelGetBlockSkyLight(RelX, itr->a_SourcePos.y + 1, RelZ, SkyLight); - - if (a_Chunk->GetTimeAlteredLight(SkyLight) <= 8) // Could use SkyLight - m_World.GetSkyDarkness(); - { - LOGD("cRedstoneSimulator: Erased daylight sensor from powered blocks list due to insufficient light level"); - m_PoweredBlocks.erase(itr); - break; - } - } - } - } - - for (LinkedBlocksList::iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr) - { - if (itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) - { - if (!IsPotentialSource(Block)) - { - LOGD("cRedstoneSimulator: 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; - } - else if ( - // Things that can send power through a block but which depends on meta - ((Block == E_BLOCK_REDSTONE_WIRE) && (Meta == 0)) || - ((Block == E_BLOCK_LEVER) && !IsLeverOn(Meta)) || - (((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta))) - ) - { - LOGD("cRedstoneSimulator: 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; - } - } - else if (itr->a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) - { - if (!IsViableMiddleBlock(Block)) - { - LOGD("cRedstoneSimulator: 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; - } - } - } - - for (SimulatedPlayerToggleableList::iterator itr = m_SimulatedPlayerToggleableBlocks.begin(); itr != m_SimulatedPlayerToggleableBlocks.end(); ++itr) - { - if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) - { - continue; - } - - if (!IsAllowedBlock(Block)) - { - LOGD("cRedstoneSimulator: 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); - break; - } - } - - for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end(); ++itr) - { - if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) - { - continue; - } - - if ((Block != E_BLOCK_REDSTONE_REPEATER_ON) && (Block != E_BLOCK_REDSTONE_REPEATER_OFF)) - { - m_RepeatersDelayList.erase(itr); - break; - } - } - - cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData(); - for (cRedstoneSimulatorChunkData::iterator itr = ChunkData.begin(); itr != ChunkData.end(); ++itr) - { - if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ)) // We are at an entry matching the current (changed) block - { - if (!IsAllowedBlock(Block)) - { - itr->DataTwo = true; // The new blocktype is not redstone; it must be queued to be removed from this list - } - else - { - itr->Data = Block; // Update block information - } - return; - } - } - - if (!IsAllowedBlock(Block)) - { - return; - } - - ChunkData.push_back(cCoordWithBlockAndBool(RelX, a_BlockY, RelZ, Block, false)); -} - - - - - -void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) -{ - // We still attempt to simulate all blocks in the chunk every tick, because of outside influence that needs to be taken into account - // For example, repeaters need to be ticked, pressure plates checked for entities, daylight sensor checked for light changes, etc. - // 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()) - { - return; - } - - int BaseX = a_Chunk->GetPosX() * cChunkDef::Width; - int BaseZ = a_Chunk->GetPosZ() * cChunkDef::Width; - - for (cRedstoneSimulatorChunkData::iterator dataitr = ChunkData.begin(); dataitr != ChunkData.end();) - { - if (dataitr->DataTwo) - { - dataitr = ChunkData.erase(dataitr); - continue; - } - - int a_X = BaseX + dataitr->x; - int a_Z = BaseZ + dataitr->z; - switch (dataitr->Data) - { - 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_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; - case E_BLOCK_NOTE_BLOCK: HandleNoteBlock(a_X, dataitr->y, a_Z); break; - case E_BLOCK_DAYLIGHT_SENSOR: HandleDaylightSensor(a_X, dataitr->y, a_Z); break; - case E_BLOCK_COMMAND_BLOCK: HandleCommandBlock(a_X, dataitr->y, a_Z); break; - - case E_BLOCK_REDSTONE_TORCH_OFF: - case E_BLOCK_REDSTONE_TORCH_ON: - { - HandleRedstoneTorch(a_X, dataitr->y, a_Z, dataitr->Data); - break; - } - case E_BLOCK_STONE_BUTTON: - case E_BLOCK_WOODEN_BUTTON: - { - HandleRedstoneButton(a_X, dataitr->y, a_Z, dataitr->Data); - break; - } - case E_BLOCK_REDSTONE_REPEATER_OFF: - case E_BLOCK_REDSTONE_REPEATER_ON: - { - HandleRedstoneRepeater(a_X, dataitr->y, a_Z, dataitr->Data); - break; - } - case E_BLOCK_PISTON: - case E_BLOCK_STICKY_PISTON: - { - HandlePiston(a_X, dataitr->y, a_Z); - break; - } - case E_BLOCK_REDSTONE_LAMP_OFF: - case E_BLOCK_REDSTONE_LAMP_ON: - { - HandleRedstoneLamp(a_X, dataitr->y, a_Z, dataitr->Data); - break; - } - case E_BLOCK_DISPENSER: - case E_BLOCK_DROPPER: - { - HandleDropSpenser(a_X, dataitr->y, a_Z); - break; - } - case E_BLOCK_WOODEN_DOOR: - case E_BLOCK_IRON_DOOR: - { - HandleDoor(a_X, dataitr->y, a_Z); - break; - } - case E_BLOCK_ACTIVATOR_RAIL: - case E_BLOCK_DETECTOR_RAIL: - case E_BLOCK_POWERED_RAIL: - { - HandleRail(a_X, dataitr->y, a_Z, dataitr->Data); - break; - } - case E_BLOCK_WOODEN_PRESSURE_PLATE: - case E_BLOCK_STONE_PRESSURE_PLATE: - { - HandlePressurePlate(a_X, dataitr->y, a_Z, dataitr->Data); - break; - } - } - ++dataitr; - } -} - - - - - -void cRedstoneSimulator::HandleRedstoneTorch(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState) -{ - static const struct // Define which directions the torch can power - { - int x, y, z; - } gCrossCoords[] = - { - { 1, 0, 0}, - {-1, 0, 0}, - { 0, 0, 1}, - { 0, 0, -1}, - { 0, 1, 0}, - } ; - - if (a_MyState == E_BLOCK_REDSTONE_TORCH_ON) - { - // Check if the block the torch is on is powered - int X = a_BlockX; int Y = a_BlockY; int Z = a_BlockZ; - AddFaceDirection(X, Y, Z, cBlockTorchHandler::MetaDataToDirection(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ)), true); // Inverse true to get the block torch is on - - if (AreCoordsDirectlyPowered(X, Y, Z)) - { - // 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)); - return; - } - - // Torch still on, make all 4(X, Z) + 1(Y) sides powered - for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) - { - BLOCKTYPE Type = m_World.GetBlock(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z); - if (i + 1 < ARRAYCOUNT(gCrossCoords)) // Sides of torch, not top (top is last) - { - if ( - ((IsMechanism(Type)) || (Type == E_BLOCK_REDSTONE_WIRE)) && // Is it a mechanism or wire? Not block/other torch etc. - (!Vector3i(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z).Equals(Vector3i(X, Y, Z))) // CAN'T power block is that it is on - ) - { - SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_ON); - } - } - else - { - // Top side, power whatever is there, including blocks - SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_ON); - // Power all blocks surrounding block above torch - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YP, E_BLOCK_REDSTONE_TORCH_ON); - } - } - - if (m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) != 0x5) // Is torch standing on ground? If NOT (i.e. on wall), power block beneath - { - BLOCKTYPE Type = m_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); - - if ((IsMechanism(Type)) || (Type == E_BLOCK_REDSTONE_WIRE)) // Still can't make a normal block powered though! - { - SetBlockPowered(a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_ON); - } - } - } - else - { - // Check if the block the torch is on is powered - int X = a_BlockX; int Y = a_BlockY; int Z = a_BlockZ; - AddFaceDirection(X, Y, Z, cBlockTorchHandler::MetaDataToDirection(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ)), true); // Inverse true to get the block torch is on - - // See if off state torch can be turned on again - if (AreCoordsDirectlyPowered(X, Y, Z)) - { - return; // Something matches, torch still powered - } - - // Block torch on not powered, can be turned on again! - m_World.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_ON, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ)); - } -} - - - - - -void cRedstoneSimulator::HandleRedstoneBlock(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BLOCK_OF_REDSTONE); - SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BLOCK_OF_REDSTONE); // Set self as powered -} - - - - - -void cRedstoneSimulator::HandleRedstoneLever(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - if (IsLeverOn(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ))) - { - SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_LEVER); - - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, E_BLOCK_LEVER); - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, E_BLOCK_LEVER); - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YM, E_BLOCK_LEVER); - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YP, E_BLOCK_LEVER); - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZM, E_BLOCK_LEVER); - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, E_BLOCK_LEVER); - } -} - - - - - -void cRedstoneSimulator::HandleRedstoneButton(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType) -{ - if (IsButtonOn(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ))) - { - SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, a_BlockType); - - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, a_BlockType); - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, a_BlockType); - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YM, a_BlockType); - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YP, a_BlockType); - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZM, a_BlockType); - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, a_BlockType); - } -} - - - - - -void cRedstoneSimulator::HandleRedstoneWire(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - static const struct // Define which directions the wire can receive power from - { - int x, y, z; - } gCrossCoords[] = - { - { 1, 0, 0}, /* Wires on same level start */ - {-1, 0, 0}, - { 0, 0, 1}, - { 0, 0, -1}, /* Wires on same level stop */ - { 1, 1, 0}, /* Wires one higher, surrounding self start */ - {-1, 1, 0}, - { 0, 1, 1}, - { 0, 1, -1}, /* Wires one higher, surrounding self stop */ - { 1,-1, 0}, /* Wires one lower, surrounding self start */ - {-1,-1, 0}, - { 0,-1, 1}, - { 0,-1, -1}, /* Wires one lower, surrounding self stop */ - } ; - - static const struct // Define which directions the wire will check for repeater prescence - { - int x, y, z; - } gSideCoords[] = - { - { 1, 0, 0 }, - {-1, 0, 0 }, - { 0, 0, 1 }, - { 0, 0,-1 }, - { 0, 1, 0 }, - }; - - // Check to see if directly beside a power source - if (IsWirePowered(a_BlockX, a_BlockY, a_BlockZ)) - { - m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 15); // Maximum power - } - else - { - NIBBLETYPE MetaToSet = 0; - NIBBLETYPE MyMeta = m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - int TimesMetaSmaller = 0, TimesFoundAWire = 0; - - for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) // Loop through all directions to transfer or receive power - { - if ((i >= 4) && (i <= 7)) // If we are currently checking for wire surrounding ourself one block above... - { - if (g_BlockIsSolid[m_World.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)]) // If there is something solid above us (wire cut off)... - { - continue; // We don't receive power from that wire - } - } - else if ((i >= 8) && (i <= 11)) // See above, but this is for wire below us - { - if (g_BlockIsSolid[m_World.GetBlock(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y + 1, a_BlockZ + gCrossCoords[i].z)]) - { - continue; - } - } - - BLOCKTYPE SurroundType; - NIBBLETYPE SurroundMeta; - m_World.GetBlockTypeMeta(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, SurroundType, SurroundMeta); - - if (SurroundType == E_BLOCK_REDSTONE_WIRE) - { - TimesFoundAWire++; - - if (SurroundMeta > 1) // Wires of power 1 or 0 cannot transfer power TO ME, don't bother checking - { - // Does surrounding wire have a higher power level than self? - // >= to fix a bug where wires bordering each other with the same power level will appear (in terms of meta) to power each other, when they aren't actually in the powered list - if (SurroundMeta >= MyMeta) - { - MetaToSet = SurroundMeta - 1; // To improve performance - } - } - - if (SurroundMeta < MyMeta) // Go through all surroundings to see if self power is larger than everyone else's - { - TimesMetaSmaller++; - } - } - } - - if (TimesMetaSmaller == TimesFoundAWire) - { - // All surrounding metas were smaller - self must have been a wire that was - // transferring power to other wires around. - // However, self not directly powered anymore, so source must have been removed, - // therefore, self must be set to meta zero - 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 - { - m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, MetaToSet); - } - } - - if (m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) != 0) // A powered wire - { - for (size_t i = 0; i < ARRAYCOUNT(gSideCoords); i++) // Look for repeaters immediately surrounding self and try to power them - { - if (m_World.GetBlock(a_BlockX + gSideCoords[i].x, a_BlockY + gSideCoords[i].y, a_BlockZ + gSideCoords[i].z) == E_BLOCK_REDSTONE_REPEATER_OFF) - { - SetBlockPowered(a_BlockX + gSideCoords[i].x, a_BlockY + gSideCoords[i].y, a_BlockZ + gSideCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); - } - } - - // Wire still powered, power blocks beneath - SetBlockPowered(a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YM, E_BLOCK_REDSTONE_WIRE); - - switch (GetWireDirection(a_BlockX, a_BlockY, a_BlockZ)) - { - case REDSTONE_NONE: - { - SetAllDirsAsPowered(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; - } - case REDSTONE_X_POS: - { - SetBlockPowered(a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, E_BLOCK_REDSTONE_WIRE); - break; - } - case REDSTONE_X_NEG: - { - SetBlockPowered(a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, E_BLOCK_REDSTONE_WIRE); - break; - } - case REDSTONE_Z_POS: - { - 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_ZP, E_BLOCK_REDSTONE_WIRE); - break; - } - case REDSTONE_Z_NEG: - { - 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_ZM, E_BLOCK_REDSTONE_WIRE); - break; - } - } - } -} - - - - - -void cRedstoneSimulator::HandleRedstoneRepeater(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState) -{ - NIBBLETYPE a_Meta = m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - - bool IsOn = ((a_MyState == E_BLOCK_REDSTONE_REPEATER_ON) ? true : false); // Cache if repeater is on - bool IsSelfPowered = IsRepeaterPowered(a_BlockX, a_BlockY, a_BlockZ, a_Meta & 0x3); // Cache if repeater is pwoered - - 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); - } - 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); - } - - for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end(); ++itr) - { - if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) - { - continue; - } - - if (itr->a_ElapsedTicks >= itr->a_DelayTicks) // Has the elapsed ticks reached the target ticks? - { - if (itr->ShouldPowerOn) - { - if (!IsOn) - { - m_World.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON, a_Meta); // For performance - } - - switch (a_Meta & 0x3) // We only want the direction (bottom) bits - { - case 0x0: - { - SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON); - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZM, E_BLOCK_REDSTONE_REPEATER_ON); - break; - } - case 0x1: - { - SetBlockPowered(a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON); - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, E_BLOCK_REDSTONE_REPEATER_ON); - break; - } - case 0x2: - { - SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON); - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, E_BLOCK_REDSTONE_REPEATER_ON); - break; - } - case 0x3: - { - SetBlockPowered(a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_ON); - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, E_BLOCK_REDSTONE_REPEATER_ON); - break; - } - } - - // Removal of the data entry will be handled in SimChunk - we still want to continue trying to power blocks, even if our delay time has reached - // Otherwise, the power state of blocks in front won't update after we have powered on - return; - } - else - { - if (IsOn) - { - 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 - return; - } - } - else - { - // Apparently, incrementing ticks only works reliably here, and not in SimChunk; - // With a world with lots of redstone, the repeaters simply do not delay - // I am confounded to say why. Perhaps optimisation failure. - LOGD("Incremented a repeater @ {%i %i %i} | Elapsed ticks: %i | Target delay: %i", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z, itr->a_ElapsedTicks, itr->a_DelayTicks); - itr->a_ElapsedTicks++; - } - } -} - - - - - -void cRedstoneSimulator::HandlePiston(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - cPiston Piston(&m_World); - if (IsPistonPowered(a_BlockX, a_BlockY, a_BlockZ, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x7)) // We only want the bottom three bits (4th controls extended-ness) - { - Piston.ExtendPiston(a_BlockX, a_BlockY, a_BlockZ); - } - else - { - Piston.RetractPiston(a_BlockX, a_BlockY, a_BlockZ); - } -} - - - - - -void cRedstoneSimulator::HandleDropSpenser(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - class cSetPowerToDropSpenser : - public cDropSpenserCallback - { - bool m_IsPowered; - public: - cSetPowerToDropSpenser(bool a_IsPowered) : m_IsPowered(a_IsPowered) {} - - virtual bool Item(cDropSpenserEntity * a_DropSpenser) override - { - a_DropSpenser->SetRedstonePower(m_IsPowered); - return false; - } - } DrSpSP (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)); - - m_World.DoWithDropSpenserAt(a_BlockX, a_BlockY, a_BlockZ, DrSpSP); -} - - - - - -void cRedstoneSimulator::HandleRedstoneLamp(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState) -{ - if (a_MyState == E_BLOCK_REDSTONE_LAMP_OFF) - { - if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) - { - m_World.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_LAMP_ON, 0); - } - } - else - { - if (!AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) - { - m_World.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_LAMP_OFF, 0); - } - } -} - - - - - -void cRedstoneSimulator::HandleTNT(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - 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.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); - } -} - - - - - -void cRedstoneSimulator::HandleDoor(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) - { - if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, true)) - { - cChunkInterface ChunkInterface(m_World.GetChunkMap()); - cBlockDoorHandler::ChangeDoor(ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); - SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, true); - } - } - else - { - if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, false)) - { - cChunkInterface ChunkInterface(m_World.GetChunkMap()); - cBlockDoorHandler::ChangeDoor(ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); - SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, false); - } - } -} - - - - - -void cRedstoneSimulator::HandleCommandBlock(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - class cSetPowerToCommandBlock : - public cCommandBlockCallback - { - bool m_IsPowered; - public: - cSetPowerToCommandBlock(bool a_IsPowered) : m_IsPowered(a_IsPowered) {} - - virtual bool Item(cCommandBlockEntity * a_CommandBlock) override - { - a_CommandBlock->SetRedstonePower(m_IsPowered); - return false; - } - } CmdBlockSP (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)); - - m_World.DoWithCommandBlockAt(a_BlockX, a_BlockY, a_BlockZ, CmdBlockSP); -} - - - - - -void cRedstoneSimulator::HandleRail(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType) -{ - switch (a_MyType) - { - case E_BLOCK_DETECTOR_RAIL: - { - if ((m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x08) == 0x08) - { - SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, a_MyType); - } - break; - } - case E_BLOCK_ACTIVATOR_RAIL: - case E_BLOCK_POWERED_RAIL: - { - if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) - { - m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) | 0x08); - } - else - { - m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x07); - } - break; - } - default: LOGD("Unhandled type of rail in %s", __FUNCTION__); - } -} - - - - - -void cRedstoneSimulator::HandleTrapdoor(int a_BlockX, int a_BlockY, int 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, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) | 0x4); - 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, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0xB); // Take into account that the fourth bit is needed for trapdoors too - SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, false); - } - } -} - - - - - -void cRedstoneSimulator::HandleNoteBlock(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - bool m_bAreCoordsPowered = AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ); - - if (m_bAreCoordsPowered) - { - if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, true)) - { - class cSetPowerToNoteBlock : - public cNoteBlockCallback - { - bool m_IsPowered; - public: - cSetPowerToNoteBlock(bool a_IsPowered) : m_IsPowered(a_IsPowered) {} - - virtual bool Item(cNoteEntity * a_NoteBlock) override - { - if (m_IsPowered) - { - a_NoteBlock->MakeSound(); - } - return false; - } - } NoteBlockSP(m_bAreCoordsPowered); - - m_World.DoWithNoteBlockAt(a_BlockX, a_BlockY, a_BlockZ, NoteBlockSP); - SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, true); - } - } - else - { - if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, false)) - { - SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, false); - } - } -} - - - - - -void cRedstoneSimulator::HandleDaylightSensor(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - int a_ChunkX, a_ChunkZ; - cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, a_ChunkX, a_ChunkZ); - - if (!m_World.IsChunkLighted(a_ChunkX, a_ChunkZ)) - { - m_World.QueueLightChunk(a_ChunkX, a_ChunkZ); - } - else - { - NIBBLETYPE SkyLight = m_World.GetBlockSkyLight(a_BlockX, a_BlockY + 1, a_BlockZ) - m_World.GetSkyDarkness(); - if (SkyLight > 8) - { - SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_DAYLIGHT_SENSOR); - } - } -} - - - - - -void cRedstoneSimulator::HandlePressurePlate(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType) -{ - switch (a_MyType) - { - case E_BLOCK_STONE_PRESSURE_PLATE: - { - // MCS feature - stone pressure plates can only be triggered by players :D - cPlayer * a_Player = m_World.FindClosestPlayer(Vector3f(a_BlockX + 0.5f, (float)a_BlockY, a_BlockZ + 0.5f), 0.5f, false); - - if (a_Player != NULL) - { - m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0x1); - SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_STONE_PRESSURE_PLATE); - } - else - { - m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0x0); - m_World.WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ); - } - break; - } - case E_BLOCK_WOODEN_PRESSURE_PLATE: - { - class cWoodenPressurePlateCallback : - public cEntityCallback - { - public: - cWoodenPressurePlateCallback(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : - m_X(a_BlockX), - m_Y(a_BlockY), - m_Z(a_BlockZ), - m_World(a_World), - m_Entity(NULL) - { - } - - virtual bool Item(cEntity * a_Entity) override - { - Vector3f EntityPos = a_Entity->GetPosition(); - Vector3f BlockPos(m_X + 0.5f, (float)m_Y, m_Z + 0.5f); - float Distance = (EntityPos - BlockPos).Length(); - - if (Distance < 0.5) - { - m_Entity = a_Entity; - return true; // Break out, we only need to know for wooden plates that at least one entity is on top - } - return false; - } - - bool FoundEntity(void) const - { - return m_Entity != NULL; - } - - protected: - cEntity * m_Entity; - cWorld * m_World; - - int m_X; - int m_Y; - int m_Z; - } ; - - cWoodenPressurePlateCallback WoodenPressurePlateCallback(a_BlockX, a_BlockY, a_BlockZ, &m_World); - m_World.ForEachEntity(WoodenPressurePlateCallback); - - if (WoodenPressurePlateCallback.FoundEntity()) - { - m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0x1); - SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WOODEN_PRESSURE_PLATE); - } - else - { - m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0x0); - m_World.WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ); - } - break; - } - default: - LOGD("Unimplemented pressure plate type %s in cRedstoneSimulator", ItemToFullString(a_MyType).c_str()); - break; - } -} - - - - - -bool cRedstoneSimulator::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 - { - if (itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) - { - return true; - } - } - return false; -} - - - - - -bool cRedstoneSimulator::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 - { - if (itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) - { - return true; - } - } - return false; -} - - - - - -bool cRedstoneSimulator::IsRepeaterPowered(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta) -{ - // 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) - { - if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; } - - switch (a_Meta) - { - case 0x0: - { - // Flip the coords to check the back of the repeater - if (itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ + 1))) { return true; } - break; - } - case 0x1: - { - if (itr->a_SourcePos.Equals(Vector3i(a_BlockX - 1, a_BlockY, a_BlockZ))) { return true; } - break; - } - case 0x2: - { - if (itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ - 1))) { return true; } - break; - } - case 0x3: - { - if (itr->a_SourcePos.Equals(Vector3i(a_BlockX + 1, a_BlockY, a_BlockZ))) { return true; } - break; - } - } - } - - 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; } - - switch (a_Meta) - { - case 0x0: - { - if (itr->a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ + 1))) { return true; } - break; - } - case 0x1: - { - if (itr->a_MiddlePos.Equals(Vector3i(a_BlockX - 1, a_BlockY, a_BlockZ))) { return true; } - break; - } - case 0x2: - { - if (itr->a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ - 1))) { return true; } - break; - } - case 0x3: - { - if (itr->a_MiddlePos.Equals(Vector3i(a_BlockX + 1, a_BlockY, a_BlockZ))) { return true; } - break; - } - } - } - return false; // Couldn't find power source behind repeater -} - - - - -bool cRedstoneSimulator::IsPistonPowered(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta) -{ - // Pistons cannot be powered through their front face; this function verifies that a source meets this requirement - - 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) - { - if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; } - - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, Face); - - if (!itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) - { - return true; - } - - a_BlockX = OldX; - a_BlockY = OldY; - a_BlockZ = OldZ; - } - - 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; } - - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, Face); - - if (!itr->a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) - { - return true; - } - - a_BlockX = OldX; - a_BlockY = OldY; - a_BlockZ = OldZ; - } - return false; // Source was in front of the piston's front face -} - - - - -bool cRedstoneSimulator::IsWirePowered(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - 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; } - - if (m_World.GetBlock(itr->a_SourcePos) != E_BLOCK_REDSTONE_WIRE) - { - return true; - } - } - - 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; } - - if (m_World.GetBlock(itr->a_SourcePos) != E_BLOCK_REDSTONE_WIRE) - { - return true; - } - } - return false; // Source was in front of the piston's front face -} - - - - - -bool cRedstoneSimulator::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) - { - if (itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) - { - if (itr->WasLastStatePowered != IsCurrentStatePowered) // Was the last power state different to the current? - { - return false; // It was, coordinates are no longer simulated - } - else - { - return true; // It wasn't, don't resimulate block, and allow players to toggle - } - } - } - return false; // Block wasn't even in the list, not simulated -} - - - - - -void cRedstoneSimulator::SetDirectionLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Direction, BLOCKTYPE a_SourceType) -{ - switch (a_Direction) - { - case BLOCK_FACE_XM: - { - BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ); - - SetBlockLinkedPowered(a_BlockX - 2, a_BlockY, a_BlockZ, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX - 1, a_BlockY + 1, a_BlockZ, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX - 1, a_BlockY - 1, a_BlockZ, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX - 1, a_BlockY, a_BlockZ + 1, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX - 1, a_BlockY, a_BlockZ - 1, a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - - break; - } - case BLOCK_FACE_XP: - { - BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ); - - SetBlockLinkedPowered(a_BlockX + 2, a_BlockY, a_BlockZ, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX + 1, a_BlockY + 1, a_BlockZ, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX + 1, a_BlockY - 1, a_BlockZ, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX + 1, a_BlockY, a_BlockZ + 1, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX + 1, a_BlockY, a_BlockZ - 1, a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - - break; - } - case BLOCK_FACE_YM: - { - BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ); - - SetBlockLinkedPowered(a_BlockX, a_BlockY - 2, a_BlockZ, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX + 1, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX - 1, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX, a_BlockY - 1, a_BlockZ + 1, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX, a_BlockY - 1, a_BlockZ - 1, a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - - break; - } - case BLOCK_FACE_YP: - { - BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ); - - SetBlockLinkedPowered(a_BlockX, a_BlockY + 2, a_BlockZ, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX + 1, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX - 1, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX, a_BlockY + 1, a_BlockZ + 1, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX, a_BlockY + 1, a_BlockZ - 1, a_BlockX, a_BlockY + 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - - break; - } - case BLOCK_FACE_ZM: - { - BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1); - - SetBlockLinkedPowered(a_BlockX, a_BlockY, a_BlockZ - 2, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX + 1, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX - 1, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX, a_BlockY + 1, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX, a_BlockY - 1, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - - break; - } - case BLOCK_FACE_ZP: - { - BLOCKTYPE MiddleBlock = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1); - - SetBlockLinkedPowered(a_BlockX, a_BlockY, a_BlockZ + 2, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX + 1, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX - 1, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX, a_BlockY + 1, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - SetBlockLinkedPowered(a_BlockX, a_BlockY - 1, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, a_SourceType, MiddleBlock); - - break; - } - default: - { - ASSERT(!"Unhandled face direction when attempting to set blocks as linked powered!"); // Zombies, that wasn't supposed to happen... - break; - } - } -} - - - - - -void cRedstoneSimulator::SetAllDirsAsPowered(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_SourceBlock) -{ - static const struct - { - int x, y, z; - } gCrossCoords[] = - { - { 1, 0, 0 }, - {-1, 0, 0 }, - { 0, 0, 1 }, - { 0, 0,-1 }, - { 0, 1, 0 }, - { 0,-1, 0 } - }; - - for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++) // Loop through struct to power all directions - { - SetBlockPowered(a_BlockX + gCrossCoords[i].x, a_BlockY + gCrossCoords[i].y, a_BlockZ + gCrossCoords[i].z, a_BlockX, a_BlockY, a_BlockZ, a_SourceBlock); - } -} - - - - - -void cRedstoneSimulator::SetBlockPowered(int a_BlockX, int a_BlockY, int a_BlockZ, int a_SourceX, int a_SourceY, int a_SourceZ, BLOCKTYPE a_SourceBlock) -{ - BLOCKTYPE Block = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ); - if (Block == E_BLOCK_AIR) - { - // Don't set air, fixes some bugs (wires powering themselves) - return; - } - - 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)) && - itr->a_SourcePos.Equals(Vector3i(a_SourceX, a_SourceY, a_SourceZ)) - ) - { - // Check for duplicates - return; - } - } - - 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); -} - - - - - -void cRedstoneSimulator::SetBlockLinkedPowered( - int a_BlockX, int a_BlockY, int a_BlockZ, - int a_MiddleX, int a_MiddleY, int a_MiddleZ, - int a_SourceX, int a_SourceY, int a_SourceZ, - BLOCKTYPE a_SourceBlock, BLOCKTYPE a_MiddleBlock -) -{ - BLOCKTYPE DestBlock = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ); - if (DestBlock == E_BLOCK_AIR) - { - // Don't set air, fixes some bugs (wires powering themselves) - return; - } - if (!IsViableMiddleBlock(a_MiddleBlock)) - { - return; - } - - 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)) && - itr->a_MiddlePos.Equals(Vector3i(a_MiddleX, a_MiddleY, a_MiddleZ)) && - itr->a_SourcePos.Equals(Vector3i(a_SourceX, a_SourceY, a_SourceZ)) - ) - { - // Check for duplicates - return; - } - } - - sLinkedPoweredBlocks RC; - 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); -} - - - - - -void cRedstoneSimulator::SetPlayerToggleableBlockAsSimulated(int a_BlockX, int a_BlockY, int a_BlockZ, bool WasLastStatePowered) -{ - for (SimulatedPlayerToggleableList::iterator itr = m_SimulatedPlayerToggleableBlocks.begin(); itr != m_SimulatedPlayerToggleableBlocks.end(); ++itr) - { - if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) - { - continue; - } - - if (itr->WasLastStatePowered != WasLastStatePowered) - { - // If power states different, update listing - itr->WasLastStatePowered = WasLastStatePowered; - return; - } - else - { - // If states the same, just ignore - return; - } - } - - // We have arrive here; no block must be in list - add one - sSimulatedPlayerToggleableList RC; - RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ); - RC.WasLastStatePowered = WasLastStatePowered; - m_SimulatedPlayerToggleableBlocks.push_back(RC); -} - - - - - -void cRedstoneSimulator::QueueRepeaterPowerChange(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta, short a_ElapsedTicks, bool ShouldPowerOn) -{ - for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end(); ++itr) - { - if (itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) - { - if (ShouldPowerOn == itr->ShouldPowerOn) // We are queued already for the same thing, don't replace entry - { - return; - } - - // 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_ElapsedTicks = 0; - itr->ShouldPowerOn = ShouldPowerOn; - return; - } - } - - // Self not in list, add self to list - 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_ElapsedTicks = 0; - RC.ShouldPowerOn = ShouldPowerOn; - m_RepeatersDelayList.push_back(RC); - return; -} - - - - - -cRedstoneSimulator::eRedstoneDirection cRedstoneSimulator::GetWireDirection(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - int Dir = REDSTONE_NONE; - - BLOCKTYPE NegX = m_World.GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ); - if (IsPotentialSource(NegX)) - { - Dir |= (REDSTONE_X_POS); - } - - BLOCKTYPE PosX = m_World.GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ); - if (IsPotentialSource(PosX)) - { - Dir |= (REDSTONE_X_NEG); - } - - BLOCKTYPE NegZ = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1); - if (IsPotentialSource(NegZ)) - { - if ((Dir & REDSTONE_X_POS) && !(Dir & REDSTONE_X_NEG)) // corner - { - Dir ^= REDSTONE_X_POS; - Dir |= REDSTONE_X_NEG; - } - if ((Dir & REDSTONE_X_NEG) && !(Dir & REDSTONE_X_POS)) // corner - { - Dir ^= REDSTONE_X_NEG; - Dir |= REDSTONE_X_POS; - } - Dir |= REDSTONE_Z_POS; - } - - BLOCKTYPE PosZ = m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1); - if (IsPotentialSource(PosZ)) - { - if ((Dir & REDSTONE_X_POS) && !(Dir & REDSTONE_X_NEG)) // corner - { - Dir ^= REDSTONE_X_POS; - Dir |= REDSTONE_X_NEG; - } - if ((Dir & REDSTONE_X_NEG) && !(Dir & REDSTONE_X_POS)) // corner - { - Dir ^= REDSTONE_X_NEG; - Dir |= REDSTONE_X_POS; - } - Dir |= REDSTONE_Z_NEG; - } - return (eRedstoneDirection)Dir; -} - - - - - -bool cRedstoneSimulator::IsLeverOn(NIBBLETYPE a_BlockMeta) -{ - // Extract the ON bit from metadata and return if true if it is set: - return ((a_BlockMeta & 0x8) == 0x8); -} - - - - - -bool cRedstoneSimulator::IsButtonOn(NIBBLETYPE a_BlockMeta) -{ - return IsLeverOn(a_BlockMeta); -} - - - - diff --git a/src/Simulator/RedstoneSimulator.h b/src/Simulator/RedstoneSimulator.h index c505b2a0f..b3e20c9a5 100644 --- a/src/Simulator/RedstoneSimulator.h +++ b/src/Simulator/RedstoneSimulator.h @@ -3,10 +3,6 @@ #include "Simulator.h" -/// Per-chunk data for the simulator, specified individual chunks to simulate; 'Data' is not used -typedef cCoordWithBlockAndBoolVector cRedstoneSimulatorChunkData; - - @@ -14,250 +10,8 @@ class cRedstoneSimulator : public cSimulator { typedef cSimulator super; + public: - cRedstoneSimulator(cWorld & a_World); - ~cRedstoneSimulator(); - - 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); } - - enum eRedstoneDirection - { - REDSTONE_NONE = 0, - REDSTONE_X_POS = 0x1, - REDSTONE_X_NEG = 0x2, - REDSTONE_Z_POS = 0x4, - REDSTONE_Z_NEG = 0x8, - }; - eRedstoneDirection GetWireDirection(int a_BlockX, int a_BlockY, int a_BlockZ); - -private: - - struct sPoweredBlocks // Define structure of the directly powered blocks list - { - Vector3i a_BlockPos; // Position of powered block - Vector3i a_SourcePos; // Position of source powering the block at a_BlockPos - }; - - struct sLinkedPoweredBlocks // Define structure of the indirectly powered blocks list (i.e. repeaters powering through a block to the block at the other side) - { - Vector3i a_BlockPos; - Vector3i a_MiddlePos; - Vector3i a_SourcePos; - }; - - struct sSimulatedPlayerToggleableList - { - Vector3i a_BlockPos; - bool WasLastStatePowered; - }; - - struct sRepeatersDelayList - { - Vector3i a_BlockPos; - short a_DelayTicks; - short a_ElapsedTicks; - bool ShouldPowerOn; - }; - - 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; - - virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override; - - // 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 - - /* ====== SOURCES ====== */ - /** Handles the redstone torch */ - void HandleRedstoneTorch(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState); - /** Handles the redstone block */ - 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 buttons */ - void HandleRedstoneButton(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType); - /** Handles daylight sensors */ - void HandleDaylightSensor(int a_BlockX, int a_BlockY, int a_BlockZ); - /** Handles pressure plates */ - void HandlePressurePlate(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType); - /* ==================== */ - - /* ====== CARRIERS ====== */ - /** Handles redstone wire */ - void HandleRedstoneWire(int a_BlockX, int a_BlockY, int a_BlockZ); - /** Handles repeaters */ - void HandleRedstoneRepeater(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState); - /* ====================== */ - - /* ====== DEVICES ====== */ - /** Handles pistons */ - void HandlePiston(int a_BlockX, int a_BlockY, int a_BlockZ); - /** Handles dispensers and droppers */ - void HandleDropSpenser(int a_BlockX, int a_BlockY, int a_BlockZ); - /** Handles TNT (exploding) */ - void HandleTNT(int a_BlockX, int a_BlockY, int a_BlockZ); - /** Handles redstone lamps */ - void HandleRedstoneLamp(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState); - /** Handles doords */ - void HandleDoor(int a_BlockX, int a_BlockY, int a_BlockZ); - /** Handles command blocks */ - void HandleCommandBlock(int a_BlockX, int a_BlockY, int a_BlockZ); - /** Handles activator, detector, and powered rails */ - 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 noteblocks */ - void HandleNoteBlock(int a_BlockX, int a_BlockY, int a_BlockZ); - /* ===================== */ - - /* ====== Helper functions ====== */ - /** Marks a block as powered */ - void SetBlockPowered(int a_BlockX, int a_BlockY, int a_BlockZ, int a_SourceX, int a_SourceY, int a_SourceZ, BLOCKTYPE a_SourceBlock); - /** Marks a block as being powered through another block */ - void SetBlockLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, int a_MiddleX, int a_MiddleY, int a_MiddleZ, int a_SourceX, int a_SourceY, int a_SourceZ, BLOCKTYPE a_SourceBlock, BLOCKTYPE a_MiddeBlock); - /** Marks a block as simulated, who should not be simulated further unless their power state changes, to accomodate a player manually toggling the block without triggering the simulator toggling it back */ - void SetPlayerToggleableBlockAsSimulated(int a_BlockX, int a_BlockY, int a_BlockZ, bool WasLastStatePowered); - /** Marks the second block in a direction as linked powered */ - void SetDirectionLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Direction, BLOCKTYPE a_SourceBlock); - /** 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); - - /** 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); } - /** Returns if a coordinate is in the directly powered blocks list */ - bool AreCoordsDirectlyPowered(int a_BlockX, int a_BlockY, int a_BlockZ); - /** Returns if a coordinate is in the indirectly powered blocks list */ - bool AreCoordsLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ); - /** Returns if a coordinate was marked as simulated (for blocks toggleable by players) */ - bool AreCoordsSimulated(int a_BlockX, int a_BlockY, int a_BlockZ, bool IsCurrentStatePowered); - /** Returns if a repeater is powered */ - bool IsRepeaterPowered(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta); - /** Returns if a piston is powered */ - bool IsPistonPowered(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta); - /** Returns if a wire is powered - The only diffence between this and a normal AreCoordsPowered is that this function checks for a wire powering another wire - */ - bool IsWirePowered(int a_BlockX, int a_BlockY, int a_BlockZ); - - - /** Returns if lever metadata marks it as emitting power */ - bool IsLeverOn(NIBBLETYPE a_BlockMeta); - /** Returns if button metadata marks it as emitting power */ - bool IsButtonOn(NIBBLETYPE a_BlockMeta); - /* ============================== */ - - /* ====== Misc Functions ====== */ - /** Returns if a block is viable to be the MiddleBlock of a SetLinkedPowered operation */ - inline static bool IsViableMiddleBlock(BLOCKTYPE Block) { return g_BlockFullyOccupiesVoxel[Block]; } - - /** Returns if a block is a mechanism (something that accepts power and does something) */ - inline static bool IsMechanism(BLOCKTYPE Block) - { - switch (Block) - { - case E_BLOCK_ACTIVATOR_RAIL: - case E_BLOCK_COMMAND_BLOCK: - case E_BLOCK_PISTON: - case E_BLOCK_STICKY_PISTON: - case E_BLOCK_DISPENSER: - case E_BLOCK_DROPPER: - case E_BLOCK_FENCE_GATE: - case E_BLOCK_HOPPER: - case E_BLOCK_NOTE_BLOCK: - case E_BLOCK_TNT: - case E_BLOCK_TRAPDOOR: - case E_BLOCK_REDSTONE_LAMP_OFF: - case E_BLOCK_REDSTONE_LAMP_ON: - case E_BLOCK_WOODEN_DOOR: - case E_BLOCK_IRON_DOOR: - case E_BLOCK_REDSTONE_REPEATER_OFF: - case E_BLOCK_REDSTONE_REPEATER_ON: - case E_BLOCK_POWERED_RAIL: - { - return true; - } - default: return false; - } - } - - /** Returns if a block has the potential to output power */ - inline static bool IsPotentialSource(BLOCKTYPE Block) - { - switch (Block) - { - case E_BLOCK_DETECTOR_RAIL: - case E_BLOCK_DAYLIGHT_SENSOR: - case E_BLOCK_WOODEN_BUTTON: - case E_BLOCK_STONE_BUTTON: - case E_BLOCK_REDSTONE_WIRE: - case E_BLOCK_REDSTONE_TORCH_ON: - case E_BLOCK_LEVER: - case E_BLOCK_REDSTONE_REPEATER_ON: - case E_BLOCK_BLOCK_OF_REDSTONE: - case E_BLOCK_ACTIVE_COMPARATOR: - { - return true; - } - default: return false; - } - } - /** Returns if a block is any sort of redstone device */ - inline static bool IsRedstone(BLOCKTYPE Block) - { - switch (Block) - { - // All redstone devices, please alpha sort - case E_BLOCK_ACTIVATOR_RAIL: - case E_BLOCK_ACTIVE_COMPARATOR: - case E_BLOCK_BLOCK_OF_REDSTONE: - case E_BLOCK_COMMAND_BLOCK: - case E_BLOCK_DETECTOR_RAIL: - case E_BLOCK_DISPENSER: - case E_BLOCK_DAYLIGHT_SENSOR: - case E_BLOCK_DROPPER: - case E_BLOCK_FENCE_GATE: - case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE: - case E_BLOCK_HOPPER: - case E_BLOCK_INACTIVE_COMPARATOR: - case E_BLOCK_IRON_DOOR: - case E_BLOCK_LEVER: - case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: - case E_BLOCK_NOTE_BLOCK: - case E_BLOCK_POWERED_RAIL: - case E_BLOCK_REDSTONE_LAMP_OFF: - case E_BLOCK_REDSTONE_LAMP_ON: - case E_BLOCK_REDSTONE_REPEATER_OFF: - case E_BLOCK_REDSTONE_REPEATER_ON: - case E_BLOCK_REDSTONE_TORCH_OFF: - case E_BLOCK_REDSTONE_TORCH_ON: - case E_BLOCK_REDSTONE_WIRE: - case E_BLOCK_STICKY_PISTON: - case E_BLOCK_STONE_BUTTON: - case E_BLOCK_STONE_PRESSURE_PLATE: - case E_BLOCK_TNT: - case E_BLOCK_TRAPDOOR: - case E_BLOCK_TRIPWIRE_HOOK: - case E_BLOCK_WOODEN_BUTTON: - case E_BLOCK_WOODEN_DOOR: - case E_BLOCK_WOODEN_PRESSURE_PLATE: - case E_BLOCK_PISTON: - { - return true; - } - default: return false; - } - } -}; +} ; diff --git a/src/WebAdmin.cpp b/src/WebAdmin.cpp index e6a5a01b3..e88de5947 100644 --- a/src/WebAdmin.cpp +++ b/src/WebAdmin.cpp @@ -42,6 +42,7 @@ public: cWebAdmin::cWebAdmin(void) : m_IsInitialized(false), + m_IsRunning(false), m_TemplateScript("<webadmin_template>") { } @@ -52,29 +53,26 @@ cWebAdmin::cWebAdmin(void) : cWebAdmin::~cWebAdmin() { - if (m_IsInitialized) - { - LOGD("Stopping WebAdmin..."); - } + ASSERT(!m_IsRunning); // Was the HTTP server stopped properly? } -void cWebAdmin::AddPlugin( cWebPlugin * a_Plugin ) +void cWebAdmin::AddPlugin(cWebPlugin * a_Plugin) { - m_Plugins.remove( a_Plugin ); - m_Plugins.push_back( a_Plugin ); + m_Plugins.remove(a_Plugin); + m_Plugins.push_back(a_Plugin); } -void cWebAdmin::RemovePlugin( cWebPlugin * a_Plugin ) +void cWebAdmin::RemovePlugin(cWebPlugin * a_Plugin) { - m_Plugins.remove( a_Plugin ); + m_Plugins.remove(a_Plugin); } @@ -87,7 +85,8 @@ bool cWebAdmin::Init(void) { LOGWARN("Regenerating webadmin.ini, all settings will be reset"); m_IniFile.AddHeaderComment(" This file controls the webadmin feature of MCServer"); - m_IniFile.AddHeaderComment(" Username format: [User:*username*] | Password format: Password=*password*; for example:"); + m_IniFile.AddHeaderComment(" Username format: [User:*username*]"); + m_IniFile.AddHeaderComment(" Password format: Password=*password*; for example:"); m_IniFile.AddHeaderComment(" [User:admin]"); m_IniFile.AddHeaderComment(" Password=admin"); } @@ -134,7 +133,24 @@ bool cWebAdmin::Start(void) m_TemplateScript.Close(); } - return m_HTTPServer.Start(*this); + m_IsRunning = m_HTTPServer.Start(*this); + return m_IsRunning; +} + + + + + +void cWebAdmin::Stop(void) +{ + if (!m_IsRunning) + { + return; + } + + LOGD("Stopping WebAdmin..."); + m_HTTPServer.Stop(); + m_IsRunning = false; } diff --git a/src/WebAdmin.h b/src/WebAdmin.h index 3eb807640..a2a07a543 100644 --- a/src/WebAdmin.h +++ b/src/WebAdmin.h @@ -56,13 +56,13 @@ struct HTTPRequest AString Username; // tolua_end - /// Parameters given in the URL, after the questionmark + /** Parameters given in the URL, after the questionmark */ StringStringMap Params; // >> EXPORTED IN MANUALBINDINGS << - /// Parameters posted as a part of a form - either in the URL (GET method) or in the body (POST method) + /** Parameters posted as a part of a form - either in the URL (GET method) or in the body (POST method) */ StringStringMap PostParams; // >> EXPORTED IN MANUALBINDINGS << - /// Same as PostParams + /** Same as PostParams */ FormDataMap FormData; // >> EXPORTED IN MANUALBINDINGS << } ; // tolua_export @@ -107,14 +107,17 @@ public: cWebAdmin(void); virtual ~cWebAdmin(); - /// Initializes the object. Returns true if successfully initialized and ready to start + /** Initializes the object. Returns true if successfully initialized and ready to start */ bool Init(void); - /// Starts the HTTP server taking care of the admin. Returns true if successful + /** Starts the HTTP server taking care of the admin. Returns true if successful */ bool Start(void); + + /** Stops the HTTP server, if it was started. */ + void Stop(void); - void AddPlugin( cWebPlugin* a_Plugin ); - void RemovePlugin( cWebPlugin* a_Plugin ); + void AddPlugin(cWebPlugin * a_Plugin); + void RemovePlugin(cWebPlugin * a_Plugin); // TODO: Convert this to the auto-locking callback mechanism used for looping players in worlds and such PluginList GetPlugins() const { return m_Plugins; } // >> EXPORTED IN MANUALBINDINGS << @@ -123,13 +126,13 @@ public: sWebAdminPage GetPage(const HTTPRequest & a_Request); - /// Returns the contents of the default page - the list of plugins and players + /** Returns the contents of the default page - the list of plugins and players */ AString GetDefaultPage(void); - /// Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) + /** Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) */ AString GetBaseURL(const AString & a_URL); - /// Escapes text passed into it, so it can be embedded into html. + /** Escapes text passed into it, so it can be embedded into html. */ static AString GetHTMLEscapedString(const AString & a_Input); AString GetIPv4Ports(void) const { return m_PortsIPv4; } @@ -137,21 +140,21 @@ public: // tolua_end - /// Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) + /** Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) */ AString GetBaseURL(const AStringVector& a_URLSplit); protected: - /// Common base class for request body data handlers + /** Common base class for request body data handlers */ class cRequestData { public: virtual ~cRequestData() {} // Force a virtual destructor in all descendants - /// Called when a new chunk of body data is received + /** Called when a new chunk of body data is received */ virtual void OnBody(const char * a_Data, int a_Size) = 0; } ; - /// The body handler for requests in the "/webadmin" and "/~webadmin" paths + /** The body handler for requests in the "/webadmin" and "/~webadmin" paths */ class cWebadminRequestData : public cRequestData, public cHTTPFormParser::cCallbacks @@ -182,10 +185,13 @@ protected: } ; - /// Set to true if Init() succeeds and the webadmin isn't to be disabled + /** Set to true if Init() succeeds and the webadmin isn't to be disabled */ bool m_IsInitialized; + + /** Set to true if Start() succeeds in starting the server, reset back to false in Stop(). */ + bool m_IsRunning; - /// The webadmin.ini file, used for the settings and allowed logins + /** The webadmin.ini file, used for the settings and allowed logins */ cIniFile m_IniFile; PluginList m_Plugins; @@ -193,19 +199,19 @@ protected: AString m_PortsIPv4; AString m_PortsIPv6; - /// The Lua template script to provide templates: + /** The Lua template script to provide templates: */ cLuaState m_TemplateScript; - /// The HTTP server which provides the underlying HTTP parsing, serialization and events + /** The HTTP server which provides the underlying HTTP parsing, serialization and events */ cHTTPServer m_HTTPServer; AString GetTemplate(void); - /// Handles requests coming to the "/webadmin" or "/~webadmin" URLs + /** Handles requests coming to the "/webadmin" or "/~webadmin" URLs */ void HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); - /// Handles requests for the root page + /** Handles requests for the root page */ void HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request); // cHTTPServer::cCallbacks overrides: diff --git a/src/World.cpp b/src/World.cpp index 04dda0d65..d2523b0a5 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -29,8 +29,9 @@ #include "Simulator/FluidSimulator.h" #include "Simulator/FireSimulator.h" #include "Simulator/NoopFluidSimulator.h" +#include "Simulator/NoopRedstoneSimulator.h" #include "Simulator/SandSimulator.h" -#include "Simulator/RedstoneSimulator.h" +#include "Simulator/IncrementalRedstoneSimulator.h" #include "Simulator/VaporizeFluidSimulator.h" // Mobs: @@ -247,11 +248,13 @@ cWorld::cWorld(const AString & a_WorldName) : m_SkyDarkness(0), m_Weather(eWeather_Sunny), m_WeatherInterval(24000), // Guaranteed 1 day of sunshine at server start :) + m_Scoreboard(this), m_GeneratorCallbacks(*this), m_TickThread(*this), m_Scoreboard(this), m_bCommandBlocksEnabled(false), m_bUseChatPrefixes(true) + m_TickThread(*this) { LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str()); @@ -599,12 +602,11 @@ void cWorld::Start(void) m_LavaSimulator = InitializeFluidSimulator(IniFile, "Lava", E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA); m_SandSimulator = new cSandSimulator(*this, IniFile); m_FireSimulator = new cFireSimulator(*this, IniFile); - m_RedstoneSimulator = new cRedstoneSimulator(*this); + m_RedstoneSimulator = InitializeRedstoneSimulator(IniFile); - // Water and Lava simulators get registered in InitializeFluidSimulator() + // Water, Lava and Redstone simulators get registered in their initialize function. m_SimulatorManager->RegisterSimulator(m_SandSimulator, 1); m_SimulatorManager->RegisterSimulator(m_FireSimulator, 1); - m_SimulatorManager->RegisterSimulator(m_RedstoneSimulator, 1); m_Lighting.Start(this); m_Storage.Start(this, m_StorageSchema, m_StorageCompressionFactor ); @@ -2874,6 +2876,36 @@ void cWorld::TabCompleteUserName(const AString & a_Text, AStringVector & a_Resul +cRedstoneSimulator * cWorld::InitializeRedstoneSimulator(cIniFile & a_IniFile) +{ + AString SimulatorName = a_IniFile.GetValueSet("Physics", "RedstoneSimulator", ""); + + if (SimulatorName.empty()) + { + LOGWARNING("[Physics] RedstoneSimulator not present or empty in %s, using the default of \"incremental\".", GetIniFileName().c_str()); + SimulatorName = "incremental"; + } + + cRedstoneSimulator * res = NULL; + + if (NoCaseCompare(SimulatorName, "incremental") == 0) + { + res = new cIncrementalRedstoneSimulator(*this); + } + else if (NoCaseCompare(SimulatorName, "noop") == 0) + { + res = new cRedstoneNoopSimulator(*this); + } + + m_SimulatorManager->RegisterSimulator(res, 1); + + return res; +} + + + + + cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const char * a_FluidName, BLOCKTYPE a_SimulateBlock, BLOCKTYPE a_StationaryBlock) { AString SimulatorNameKey; diff --git a/src/World.h b/src/World.h index a2385fca4..0174be03b 100644 --- a/src/World.h +++ b/src/World.h @@ -875,6 +875,9 @@ private: /** Creates a new fluid simulator, loads its settings from the inifile (a_FluidName section) */ cFluidSimulator * InitializeFluidSimulator(cIniFile & a_IniFile, const char * a_FluidName, BLOCKTYPE a_SimulateBlock, BLOCKTYPE a_StationaryBlock); + + /** Creates a new redstone simulator.*/ + cRedstoneSimulator * InitializeRedstoneSimulator(cIniFile & a_IniFile); }; // tolua_export |