summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorHowaner <franzi.moos@googlemail.com>2014-09-19 14:31:18 +0200
committerHowaner <franzi.moos@googlemail.com>2014-09-19 14:31:18 +0200
commit4398156b2e56ccfcb41f750906501ecf446be045 (patch)
treedc1c070c6c7490619d8b05e30ace2c959f085bb2 /src
parentDerp (diff)
parentQtBiomeVisualiser: More gcc fixes. (diff)
downloadcuberite-4398156b2e56ccfcb41f750906501ecf446be045.tar
cuberite-4398156b2e56ccfcb41f750906501ecf446be045.tar.gz
cuberite-4398156b2e56ccfcb41f750906501ecf446be045.tar.bz2
cuberite-4398156b2e56ccfcb41f750906501ecf446be045.tar.lz
cuberite-4398156b2e56ccfcb41f750906501ecf446be045.tar.xz
cuberite-4398156b2e56ccfcb41f750906501ecf446be045.tar.zst
cuberite-4398156b2e56ccfcb41f750906501ecf446be045.zip
Diffstat (limited to 'src')
-rw-r--r--src/Blocks/BlockHandler.cpp2
-rw-r--r--src/ClientHandle.cpp12
-rw-r--r--src/ClientHandle.h14
-rw-r--r--src/Defines.h2
-rw-r--r--src/Entities/Player.cpp29
-rw-r--r--src/Entities/Player.h3
-rw-r--r--src/Generating/BioGen.cpp148
-rw-r--r--src/Protocol/Protocol17x.cpp13
-rw-r--r--src/Protocol/ProtocolRecognizer.cpp2
-rw-r--r--src/Root.cpp4
-rw-r--r--src/Server.cpp7
-rw-r--r--src/Server.h8
-rw-r--r--src/StringUtils.cpp28
-rw-r--r--src/StringUtils.h5
-rw-r--r--src/VoronoiMap.cpp126
-rw-r--r--src/VoronoiMap.h44
-rw-r--r--src/World.h3
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; }