diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Blocks/BlockHandler.cpp | 2 | ||||
-rw-r--r-- | src/ClientHandle.cpp | 12 | ||||
-rw-r--r-- | src/ClientHandle.h | 14 | ||||
-rw-r--r-- | src/Defines.h | 2 | ||||
-rw-r--r-- | src/Entities/Player.cpp | 29 | ||||
-rw-r--r-- | src/Entities/Player.h | 3 | ||||
-rw-r--r-- | src/Generating/BioGen.cpp | 148 | ||||
-rw-r--r-- | src/Protocol/Protocol17x.cpp | 13 | ||||
-rw-r--r-- | src/Protocol/ProtocolRecognizer.cpp | 2 | ||||
-rw-r--r-- | src/Root.cpp | 4 | ||||
-rw-r--r-- | src/Server.cpp | 7 | ||||
-rw-r--r-- | src/Server.h | 8 | ||||
-rw-r--r-- | src/StringUtils.cpp | 28 | ||||
-rw-r--r-- | src/StringUtils.h | 5 | ||||
-rw-r--r-- | src/VoronoiMap.cpp | 126 | ||||
-rw-r--r-- | src/VoronoiMap.h | 44 | ||||
-rw-r--r-- | src/World.h | 3 |
17 files changed, 339 insertions, 111 deletions
diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp index cee2f4b99..30b303cfd 100644 --- a/src/Blocks/BlockHandler.cpp +++ b/src/Blocks/BlockHandler.cpp @@ -427,7 +427,7 @@ void cBlockHandler::DropBlock(cChunkInterface & a_ChunkInterface, cWorldInterfac if (a_CanDrop) { if ((a_Digger != NULL) && (a_Digger->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchSilkTouch) > 0)) - { + { switch (m_BlockType) { case E_BLOCK_CAKE: diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 487ef3867..43a4161e4 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -312,8 +312,16 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID, ASSERT(m_Player == NULL); m_Username = a_Name; - m_UUID = a_UUID; - m_Properties = a_Properties; + + // Only assign UUID and properties if not already pre-assigned (BungeeCord sends those in the Handshake packet): + if (m_UUID.empty()) + { + m_UUID = a_UUID; + } + if (m_Properties.empty()) + { + m_Properties = a_Properties; + } // Send login success (if the protocol supports it): m_Protocol->SendLoginSuccess(); diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 05d05e052..b91c3722c 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -64,15 +64,27 @@ public: const AString & GetIPString(void) const { return m_IPString; } // tolua_export + /** Sets the IP string that the client is using. Overrides the IP string that was read from the socket. + Used mainly by BungeeCord compatibility code. */ + void SetIPString(const AString & a_IPString) { m_IPString = a_IPString; } + cPlayer * GetPlayer(void) { return m_Player; } // tolua_export /** Returns the player's UUID, as used by the protocol, in the short form (no dashes) */ const AString & GetUUID(void) const { return m_UUID; } // tolua_export - void SetUUID(const AString & a_UUID) { m_UUID = a_UUID; } + /** Sets the player's UUID, as used by the protocol. Short UUID form (no dashes) is expected. + Used mainly by BungeeCord compatibility code - when authenticating is done on the BungeeCord server + and the results are passed to MCS running in offline mode. */ + void SetUUID(const AString & a_UUID) { ASSERT(a_UUID.size() == 32); m_UUID = a_UUID; } const Json::Value & GetProperties(void) const { return m_Properties; } + /** Sets the player's properties, such as skin image and signature. + Used mainly by BungeeCord compatibility code - property querying is done on the BungeeCord server + and the results are passed to MCS running in offline mode. */ + void SetProperties(const Json::Value & a_Properties) { m_Properties = a_Properties; } + /** Generates an UUID based on the username stored for this client, and stores it in the m_UUID member. This is used for the offline (non-auth) mode, when there's no UUID source. Each username generates a unique and constant UUID, so that when the player reconnects with the same name, their UUID is the same. diff --git a/src/Defines.h b/src/Defines.h index 78c58034e..6355b75b4 100644 --- a/src/Defines.h +++ b/src/Defines.h @@ -115,12 +115,14 @@ enum eGameMode eGameMode_Survival = 0, eGameMode_Creative = 1, eGameMode_Adventure = 2, + eGameMode_Spectator = 3, // Easier-to-use synonyms: gmNotSet = eGameMode_NotSet, gmSurvival = eGameMode_Survival, gmCreative = eGameMode_Creative, gmAdventure = eGameMode_Adventure, + gmSpectator = eGameMode_Spectator, // These two are used to check GameMode for validity when converting from integers. gmMax, // Gets automatically assigned diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 55065d550..871311e86 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -451,6 +451,11 @@ void cPlayer::CancelChargingBow(void) void cPlayer::SetTouchGround(bool a_bTouchGround) { + if (IsGameModeSpectator()) // You can fly through the ground in Spectator + { + return; + } + m_bTouchGround = a_bTouchGround; if (!m_bTouchGround) @@ -585,7 +590,7 @@ bool cPlayer::Feed(int a_Food, double a_Saturation) void cPlayer::AddFoodExhaustion(double a_Exhaustion) { - if (!IsGameModeCreative()) + if (!(IsGameModeCreative() || IsGameModeSpectator())) { m_FoodExhaustionLevel = std::min(m_FoodExhaustionLevel + a_Exhaustion, 40.0); } @@ -823,9 +828,9 @@ bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) { if ((a_TDI.DamageType != dtInVoid) && (a_TDI.DamageType != dtPlugin)) { - if (IsGameModeCreative()) + if (IsGameModeCreative() || IsGameModeSpectator()) { - // No damage / health in creative mode if not void or plugin damage + // No damage / health in creative or spectator mode if not void or plugin damage return false; } } @@ -1043,6 +1048,14 @@ bool cPlayer::IsGameModeAdventure(void) const +bool cPlayer::IsGameModeSpectator(void) const +{ + return (m_GameMode == gmSpectator) || // Either the player is explicitly in Spectator + ((m_GameMode == gmNotSet) && m_World->IsGameModeSpectator()); // or they inherit from the world and the world is Adventure +} + + + void cPlayer::SetTeam(cTeam * a_Team) { @@ -1158,7 +1171,7 @@ void cPlayer::SetGameMode(eGameMode a_GameMode) m_GameMode = a_GameMode; m_ClientHandle->SendGameMode(a_GameMode); - if (!IsGameModeCreative()) + if (!(IsGameModeCreative() || IsGameModeSpectator())) { SetFlying(false); SetCanFly(false); @@ -1342,6 +1355,7 @@ void cPlayer::MoveTo( const Vector3d & a_NewPos) void cPlayer::SetVisible(bool a_bVisible) { + // Need to Check if the player or other players are in gamemode spectator, but will break compatibility if (a_bVisible && !m_bVisible) // Make visible { m_bVisible = true; @@ -1502,6 +1516,11 @@ void cPlayer::TossPickup(const cItem & a_Item) void cPlayer::TossItems(const cItems & a_Items) { + if (IsGameModeSpectator()) // Players can't toss items in spectator + { + return; + } + m_Stats.AddValue(statItemsDropped, (StatValue)a_Items.Size()); double vX = 0, vY = 0, vZ = 0; @@ -1788,7 +1807,7 @@ bool cPlayer::SaveToDisk() void cPlayer::UseEquippedItem(int a_Amount) { - if (IsGameModeCreative()) // No damage in creative + if (IsGameModeCreative() || IsGameModeSpectator()) // No damage in creative or spectator { return; } diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 9821cc6d9..d64dd6b99 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -171,6 +171,9 @@ public: /** Returns true if the player is in Adventure mode, either explicitly, or by inheriting from current world */ bool IsGameModeAdventure(void) const; + /** Returns true if the player is in Spectator mode, either explicitly, or by inheriting from current world */ + bool IsGameModeSpectator(void) const; + AString GetIP(void) const { return m_IP; } // tolua_export /** Returns the associated team, NULL if none */ diff --git a/src/Generating/BioGen.cpp b/src/Generating/BioGen.cpp index 60ad4e3eb..217ca8f80 100644 --- a/src/Generating/BioGen.cpp +++ b/src/Generating/BioGen.cpp @@ -13,72 +13,6 @@ //////////////////////////////////////////////////////////////////////////////// -// cBiomeGen: - -cBiomeGen * cBiomeGen::CreateBiomeGen(cIniFile & a_IniFile, int a_Seed, bool & a_CacheOffByDefault) -{ - AString BiomeGenName = a_IniFile.GetValueSet("Generator", "BiomeGen", ""); - if (BiomeGenName.empty()) - { - LOGWARN("[Generator] BiomeGen value not set in world.ini, using \"MultiStepMap\"."); - BiomeGenName = "MultiStepMap"; - } - - cBiomeGen * res = NULL; - a_CacheOffByDefault = false; - if (NoCaseCompare(BiomeGenName, "constant") == 0) - { - res = new cBioGenConstant; - a_CacheOffByDefault = true; // we're generating faster than a cache would retrieve data :) - } - else if (NoCaseCompare(BiomeGenName, "checkerboard") == 0) - { - res = new cBioGenCheckerboard; - a_CacheOffByDefault = true; // we're (probably) generating faster than a cache would retrieve data - } - else if (NoCaseCompare(BiomeGenName, "voronoi") == 0) - { - res = new cBioGenVoronoi(a_Seed); - } - else if (NoCaseCompare(BiomeGenName, "distortedvoronoi") == 0) - { - res = new cBioGenDistortedVoronoi(a_Seed); - } - else if (NoCaseCompare(BiomeGenName, "twolevel") == 0) - { - res = new cBioGenTwoLevel(a_Seed); - } - else - { - if (NoCaseCompare(BiomeGenName, "multistepmap") != 0) - { - LOGWARNING("Unknown BiomeGen \"%s\", using \"MultiStepMap\" instead.", BiomeGenName.c_str()); - } - res = new cBioGenMultiStepMap(a_Seed); - - /* - // Performance-testing: - LOGINFO("Measuring performance of cBioGenMultiStepMap..."); - clock_t BeginTick = clock(); - for (int x = 0; x < 5000; x++) - { - cChunkDef::BiomeMap Biomes; - res->GenBiomes(x * 5, x * 5, Biomes); - } - clock_t Duration = clock() - BeginTick; - LOGINFO("cBioGenMultiStepMap for 5000 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); - //*/ - } - res->InitializeBiomeGen(a_IniFile); - - return res; -} - - - - - -//////////////////////////////////////////////////////////////////////////////// // cBioGenConstant: void cBioGenConstant::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) @@ -402,8 +336,13 @@ void cBioGenVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & void cBioGenVoronoi::InitializeBiomeGen(cIniFile & a_IniFile) { super::InitializeBiomeGen(a_IniFile); - m_Voronoi.SetCellSize(a_IniFile.GetValueSetI("Generator", "VoronoiCellSize", 64)); - InitializeBiomes (a_IniFile.GetValueSet ("Generator", "VoronoiBiomes", "")); + int CellSize = a_IniFile.GetValueSetI("Generator", "VoronoiCellSize", 128); + int JitterSize = a_IniFile.GetValueSetI("Generator", "VoronoiJitterSize", CellSize); + int OddRowOffset = a_IniFile.GetValueSetI("Generator", "VoronoiOddRowOffset", 0); + m_Voronoi.SetCellSize(CellSize); + m_Voronoi.SetJitterSize(JitterSize); + m_Voronoi.SetOddRowOffset(OddRowOffset); + InitializeBiomes(a_IniFile.GetValueSet ("Generator", "VoronoiBiomes", "")); } @@ -846,9 +785,10 @@ void cBioGenTwoLevel::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap { for (int x = 0; x < cChunkDef::Width; x++) { - int MinDist1, MinDist2; - int BiomeGroup = m_VoronoiLarge.GetValueAt(DistortX[x][z], DistortZ[x][z], MinDist1, MinDist2) / 7; - int BiomeIdx = m_VoronoiSmall.GetValueAt(DistortX[x][z], DistortZ[x][z], MinDist1, MinDist2) / 11; + int SeedX, SeedZ, MinDist2; + int BiomeGroup = m_VoronoiLarge.GetValueAt(DistortX[x][z], DistortZ[x][z], SeedX, SeedZ, MinDist2) / 7; + int BiomeIdx = m_VoronoiSmall.GetValueAt(DistortX[x][z], DistortZ[x][z], SeedX, SeedZ, MinDist2) / 11; + int MinDist1 = (DistortX[x][z] - SeedX) * (DistortX[x][z] - SeedX) + (DistortZ[x][z] - SeedZ) * (DistortZ[x][z] - SeedZ); cChunkDef::SetBiome(a_BiomeMap, x, z, SelectBiome(BiomeGroup, BiomeIdx, (MinDist1 < MinDist2 / 4) ? 0 : 1)); } } @@ -987,3 +927,69 @@ void cBioGenTwoLevel::InitializeBiomeGen(cIniFile & a_IniFile) + +//////////////////////////////////////////////////////////////////////////////// +// cBiomeGen: + +cBiomeGen * cBiomeGen::CreateBiomeGen(cIniFile & a_IniFile, int a_Seed, bool & a_CacheOffByDefault) +{ + AString BiomeGenName = a_IniFile.GetValueSet("Generator", "BiomeGen", ""); + if (BiomeGenName.empty()) + { + LOGWARN("[Generator] BiomeGen value not set in world.ini, using \"MultiStepMap\"."); + BiomeGenName = "MultiStepMap"; + } + + cBiomeGen * res = NULL; + a_CacheOffByDefault = false; + if (NoCaseCompare(BiomeGenName, "constant") == 0) + { + res = new cBioGenConstant; + a_CacheOffByDefault = true; // we're generating faster than a cache would retrieve data :) + } + else if (NoCaseCompare(BiomeGenName, "checkerboard") == 0) + { + res = new cBioGenCheckerboard; + a_CacheOffByDefault = true; // we're (probably) generating faster than a cache would retrieve data + } + else if (NoCaseCompare(BiomeGenName, "voronoi") == 0) + { + res = new cBioGenVoronoi(a_Seed); + } + else if (NoCaseCompare(BiomeGenName, "distortedvoronoi") == 0) + { + res = new cBioGenDistortedVoronoi(a_Seed); + } + else if (NoCaseCompare(BiomeGenName, "twolevel") == 0) + { + res = new cBioGenTwoLevel(a_Seed); + } + else + { + if (NoCaseCompare(BiomeGenName, "multistepmap") != 0) + { + LOGWARNING("Unknown BiomeGen \"%s\", using \"MultiStepMap\" instead.", BiomeGenName.c_str()); + } + res = new cBioGenMultiStepMap(a_Seed); + + /* + // Performance-testing: + LOGINFO("Measuring performance of cBioGenMultiStepMap..."); + clock_t BeginTick = clock(); + for (int x = 0; x < 5000; x++) + { + cChunkDef::BiomeMap Biomes; + res->GenBiomes(x * 5, x * 5, Biomes); + } + clock_t Duration = clock() - BeginTick; + LOGINFO("cBioGenMultiStepMap for 5000 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC); + //*/ + } + res->InitializeBiomeGen(a_IniFile); + + return res; +} + + + + diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 05935fbe1..2fbc58541 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -102,6 +102,19 @@ cProtocol172::cProtocol172(cClientHandle * a_Client, const AString & a_ServerAdd m_IsEncrypted(false), m_LastSentDimension(dimNotSet) { + // BungeeCord handling: + // If BC is setup with ip_forward == true, it sends additional data in the login packet's ServerAddress field: + // hostname\00ip-address\00uuid\00profile-properties-as-json + AStringVector Params; + if (cRoot::Get()->GetServer()->ShouldAllowBungeeCord() && SplitZeroTerminatedStrings(a_ServerAddress, Params) && (Params.size() == 4)) + { + LOGD("Player at %s connected via BungeeCord", Params[1].c_str()); + m_ServerAddress = Params[0]; + m_Client->SetIPString(Params[1]); + m_Client->SetUUID(cMojangAPI::MakeUUIDShort(Params[2])); + m_Client->SetProperties(Params[3]); + } + // Create the comm log file, if so requested: if (g_ShouldLogCommIn || g_ShouldLogCommOut) { diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 36e791fc7..4516975bf 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -28,7 +28,7 @@ cProtocolRecognizer::cProtocolRecognizer(cClientHandle * a_Client) : super(a_Client), m_Protocol(NULL), - m_Buffer(512) + m_Buffer(8192) // We need a larger buffer to support BungeeCord - it sends one huge packet at the start { } diff --git a/src/Root.cpp b/src/Root.cpp index 86a497a76..966a9b4ba 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -113,8 +113,8 @@ void cRoot::Start(void) LOG("--- Started Log ---\n"); #ifdef BUILD_ID - LOG("MCServer " BUILD_SERIES_NAME " build id: " BUILD_ID ); - LOG("from commit id: " BUILD_COMMIT_ID " built at: " BUILD_DATETIME ); + LOG("MCServer " BUILD_SERIES_NAME " build id: " BUILD_ID); + LOG("from commit id: " BUILD_COMMIT_ID " built at: " BUILD_DATETIME); #endif cDeadlockDetect dd; diff --git a/src/Server.cpp b/src/Server.cpp index 069e2a169..969ffd693 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -259,6 +259,13 @@ bool cServer::InitServer(cIniFile & a_SettingsIni) m_ServerID = sid.str(); m_ServerID.resize(16, '0'); } + + // Check if both BungeeCord and online mode are on, if so, warn the admin: + m_ShouldAllowBungeeCord = a_SettingsIni.GetValueSetB("Authentication", "AllowBungeeCord", false); + if (m_ShouldAllowBungeeCord && m_ShouldAuthenticate) + { + LOGWARNING("WARNING: BungeeCord is allowed and server set to online mode. This is unsafe and will not work properly. Disable either authentication or BungeeCord in settings.ini."); + } m_ShouldLoadOfflinePlayerData = a_SettingsIni.GetValueSetB("PlayerData", "LoadOfflinePlayerData", false); m_ShouldLoadNamedPlayerData = a_SettingsIni.GetValueSetB("PlayerData", "LoadNamedPlayerData", true); diff --git a/src/Server.h b/src/Server.h index f20e6932f..6d659fa40 100644 --- a/src/Server.h +++ b/src/Server.h @@ -131,6 +131,11 @@ public: // tolua_export Loaded from the settings.ini [PlayerData].LoadNamedPlayerData setting. */ bool ShouldLoadNamedPlayerData(void) const { return m_ShouldLoadNamedPlayerData; } + /** Returns true if BungeeCord logins (that specify the player's UUID) are allowed. + Read from settings, admins should set this to true only when they chain to BungeeCord, + it makes the server vulnerable to identity theft through direct connections. */ + bool ShouldAllowBungeeCord(void) const { return m_ShouldAllowBungeeCord; } + private: friend class cRoot; // so cRoot can create and destroy cServer @@ -230,6 +235,9 @@ private: This allows a seamless transition from name-based to UUID-based player storage. Loaded from the settings.ini [PlayerData].LoadNamedPlayerData setting. */ bool m_ShouldLoadNamedPlayerData; + + /** True if BungeeCord handshake packets (with player UUID) should be accepted. */ + bool m_ShouldAllowBungeeCord; cServer(void); diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp index 5f88cbf64..73147eebc 100644 --- a/src/StringUtils.cpp +++ b/src/StringUtils.cpp @@ -869,3 +869,31 @@ void SetBEInt(char * a_Mem, Int32 a_Value) + +bool SplitZeroTerminatedStrings(const AString & a_Strings, AStringVector & a_Output) +{ + a_Output.clear(); + size_t size = a_Strings.size(); + size_t start = 0; + bool res = false; + for (size_t i = 0; i < size; i++) + { + if (a_Strings[i] == 0) + { + a_Output.push_back(a_Strings.substr(start, i - start)); + start = i + 1; + res = true; + } + } + if (start < size) + { + a_Output.push_back(a_Strings.substr(start, size - start)); + res = true; + } + + return res; +} + + + + diff --git a/src/StringUtils.h b/src/StringUtils.h index 72a90a8c2..a76894d05 100644 --- a/src/StringUtils.h +++ b/src/StringUtils.h @@ -99,6 +99,11 @@ extern int GetBEInt(const char * a_Mem); /// Writes four bytes to the specified memory location so that they interpret as BigEndian int extern void SetBEInt(char * a_Mem, Int32 a_Value); +/** Splits a string that has embedded \0 characters, on those characters. +a_Output is first cleared and then each separate string is pushed back into a_Output. +Returns true if there are at least two strings in a_Output (there was at least one \0 separator). */ +extern bool SplitZeroTerminatedStrings(const AString & a_Strings, AStringVector & a_Output); + /// Parses any integer type. Checks bounds and returns errors out of band. template <class T> bool StringToInteger(const AString & a_str, T & a_Num) diff --git a/src/VoronoiMap.cpp b/src/VoronoiMap.cpp index 5efd09c01..5ad634fe4 100644 --- a/src/VoronoiMap.cpp +++ b/src/VoronoiMap.cpp @@ -10,11 +10,13 @@ -cVoronoiMap::cVoronoiMap(int a_Seed, int a_CellSize) : +cVoronoiMap::cVoronoiMap(int a_Seed, int a_CellSize, int a_JitterSize) : m_Noise1(a_Seed + 1), m_Noise2(a_Seed + 2), m_Noise3(a_Seed + 3), - m_CellSize(a_CellSize), + m_CellSize(std::max(a_CellSize, 2)), + m_JitterSize(Clamp(a_JitterSize, 1, a_CellSize)), + m_OddRowOffset(0), m_CurrentCellX(9999999), // Cell coords that are definitely out of the range for normal generator, so that the first query will overwrite them m_CurrentCellZ(9999999) { @@ -26,7 +28,29 @@ cVoronoiMap::cVoronoiMap(int a_Seed, int a_CellSize) : void cVoronoiMap::SetCellSize(int a_CellSize) { + a_CellSize = std::max(a_CellSize, 2); // Cell size must be at least 2 m_CellSize = a_CellSize; + + // For compatibility with previous version, which didn't have the jitter, we set jitter here as well. + m_JitterSize = a_CellSize; +} + + + + + +void cVoronoiMap::SetJitterSize(int a_JitterSize) +{ + m_JitterSize = Clamp(a_JitterSize, 1, m_CellSize); +} + + + + + +void cVoronoiMap::SetOddRowOffset(int a_OddRowOffset) +{ + m_OddRowOffset = Clamp(a_OddRowOffset, -m_CellSize, m_CellSize); } @@ -35,8 +59,8 @@ void cVoronoiMap::SetCellSize(int a_CellSize) int cVoronoiMap::GetValueAt(int a_X, int a_Y) { - int MinDist1, MinDist2; - return GetValueAt(a_X, a_Y, MinDist1, MinDist2); + int SeedX, SeedY, MinDist2; + return GetValueAt(a_X, a_Y, SeedX, SeedY, MinDist2); } @@ -45,41 +69,47 @@ int cVoronoiMap::GetValueAt(int a_X, int a_Y) int cVoronoiMap::GetValueAt(int a_X, int a_Y, int & a_MinDist) { - int MinDist2; - return GetValueAt(a_X, a_Y, a_MinDist, MinDist2); + int SeedX, SeedY, MinDist2; + int res = GetValueAt(a_X, a_Y, SeedX, SeedY, MinDist2); + a_MinDist = (a_X - SeedX) * (a_X - SeedX) + (a_Y - SeedY) * (a_Y - SeedY); + return res; } -int cVoronoiMap::GetValueAt(int a_X, int a_Y, int & a_MinDist1, int & a_MinDist2) +int cVoronoiMap::GetValueAt( + int a_X, int a_Y, // Coords to query + int & a_NearestSeedX, int & a_NearestSeedY, // Coords of the closest cell + int & a_MinDist2 // Distance to the second closest cell +) { - // Note that due to historical reasons, the algorithm uses XZ coords, while the input uses XY coords. - // This is because the algorithm was first implemented directly in the biome generators which use MC coords. - int CellX = a_X / m_CellSize; - int CellZ = a_Y / m_CellSize; + int CellY = a_Y / m_CellSize; - UpdateCell(CellX, CellZ); + UpdateCell(CellX, CellY); // Get 5x5 neighboring cell seeds, compare distance to each. Return the value in the minumim-distance cell + int NearestSeedX = 0, NearestSeedY = 0; int MinDist = m_CellSize * m_CellSize * 16; // There has to be a cell closer than this int MinDist2 = MinDist; int res = 0; // Will be overriden for (int x = 0; x < 5; x++) { - for (int z = 0; z < 5; z++) + for (int y = 0; y < 5; y++) { - int SeedX = m_SeedX[x][z]; - int SeedZ = m_SeedZ[x][z]; + int SeedX = m_SeedX[x][y]; + int SeedY = m_SeedZ[x][y]; - int Dist = (SeedX - a_X) * (SeedX - a_X) + (SeedZ - a_Y) * (SeedZ - a_Y); + int Dist = (SeedX - a_X) * (SeedX - a_X) + (SeedY - a_Y) * (SeedY - a_Y); if (Dist < MinDist) { + NearestSeedX = SeedX; + NearestSeedY = SeedY; MinDist2 = MinDist; MinDist = Dist; - res = m_Noise3.IntNoise2DInt(x + CellX - 2, z + CellZ - 2); + res = m_Noise3.IntNoise2DInt(x + CellX - 2, y + CellY - 2); } else if (Dist < MinDist2) { @@ -88,7 +118,8 @@ int cVoronoiMap::GetValueAt(int a_X, int a_Y, int & a_MinDist1, int & a_MinDist2 } // for z } // for x - a_MinDist1 = MinDist; + a_NearestSeedX = NearestSeedX; + a_NearestSeedY = NearestSeedY; a_MinDist2 = MinDist2; return res; } @@ -97,6 +128,58 @@ int cVoronoiMap::GetValueAt(int a_X, int a_Y, int & a_MinDist1, int & a_MinDist2 +void cVoronoiMap::FindNearestSeeds( + int a_X, int a_Y, + int & a_NearestSeedX, int & a_NearestSeedY, + int & a_SecondNearestSeedX, int & a_SecondNearestSeedY +) +{ + int CellX = a_X / m_CellSize; + int CellY = a_Y / m_CellSize; + + UpdateCell(CellX, CellY); + + // Get 5x5 neighboring cell seeds, compare distance to each. Return the value in the minumim-distance cell + int NearestSeedX = 0, NearestSeedY = 0; + int SecondNearestSeedX = 0, SecondNearestSeedY = 0; + int MinDist = m_CellSize * m_CellSize * 16; // There has to be a cell closer than this + int MinDist2 = MinDist; + for (int x = 0; x < 5; x++) + { + for (int y = 0; y < 5; y++) + { + int SeedX = m_SeedX[x][y]; + int SeedY = m_SeedZ[x][y]; + + int Dist = (SeedX - a_X) * (SeedX - a_X) + (SeedY - a_Y) * (SeedY - a_Y); + if (Dist < MinDist) + { + SecondNearestSeedX = NearestSeedX; + SecondNearestSeedY = NearestSeedY; + MinDist2 = MinDist; + NearestSeedX = SeedX; + NearestSeedY = SeedY; + MinDist = Dist; + } + else if (Dist < MinDist2) + { + SecondNearestSeedX = SeedX; + SecondNearestSeedY = SeedY; + MinDist2 = Dist; + } + } // for z + } // for x + + a_NearestSeedX = NearestSeedX; + a_NearestSeedY = NearestSeedY; + a_SecondNearestSeedX = SecondNearestSeedX; + a_SecondNearestSeedY = SecondNearestSeedY; +} + + + + + void cVoronoiMap::UpdateCell(int a_CellX, int a_CellZ) { // If the specified cell is currently cached, bail out: @@ -111,12 +194,13 @@ void cVoronoiMap::UpdateCell(int a_CellX, int a_CellZ) for (int x = 0; x < 5; x++) { int BaseX = (NoiseBaseX + x) * m_CellSize; + int OddRowOffset = ((NoiseBaseX + x) & 0x01) * m_OddRowOffset; for (int z = 0; z < 5; z++) { - int OffsetX = (m_Noise1.IntNoise2DInt(NoiseBaseX + x, NoiseBaseZ + z) / 8) % m_CellSize; - int OffsetZ = (m_Noise2.IntNoise2DInt(NoiseBaseX + x, NoiseBaseZ + z) / 8) % m_CellSize; + int OffsetX = (m_Noise1.IntNoise2DInt(NoiseBaseX + x, NoiseBaseZ + z) / 8) % m_JitterSize; + int OffsetZ = (m_Noise2.IntNoise2DInt(NoiseBaseX + x, NoiseBaseZ + z) / 8) % m_JitterSize; m_SeedX[x][z] = BaseX + OffsetX; - m_SeedZ[x][z] = (NoiseBaseZ + z) * m_CellSize + OffsetZ; + m_SeedZ[x][z] = (NoiseBaseZ + z) * m_CellSize + OddRowOffset + OffsetZ; } // for z } // for x m_CurrentCellX = a_CellX; diff --git a/src/VoronoiMap.h b/src/VoronoiMap.h index 84cf206e9..dfb11e9ce 100644 --- a/src/VoronoiMap.h +++ b/src/VoronoiMap.h @@ -18,19 +18,40 @@ class cVoronoiMap { public: - cVoronoiMap(int a_Seed, int a_CellSize = 128); + cVoronoiMap(int a_Seed, int a_CellSize = 128, int a_JitterSize = 128); - /// Sets the cell size used for generating the Voronoi seeds + /** Sets both the cell size and jitter size used for generating the Voronoi seeds. */ void SetCellSize(int a_CellSize); + + /** Sets the jitter size. Clamps it to current cell size. */ + void SetJitterSize(int a_JitterSize); + + /** Sets the offset that is added to each odd row of cells. + This offset makes the voronoi cells align to a non-grid. + Clamps the value to [-m_CellSize, +m_CellSize]. */ + void SetOddRowOffset(int a_OddRowOffset); - /// Returns the value in the cell into which the specified point lies + /** Returns the value in the cell into which the specified point lies. */ int GetValueAt(int a_X, int a_Y); - /// Returns the value in the cell into which the specified point lies, and the distance to the nearest Voronoi seed + /** Returns the value in the cell into which the specified point lies, + and the distance to the nearest Voronoi seed. */ int GetValueAt(int a_X, int a_Y, int & a_MinDistance); - /// Returns the value in the cell into which the specified point lies, and the distances to the 2 nearest Voronoi seeds. Uses a cache - int GetValueAt(int a_X, int a_Y, int & a_MinDistance1, int & a_MinDistance2); + /** Returns the value in the cell into which the specified point lies, + and the distances to the 2 nearest Voronoi seeds. Uses a cache. */ + int GetValueAt( + int a_X, int a_Y, // Coords to query + int & a_NearestSeedX, int & a_NearestSeedY, // Coords of the closest cell's seed + int & a_MinDist2 // Distance to the second closest cell's seed + ); + + /** Finds the nearest and second nearest seeds, returns their coords. */ + void FindNearestSeeds( + int a_X, int a_Y, + int & a_NearestSeedX, int & a_NearestSeedY, + int & a_SecondNearestSeedX, int & a_SecondNearestSeedY + ); protected: /// The noise used for generating Voronoi seeds @@ -38,8 +59,17 @@ protected: cNoise m_Noise2; cNoise m_Noise3; - /// Size of the Voronoi cells (avg X/Y distance between the seeds) + /** Size of the Voronoi cells (avg X/Y distance between the seeds). Expected to be at least 2. */ int m_CellSize; + + /** The amount that the cell seeds may be offset from the grid. + Expected to be at least 1 and less than m_CellSize. */ + int m_JitterSize; + + /** The constant amount that the cell seeds of every odd row will be offset from the grid. + This allows us to have non-rectangular grids. + Expected to be between -m_CellSize and +m_CellSize. */ + int m_OddRowOffset; /** The X coordinate of the currently cached cell neighborhood */ int m_CurrentCellX; diff --git a/src/World.h b/src/World.h index 391124a03..eee0ced54 100644 --- a/src/World.h +++ b/src/World.h @@ -188,6 +188,9 @@ public: /** Returns true if the world is in Adventure mode */ bool IsGameModeAdventure(void) const { return (m_GameMode == gmAdventure); } + /** Returns true if the world is in Spectator mode */ + bool IsGameModeSpectator(void) const { return (m_GameMode == gmSpectator); } + bool IsPVPEnabled(void) const { return m_bEnabledPVP; } bool IsDeepSnowEnabled(void) const { return m_IsDeepSnowEnabled; } |