1
0
Fork 0

Merge branch 'master' into MobSpawner

This commit is contained in:
Howaner 2014-09-19 17:33:22 +02:00
commit 09ca7d144a
25 changed files with 294 additions and 104 deletions

View File

@ -1502,7 +1502,7 @@ function OnPlayerJoined(a_Player)
-- Test composite chat chaining:
a_Player:SendMessage(cCompositeChat()
:AddTextPart("Hello, ")
:AddUrlPart(a_Player:GetName(), "www.mc-server.org", "u@2")
:AddUrlPart(a_Player:GetName(), "http://www.mc-server.org", "u@2")
:AddSuggestCommandPart(", and welcome.", "/help", "u")
:AddRunCommandPart(" SetDay", "/time set 0")
)

View File

@ -1,6 +1,7 @@
#pragma once
#include <QWidget>
#include <memory>
#include "ChunkCache.h"
#include "ChunkSource.h"

View File

@ -3,6 +3,7 @@
#include <QObject>
#include <QCache>
#include <QMutex>
#include <memory>

View File

@ -1,6 +1,8 @@
#pragma once
#include <QObject>
#include <QRunnable>
#include <memory>

View File

@ -120,8 +120,8 @@ static void biomesToImage(cChunkDef::BiomeMap & a_Biomes, Chunk::Image & a_Image
{
// Make sure the two arrays are of the same size, compile-time.
// Note that a_Image is actually 4 items per pixel, so the array is 4 times bigger:
static const char Check1[4 * ARRAYCOUNT(a_Biomes) - ARRAYCOUNT(a_Image) + 1];
static const char Check2[ARRAYCOUNT(a_Image) - 4 * ARRAYCOUNT(a_Biomes) + 1];
static const char Check1[4 * ARRAYCOUNT(a_Biomes) - ARRAYCOUNT(a_Image) + 1] = {};
static const char Check2[ARRAYCOUNT(a_Image) - 4 * ARRAYCOUNT(a_Biomes) + 1] = {};
// Convert the biomes into color:
for (size_t i = 0; i < ARRAYCOUNT(a_Biomes); i++)

View File

@ -1,5 +1,6 @@
#pragma once
#include <QString>
#include <QMutex>
#include "Chunk.h"

View File

@ -55,5 +55,6 @@ INCLUDEPATH += $$_PRO_FILE_PWD_ \
$$_PRO_FILE_PWD_/../../lib
CONFIG += C++11

@ -1 +1 @@
Subproject commit 203c2fb68bbf871eaf4ca98756a113d74d620dea
Subproject commit 55edadd56d0d6f506954ad00c3b9a5d425814a2f

View File

@ -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:

View File

@ -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();

View File

@ -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.

View File

@ -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

View File

@ -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);
@ -1340,6 +1353,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;
@ -1500,6 +1514,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;
@ -1786,7 +1805,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;
}

View File

@ -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 */

View File

@ -12,72 +12,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:
@ -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;
}

View File

@ -100,6 +100,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)
{

View File

@ -27,7 +27,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
{
}

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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)

View File

@ -59,8 +59,8 @@ void cVoronoiMap::SetOddRowOffset(int a_OddRowOffset)
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);
}
@ -69,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)
{
@ -112,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;
}
@ -121,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:

View File

@ -40,7 +40,18 @@ public:
/** 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);
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

View File

@ -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; }