Minecraft Consoles latest changes + Better shadow for water in Extra mode

Culling for water need more work in extra graphics mode.
This commit is contained in:
GabsPuNs
2026-04-08 23:47:27 -04:00
parent 23102c65b6
commit 4dfedbffd1
12 changed files with 453 additions and 102 deletions

View File

@@ -326,7 +326,10 @@ bool TileRenderer::tesselateInWorld( Tile* tt, int x, int y, int z, int forceDat
retVal = tesselateQuartzInWorld(tt, x, y, z);
break;
case Tile::SHAPE_WATER:
retVal = tesselateWaterInWorld( tt, x, y, z );
if (Minecraft::GetInstance()->options->mipmapsBlend)
retVal = tesselateWaterInWorldAO( tt, x, y, z );
else
retVal = tesselateWaterInWorld( tt, x, y, z );
break;
case Tile::SHAPE_CACTUS:
retVal = tesselateCactusInWorld( tt, x, y, z );
@@ -4755,6 +4758,209 @@ bool TileRenderer::tesselateWaterInWorld(Tile* tt, int x, int y, int z)
return changed;
}
bool TileRenderer::tesselateWaterInWorldAO(Tile* tt, int x, int y, int z)
{
Tesselator* t = Tesselator::getInstance();
int col = tt->getColor(level, x, y, z);
float r = ((col >> 16) & 0xff) / 255.0f;
float g = ((col >> 8) & 0xff) / 255.0f;
float b = (col & 0xff) / 255.0f;
bool up = tt->shouldRenderFace(level, x, y + 1, z, 1);
bool down = tt->shouldRenderFace(level, x, y - 1, z, 0);
bool dirs[4];
dirs[0] = tt->shouldRenderFace( level, x, y, z - 1, 2 );
dirs[1] = tt->shouldRenderFace( level, x, y, z + 1, 3 );
dirs[2] = tt->shouldRenderFace( level, x - 1, y, z, 4 );
dirs[3] = tt->shouldRenderFace( level, x + 1, y, z, 5 );
if (!up && !down && !dirs[0] && !dirs[1] && !dirs[2] && !dirs[3])
return false;
bool changed = false;
Material* m = tt->material;
int data = level->getData(x, y, z);
float h0 = getWaterHeight(x, y, z, m);
float h1 = getWaterHeight(x, y, z + 1, m);
float h2 = getWaterHeight(x + 1, y, z + 1, m);
float h3 = getWaterHeight(x + 1, y, z, m);
float maxh = h0;
if (h1 > maxh) maxh = h1;
if (h2 > maxh) maxh = h2;
if (h3 > maxh) maxh = h3;
// 4J - added. Farm tiles often found beside water, but they consider themselves non-solid as they only extend up to 15.0f / 16.0f.
// If the max height of this water is below that level, don't bother rendering sides bordering onto farmland.
if (maxh <= ( 15.0f / 16.0f ))
{
if (level->getTile(x, y, z - 1) == Tile::farmland_Id) dirs[0] = false;
if (level->getTile(x, y, z + 1) == Tile::farmland_Id) dirs[1] = false;
if (level->getTile(x - 1, y, z) == Tile::farmland_Id) dirs[2] = false;
if (level->getTile(x + 1, y, z) == Tile::farmland_Id) dirs[3] = false;
}
constexpr float EPS = 0.0001f;
if (noCulling || up)
{
changed = true;
Icon* tex = getTexture(tt, 1, data);
float angle = static_cast<float>(LiquidTile::getSlopeAngle(level, x, y, z, m));
if (angle > -999.0f) tex = getTexture(tt, 2, data);
h0 -= EPS; h1 -= EPS; h2 -= EPS; h3 -= EPS;
float u0, v0, u1, v1, u2, v2, u3, v3;
if (angle >= -999.0f)
{
float cc = 8.0f;
float fsin = Mth::sin(angle) * 0.25f;
float fcos = Mth::cos(angle) * 0.25f;
u0 = tex->getU(cc + (-fcos - fsin) * SharedConstants::WORLD_RESOLUTION);
v0 = tex->getV(cc + (fsin - fcos) * SharedConstants::WORLD_RESOLUTION);
u1 = tex->getU(cc + (fsin - fcos) * SharedConstants::WORLD_RESOLUTION);
v1 = tex->getV(cc + (fcos + fsin) * SharedConstants::WORLD_RESOLUTION);
u2 = tex->getU(cc + (fcos + fsin) * SharedConstants::WORLD_RESOLUTION);
v2 = tex->getV(cc + (fcos - fsin) * SharedConstants::WORLD_RESOLUTION);
u3 = tex->getU(cc + (fcos - fsin) * SharedConstants::WORLD_RESOLUTION);
v3 = tex->getV(cc + (-fcos - fsin) * SharedConstants::WORLD_RESOLUTION);
}
else
{
u0 = tex->getU(0.0f, true); v0 = tex->getV(0.0f, true);
u1 = u0; v1 = tex->getV(SharedConstants::WORLD_RESOLUTION, true);
u2 = tex->getU(SharedConstants::WORLD_RESOLUTION, true); v2 = v1;
u3 = u2; v3 = v0;
}
int pY = y + 1;
int centerColor = tt->getLightColor(level, x, pY, z);
float ll0Y0 = tt->getShadeBrightness(level, x, pY, z);
auto getWaterLight = [&](int sx, int sy, int sz) -> int {
if (level->isSolidBlockingTile(sx, sy, sz)) return centerColor;
return tt->getLightColor(level, sx, sy, sz);
};
auto getWaterBr = [&](int sx, int sy, int sz) -> float {
if (level->isSolidBlockingTile(sx, sy, sz)) return ll0Y0;
return tt->getShadeBrightness(level, sx, sy, sz);
};
int ccxY0 = getWaterLight(x - 1, pY, z);
int ccXY0 = getWaterLight(x + 1, pY, z);
int cc0Yz = getWaterLight(x, pY, z - 1);
int cc0YZ = getWaterLight(x, pY, z + 1);
float llxY0 = getWaterBr(x - 1, pY, z);
float llXY0 = getWaterBr(x + 1, pY, z);
float ll0Yz = getWaterBr(x, pY, z - 1);
float ll0YZ = getWaterBr(x, pY, z + 1);
float llxYz, llXYz, llxYZ, llXYZ;
int ccxYz, ccXYz, ccxYZ, ccXYZ;
llxYz = getWaterBr(x - 1, pY, z - 1); ccxYz = getWaterLight(x - 1, pY, z - 1);
llXYz = getWaterBr(x + 1, pY, z - 1); ccXYz = getWaterLight(x + 1, pY, z - 1);
llxYZ = getWaterBr(x - 1, pY, z + 1); ccxYZ = getWaterLight(x - 1, pY, z + 1);
llXYZ = getWaterBr(x + 1, pY, z + 1); ccXYZ = getWaterLight(x + 1, pY, z + 1);
float b0 = (llxY0 + llxYz + ll0Y0 + ll0Yz) / 4.0f;
int c0 = blend(ccxY0, ccxYz, centerColor, cc0Yz);
float b1 = (llxYZ + llxY0 + ll0YZ + ll0Y0) / 4.0f;
int c1 = blend(ccxYZ, ccxY0, cc0YZ, centerColor);
float b2 = (ll0YZ + ll0Y0 + llXYZ + llXY0) / 4.0f;
int c2 = blend(cc0YZ, centerColor, ccXYZ, ccXY0);
float b3 = (ll0Y0 + ll0Yz + llXY0 + llXYz) / 4.0f;
int c3 = blend(centerColor, cc0Yz, ccXY0, ccXYz);
t->tex2(c0); t->color(r * b0, g * b0, b * b0);
t->vertexUV((float)x, (float)(y + h0), (float)z, u0, v0);
t->tex2(c1); t->color(r * b1, g * b1, b * b1);
t->vertexUV((float)x, (float)(y + h1), (float)(z + 1), u1, v1);
t->tex2(c2); t->color(r * b2, g * b2, b * b2);
t->vertexUV((float)(x + 1), (float)(y + h2), (float)(z + 1), u2, v2);
t->tex2(c3); t->color(r * b3, g * b3, b * b3);
t->vertexUV((float)(x + 1), (float)(y + h3), (float)z, u3, v3);
if (static_cast<LiquidTile*>(tt)->LiquidTile::shouldRenderBackwardUpFace(level, x, y + 1, z))
{
t->tex2(c0); t->color(r * b0, g * b0, b * b0);
t->vertexUV((float)x, (float)(y + h0), (float)z, u0, v0);
t->tex2(c3); t->color(r * b3, g * b3, b * b3);
t->vertexUV((float)(x + 1), (float)(y + h3), (float)z, u3, v3);
t->tex2(c2); t->color(r * b2, g * b2, b * b2);
t->vertexUV((float)(x + 1), (float)(y + h2), (float)(z + 1), u2, v2);
t->tex2(c1); t->color(r * b1, g * b1, b * b1);
t->vertexUV((float)x, (float)(y + h1), (float)(z + 1), u1, v1);
}
}
if (noCulling || down)
{
t->tex2(getLightColor(tt, level, x, y - 1, z));
float bl = tt->getShadeBrightness(level, x, y - 1, z);
t->color(r * 0.5f * bl, g * 0.5f * bl, b * 0.5f * bl);
renderFaceDown(tt, x, y + EPS, z, getTexture(tt, 0));
changed = true;
}
for (int face = 0; face < 4; face++)
{
if (noCulling || dirs[face])
{
int xt = x, zt = z;
float x0, z0, x1, z1, hh0, hh1;
if (face == 0) { zt--; x1 = x + 1.0f; z1 = z + EPS; x0 = x; z0 = z + EPS; hh0 = h0; hh1 = h3; }
else if (face == 1) { zt++; x1 = x; z1 = z + 1.0f - EPS; x0 = x + 1.0f; z0 = z + 1.0f - EPS; hh0 = h2; hh1 = h1; }
else if (face == 2) { xt--; x1 = x + EPS; z1 = z; x0 = x + EPS; z0 = z + 1.0f; hh0 = h1; hh1 = h0; }
else { xt++; x0 = x + 1.0f - EPS; z1 = z + 1.0f; x1 = x + 1.0f - EPS; z0 = z; hh0 = h3; hh1 = h2; }
changed = true;
Icon* tex = getTexture(tt, face + 2, data);
float side_v_h0 = tex->getV((1.0f - hh0) * 8.0f);
float side_v_h1 = tex->getV((1.0f - hh1) * 8.0f);
float side_u0 = tex->getU(0.0f, true), side_u1 = tex->getU(8.0f, true), side_v1 = tex->getV(8.0f, true);
int lightTop = getLightColor(tt, level, xt, y + 1, zt);
int lightBot = getLightColor(tt, level, xt, y, zt);
float br = (face < 2) ? 0.8f : 0.6f;
t->tex2(lightTop); t->color(br * r, br * g, br * b);
t->vertexUV(x0, y + hh0, z0, side_u0, side_v_h0);
t->vertexUV(x1, y + hh1, z1, side_u1, side_v_h1);
t->tex2(lightBot);
t->vertexUV(x1, (float)y, z1, side_u1, side_v1);
t->vertexUV(x0, (float)y, z0, side_u0, side_v1);
t->tex2(lightBot);
t->vertexUV(x0, (float)y, z0, side_u0, side_v1);
t->vertexUV(x1, (float)y, z1, side_u1, side_v1);
t->tex2(lightTop);
t->vertexUV(x1, y + hh1, z1, side_u1, side_v_h1);
t->vertexUV(x0, y + hh0, z0, side_u0, side_v_h0);
}
}
tileShapeY0 = 0.0f;
tileShapeY1 = 1.0f;
return changed;
}
float TileRenderer::getWaterHeight( int x, int y, int z, Material* m )
{
int count = 0;

View File

@@ -150,6 +150,7 @@ private:
void tesselateRowTexture( Tile* tt, int data, float x, float y, float z );
bool tesselateWaterInWorld( Tile* tt, int x, int y, int z );
bool tesselateWaterInWorldAO( Tile* tt, int x, int y, int z );
private:
float getWaterHeight( int x, int y, int z, Material* m );
public:

View File

@@ -159,7 +159,7 @@ namespace ServerRuntime
}
IServerCliCommand *command = m_registry->FindMutable(parsed.tokens[0]);
if (command == NULL)
if (command == nullptr)
{
LogWarn("Unknown command: " + parsed.tokens[0]);
return false;
@@ -170,7 +170,7 @@ namespace ServerRuntime
void ServerCliEngine::BuildCompletions(const std::string &line, std::vector<std::string> *out) const
{
if (out == NULL)
if (out == nullptr)
{
return;
}
@@ -211,7 +211,7 @@ namespace ServerRuntime
else
{
const IServerCliCommand *command = m_registry->Find(commandToken);
if (command != NULL)
if (command != nullptr)
{
command->Complete(context, this, out);
}
@@ -254,13 +254,13 @@ namespace ServerRuntime
{
std::vector<std::string> result;
MinecraftServer *server = MinecraftServer::getInstance();
if (server == NULL)
if (server == nullptr)
{
return result;
}
PlayerList *players = server->getPlayers();
if (players == NULL)
if (players == nullptr)
{
return result;
}
@@ -268,7 +268,7 @@ namespace ServerRuntime
for (size_t i = 0; i < players->players.size(); ++i)
{
std::shared_ptr<ServerPlayer> player = players->players[i];
if (player != NULL)
if (player != nullptr)
{
result.push_back(StringUtils::WideToUtf8(player->getName()));
}
@@ -280,13 +280,13 @@ namespace ServerRuntime
std::shared_ptr<ServerPlayer> ServerCliEngine::FindPlayerByNameUtf8(const std::string &name) const
{
MinecraftServer *server = MinecraftServer::getInstance();
if (server == NULL)
if (server == nullptr)
{
return nullptr;
}
PlayerList *players = server->getPlayers();
if (players == NULL)
if (players == nullptr)
{
return nullptr;
}
@@ -295,7 +295,7 @@ namespace ServerRuntime
for (size_t i = 0; i < players->players.size(); ++i)
{
std::shared_ptr<ServerPlayer> player = players->players[i];
if (player != NULL && equalsIgnoreCase(player->getName(), target))
if (player != nullptr && equalsIgnoreCase(player->getName(), target))
{
return player;
}
@@ -345,28 +345,28 @@ namespace ServerRuntime
return GameType::CREATIVE;
}
char *end = NULL;
char *end = nullptr;
long id = strtol(lowered.c_str(), &end, 10);
if (end != NULL && *end == 0)
if (end != nullptr && *end == 0)
{
// Numeric fallback supports extended ids handled by level settings.
return LevelSettings::validateGameType((int)id);
}
return NULL;
return nullptr;
}
bool ServerCliEngine::DispatchWorldCommand(EGameCommand command, byteArray commandData, const std::shared_ptr<CommandSender> &sender) const
{
MinecraftServer *server = MinecraftServer::getInstance();
if (server == NULL)
if (server == nullptr)
{
LogWarn("MinecraftServer instance is not available.");
return false;
}
CommandDispatcher *dispatcher = server->getCommandDispatcher();
if (dispatcher == NULL)
if (dispatcher == nullptr)
{
LogWarn("Command dispatcher is not available.");
return false;

View File

@@ -14,7 +14,7 @@ namespace
bool UseStreamInputMode()
{
const char *mode = getenv("SERVER_CLI_INPUT_MODE");
if (mode != NULL)
if (mode != nullptr)
{
return _stricmp(mode, "stream") == 0
|| _stricmp(mode, "stdin") == 0;
@@ -25,7 +25,7 @@ namespace
int WaitForStdinReadable(HANDLE stdinHandle, DWORD waitMs)
{
if (stdinHandle == NULL || stdinHandle == INVALID_HANDLE_VALUE)
if (stdinHandle == nullptr || stdinHandle == INVALID_HANDLE_VALUE)
{
return -1;
}
@@ -34,7 +34,7 @@ namespace
if (fileType == FILE_TYPE_PIPE)
{
DWORD available = 0;
if (!PeekNamedPipe(stdinHandle, NULL, 0, NULL, &available, NULL))
if (!PeekNamedPipe(stdinHandle, nullptr, 0, nullptr, &available, nullptr))
{
return -1;
}
@@ -64,11 +64,11 @@ namespace
namespace ServerRuntime
{
// C-style completion callback bridge requires a static instance pointer.
ServerCliInput *ServerCliInput::s_instance = NULL;
ServerCliInput *ServerCliInput::s_instance = nullptr;
ServerCliInput::ServerCliInput()
: m_running(false)
, m_engine(NULL)
, m_engine(nullptr)
{
}
@@ -79,7 +79,7 @@ namespace ServerRuntime
void ServerCliInput::Start(ServerCliEngine *engine)
{
if (engine == NULL || m_running.exchange(true))
if (engine == nullptr || m_running.exchange(true))
{
return;
}
@@ -107,14 +107,14 @@ namespace ServerRuntime
CancelSynchronousIo((HANDLE)m_inputThread.native_handle());
m_inputThread.join();
}
linenoiseSetCompletionCallback(NULL);
linenoiseSetCompletionCallback(nullptr);
if (s_instance == this)
{
s_instance = NULL;
s_instance = nullptr;
}
m_engine = NULL;
m_engine = nullptr;
LogInfo("console", "CLI input thread stopped.");
}
@@ -143,9 +143,9 @@ namespace ServerRuntime
while (m_running)
{
char *line = linenoise("server> ");
if (line == NULL)
if (line == nullptr)
{
// NULL is expected on stop request (or Ctrl+C inside linenoise).
// nullptr is expected on stop request (or Ctrl+C inside linenoise).
if (!m_running)
{
break;
@@ -166,7 +166,7 @@ namespace ServerRuntime
void ServerCliInput::RunStreamInputLoop()
{
HANDLE stdinHandle = GetStdHandle(STD_INPUT_HANDLE);
if (stdinHandle == NULL || stdinHandle == INVALID_HANDLE_VALUE)
if (stdinHandle == nullptr || stdinHandle == INVALID_HANDLE_VALUE)
{
LogWarn("console", "stream input mode requested but STDIN handle is unavailable; falling back to linenoise.");
RunLinenoiseLoop();
@@ -190,7 +190,7 @@ namespace ServerRuntime
char ch = 0;
DWORD bytesRead = 0;
if (!ReadFile(stdinHandle, &ch, 1, &bytesRead, NULL) || bytesRead == 0)
if (!ReadFile(stdinHandle, &ch, 1, &bytesRead, nullptr) || bytesRead == 0)
{
Sleep(10);
continue;
@@ -249,7 +249,7 @@ namespace ServerRuntime
void ServerCliInput::EnqueueLine(const char *line)
{
if (line == NULL || line[0] == 0 || m_engine == NULL)
if (line == nullptr || line[0] == 0 || m_engine == nullptr)
{
return;
}
@@ -262,7 +262,7 @@ namespace ServerRuntime
void ServerCliInput::CompletionThunk(const char *line, linenoiseCompletions *completions)
{
// Static thunk forwards callback into instance state.
if (s_instance != NULL)
if (s_instance != nullptr)
{
s_instance->BuildCompletions(line, completions);
}
@@ -270,7 +270,7 @@ namespace ServerRuntime
void ServerCliInput::BuildCompletions(const char *line, linenoiseCompletions *completions)
{
if (line == NULL || completions == NULL || m_engine == NULL)
if (line == nullptr || completions == nullptr || m_engine == nullptr)
{
return;
}

View File

@@ -52,7 +52,7 @@ namespace ServerRuntime
auto it = m_lookup.find(key);
if (it == m_lookup.end())
{
return NULL;
return nullptr;
}
return it->second;
}
@@ -63,7 +63,7 @@ namespace ServerRuntime
auto it = m_lookup.find(key);
if (it == m_lookup.end())
{
return NULL;
return nullptr;
}
return it->second;
}

View File

@@ -47,7 +47,7 @@ namespace ServerRuntime
static void ResetConnectionLogEntry(ConnectionLogEntry *entry)
{
if (entry == NULL)
if (entry == nullptr)
{
return;
}
@@ -58,7 +58,7 @@ namespace ServerRuntime
static std::string NormalizeRemoteIp(const char *ip)
{
if (ip == NULL || ip[0] == 0)
if (ip == nullptr || ip[0] == 0)
{
return std::string("unknown");
}
@@ -80,7 +80,7 @@ namespace ServerRuntime
// Default to the main app channel when the caller does not provide a source tag.
static const char *NormalizeClientLogSource(const char *source)
{
if (source == NULL || source[0] == 0)
if (source == nullptr || source[0] == 0)
{
return "app";
}
@@ -101,7 +101,7 @@ namespace ServerRuntime
// Split one debug payload into individual lines so each line becomes a prompt-safe server log entry.
static void ForwardClientDebugMessage(const char *source, const char *message)
{
if (message == NULL || message[0] == 0)
if (message == nullptr || message[0] == 0)
{
return;
}
@@ -131,7 +131,7 @@ namespace ServerRuntime
// Share the same formatting path for app, user, and legacy debug-spew forwards.
static void ForwardFormattedClientDebugLogV(const char *source, const char *format, va_list args)
{
if (!IsDedicatedServerLoggingEnabled() || format == NULL || format[0] == 0)
if (!IsDedicatedServerLoggingEnabled() || format == nullptr || format[0] == 0)
{
return;
}
@@ -376,7 +376,7 @@ namespace ServerRuntime
*/
bool TryGetConnectionRemoteIp(unsigned char smallId, std::string *outIp)
{
if (!IsDedicatedServerLoggingEnabled() || outIp == NULL)
if (!IsDedicatedServerLoggingEnabled() || outIp == nullptr)
{
return false;
}

View File

@@ -117,7 +117,7 @@ static int ClampInt(int value, int minValue, int maxValue)
static bool TryParseBool(const std::string &value, bool *outValue)
{
if (outValue == NULL)
if (outValue == nullptr)
{
return false;
}
@@ -138,7 +138,7 @@ static bool TryParseBool(const std::string &value, bool *outValue)
static bool TryParseInt(const std::string &value, int *outValue)
{
if (outValue == NULL)
if (outValue == nullptr)
{
return false;
}
@@ -149,7 +149,7 @@ static bool TryParseInt(const std::string &value, int *outValue)
return false;
}
char *end = NULL;
char *end = nullptr;
long parsed = strtol(trimmed.c_str(), &end, 10);
if (end == trimmed.c_str() || *end != 0)
{
@@ -162,7 +162,7 @@ static bool TryParseInt(const std::string &value, int *outValue)
static bool TryParseInt64(const std::string &value, __int64 *outValue)
{
if (outValue == NULL)
if (outValue == nullptr)
{
return false;
}
@@ -173,7 +173,7 @@ static bool TryParseInt64(const std::string &value, __int64 *outValue)
return false;
}
char *end = NULL;
char *end = nullptr;
__int64 parsed = _strtoi64(trimmed.c_str(), &end, 10);
if (end == trimmed.c_str() || *end != 0)
{
@@ -265,7 +265,7 @@ static std::string NormalizeSaveId(const std::string &source)
static void ApplyDefaultServerProperties(std::unordered_map<std::string, std::string> *properties)
{
if (properties == NULL)
if (properties == nullptr)
{
return;
}
@@ -288,13 +288,13 @@ static void ApplyDefaultServerProperties(std::unordered_map<std::string, std::st
*/
static bool ReadServerPropertiesFile(const char *filePath, std::unordered_map<std::string, std::string> *properties, int *outParsedCount)
{
if (properties == NULL)
if (properties == nullptr)
{
return false;
}
std::string text;
if (filePath == NULL || !FileUtils::ReadTextFile(filePath, &text))
if (filePath == nullptr || !FileUtils::ReadTextFile(filePath, &text))
{
return false;
}
@@ -373,7 +373,7 @@ static bool ReadServerPropertiesFile(const char *filePath, std::unordered_map<st
start = nextStart;
}
if (outParsedCount != NULL)
if (outParsedCount != nullptr)
{
*outParsedCount = parsedCount;
}
@@ -390,7 +390,7 @@ static bool ReadServerPropertiesFile(const char *filePath, std::unordered_map<st
*/
static bool WriteServerPropertiesFile(const char *filePath, const std::unordered_map<std::string, std::string> &properties)
{
if (filePath == NULL)
if (filePath == nullptr)
{
return false;
}
@@ -428,7 +428,7 @@ static bool ReadNormalizedBoolProperty(
if (raw != normalized)
{
(*properties)[key] = normalized;
if (shouldWrite != NULL)
if (shouldWrite != nullptr)
{
*shouldWrite = true;
}
@@ -457,7 +457,7 @@ static int ReadNormalizedIntProperty(
if (raw != normalized)
{
(*properties)[key] = normalized;
if (shouldWrite != NULL)
if (shouldWrite != nullptr)
{
*shouldWrite = true;
}
@@ -486,7 +486,7 @@ static std::string ReadNormalizedStringProperty(
if (value != (*properties)[key])
{
(*properties)[key] = value;
if (shouldWrite != NULL)
if (shouldWrite != nullptr)
{
*shouldWrite = true;
}
@@ -507,7 +507,7 @@ static bool ReadNormalizedOptionalInt64Property(
if ((*properties)[key] != "")
{
(*properties)[key] = "";
if (shouldWrite != NULL)
if (shouldWrite != nullptr)
{
*shouldWrite = true;
}
@@ -519,7 +519,7 @@ static bool ReadNormalizedOptionalInt64Property(
if (!TryParseInt64(raw, &parsed))
{
(*properties)[key] = "";
if (shouldWrite != NULL)
if (shouldWrite != nullptr)
{
*shouldWrite = true;
}
@@ -530,13 +530,13 @@ static bool ReadNormalizedOptionalInt64Property(
if (raw != normalized)
{
(*properties)[key] = normalized;
if (shouldWrite != NULL)
if (shouldWrite != nullptr)
{
*shouldWrite = true;
}
}
if (outValue != NULL)
if (outValue != nullptr)
{
*outValue = parsed;
}
@@ -560,7 +560,7 @@ static EServerLogLevel ReadNormalizedLogLevelProperty(
if (raw != normalized)
{
(*properties)[key] = normalized;
if (shouldWrite != NULL)
if (shouldWrite != nullptr)
{
*shouldWrite = true;
}
@@ -594,13 +594,13 @@ static std::string ReadNormalizedLevelTypeProperty(
if (raw != normalized)
{
(*properties)[key] = normalized;
if (shouldWrite != NULL)
if (shouldWrite != nullptr)
{
*shouldWrite = true;
}
}
if (outIsFlat != NULL)
if (outIsFlat != nullptr)
{
*outIsFlat = isFlat;
}
@@ -658,7 +658,7 @@ static int WorldSizeToHellScale(int worldSize)
static bool TryParseWorldSize(const std::string &lowered, int *outWorldSize)
{
if (outWorldSize == NULL)
if (outWorldSize == nullptr)
{
return false;
}
@@ -712,17 +712,17 @@ static int ReadNormalizedWorldSizeProperty(
if (raw != normalized)
{
(*properties)[key] = normalized;
if (shouldWrite != NULL)
if (shouldWrite != nullptr)
{
*shouldWrite = true;
}
}
if (outXzChunks != NULL)
if (outXzChunks != nullptr)
{
*outXzChunks = WorldSizeToXzChunks(worldSize);
}
if (outHellScale != NULL)
if (outHellScale != nullptr)
{
*outHellScale = WorldSizeToHellScale(worldSize);
}

View File

@@ -28,6 +28,7 @@
#include "../../Minecraft.World/TilePos.h"
#include "../../Minecraft.World/compression.h"
#include "../../Minecraft.World/OldChunkStorage.h"
#include "../../Minecraft.World/ConsoleSaveFileOriginal.h"
#include "../../Minecraft.World/net.minecraft.world.level.tile.h"
#include "../../Minecraft.World/Random.h"
@@ -183,10 +184,10 @@ using ServerRuntime::WorldBootstrapResult;
static bool ParseIntArg(const char *value, int *outValue)
{
if (value == NULL || *value == 0)
if (value == nullptr || *value == 0)
return false;
char *end = NULL;
char *end = nullptr;
long parsed = strtol(value, &end, 10);
if (end == value || *end != 0)
return false;
@@ -197,10 +198,10 @@ static bool ParseIntArg(const char *value, int *outValue)
static bool ParseInt64Arg(const char *value, __int64 *outValue)
{
if (value == NULL || *value == 0)
if (value == nullptr || *value == 0)
return false;
char *end = NULL;
char *end = nullptr;
__int64 parsed = _strtoi64(value, &end, 10);
if (end == value || *end != 0)
return false;
@@ -277,9 +278,9 @@ static bool ParseCommandLine(int argc, char **argv, DedicatedServerConfig *confi
static void SetExeWorkingDirectory()
{
char exePath[MAX_PATH] = {};
GetModuleFileNameA(NULL, exePath, MAX_PATH);
GetModuleFileNameA(nullptr, exePath, MAX_PATH);
char *slash = strrchr(exePath, '\\');
if (slash != NULL)
if (slash != nullptr)
{
*(slash + 1) = 0;
SetCurrentDirectoryA(exePath);
@@ -288,7 +289,7 @@ static void SetExeWorkingDirectory()
static void ApplyServerPropertiesToDedicatedConfig(const ServerPropertiesConfig &serverProperties, DedicatedServerConfig *config)
{
if (config == NULL)
if (config == nullptr)
{
return;
}
@@ -325,6 +326,7 @@ static void TickCoreSystems()
g_NetworkManager.DoWork();
ProfileManager.Tick();
StorageManager.Tick();
ConsoleSaveFileOriginal::flushPendingBackgroundSave();
}
/**
@@ -420,7 +422,7 @@ int main(int argc, char **argv)
#endif
LogStartupStep("registering hidden window class");
HINSTANCE hInstance = GetModuleHandle(NULL);
HINSTANCE hInstance = GetModuleHandle(nullptr);
MyRegisterClass(hInstance);
LogStartupStep("creating hidden window");
@@ -490,7 +492,7 @@ int main(int argc, char **argv)
LogStartupStep("creating Minecraft singleton");
Minecraft::main();
Minecraft *minecraft = Minecraft::GetInstance();
if (minecraft == NULL)
if (minecraft == nullptr)
{
LogError("startup", "Minecraft initialization failed.");
CleanupDevice();
@@ -655,7 +657,7 @@ int main(int argc, char **argv)
break;
}
if (autosaveRequested && app.GetXuiServerAction(kServerActionPad) == eXuiServerAction_Idle)
if (autosaveRequested && app.GetXuiServerAction(kServerActionPad) == eXuiServerAction_Idle && !ConsoleSaveFileOriginal::hasPendingBackgroundSave())
{
LogWorldIO("autosave completed");
autosaveRequested = false;
@@ -669,7 +671,7 @@ int main(int argc, char **argv)
DWORD now = GetTickCount();
if ((LONG)(now - nextAutosaveTick) >= 0)
{
if (app.GetXuiServerAction(kServerActionPad) == eXuiServerAction_Idle)
if (app.GetXuiServerAction(kServerActionPad) == eXuiServerAction_Idle && !ConsoleSaveFileOriginal::hasPendingBackgroundSave())
{
LogWorldIO("requesting autosave");
app.SetXuiServerAction(kServerActionPad, eXuiServerAction_AutoSaveGame);
@@ -685,25 +687,38 @@ int main(int argc, char **argv)
LogInfof("shutdown", "Dedicated server stopped");
MinecraftServer *server = MinecraftServer::getInstance();
if (server != NULL)
if (server != nullptr && !ConsoleSaveFileOriginal::hasPendingBackgroundSave())
{
server->setSaveOnExit(true);
}
if (server != NULL)
{
LogWorldIO("requesting save before shutdown");
LogWorldIO("using saveOnExit for shutdown");
}
if (ConsoleSaveFileOriginal::hasPendingBackgroundSave())
{
LogWorldIO("Waiting for autosave to complete...");
}
MinecraftServer::HaltServer();
if (g_NetworkManager.ServerStoppedValid())
{
C4JThread waitThread(&WaitForServerStoppedThreadProc, NULL, "WaitServerStopped");
C4JThread waitThread(&WaitForServerStoppedThreadProc, nullptr, "WaitServerStopped");
waitThread.Run();
while (waitThread.isRunning())
{
TickCoreSystems();
Sleep(10);
}
waitThread.WaitForCompletion(INFINITE);
}
while (ConsoleSaveFileOriginal::hasPendingBackgroundSave())
{
TickCoreSystems();
Sleep(10);
}
LogInfof("shutdown", "Cleaning up and exiting.");
WinsockNetLayer::Shutdown();
LogDebugf("shutdown", "Network layer shutdown complete.");
@@ -714,5 +729,4 @@ int main(int argc, char **argv)
return 0;
}
}

View File

@@ -31,7 +31,7 @@ struct SaveInfoQueryContext
SaveInfoQueryContext()
: done(false)
, success(false)
, details(NULL)
, details(nullptr)
{
}
};
@@ -75,7 +75,7 @@ static void SetStorageSaveUniqueFilename(const std::string &saveFilename)
static void LogSaveFilename(const char *prefix, const std::string &saveFilename)
{
LogInfof("world-io", "%s: %s", (prefix != NULL) ? prefix : "save-filename", saveFilename.c_str());
LogInfof("world-io", "%s: %s", (prefix != nullptr) ? prefix : "save-filename", saveFilename.c_str());
}
/**
@@ -86,7 +86,7 @@ static void LogSaveFilename(const char *prefix, const std::string &saveFilename)
*/
static bool EnsureDirectoryExists(const std::wstring &directoryPath, bool *outCreated)
{
if (outCreated != NULL)
if (outCreated != nullptr)
{
*outCreated = false;
}
@@ -107,9 +107,9 @@ static bool EnsureDirectoryExists(const std::wstring &directoryPath, bool *outCr
return false;
}
if (CreateDirectoryW(directoryPath.c_str(), NULL))
if (CreateDirectoryW(directoryPath.c_str(), nullptr))
{
if (outCreated != NULL)
if (outCreated != nullptr)
{
*outCreated = true;
}
@@ -189,7 +189,7 @@ static void LogEnumeratedSaveInfo(int index, const SAVE_INFO &saveInfo)
static int GetSavesInfoCallbackProc(LPVOID lpParam, SAVE_DETAILS *pSaveDetails, const bool bRes)
{
SaveInfoQueryContext *context = (SaveInfoQueryContext *)lpParam;
if (context != NULL)
if (context != nullptr)
{
context->details = pSaveDetails;
context->success = bRes;
@@ -207,7 +207,7 @@ static int GetSavesInfoCallbackProc(LPVOID lpParam, SAVE_DETAILS *pSaveDetails,
static int LoadSaveDataCallbackProc(LPVOID lpParam, const bool bIsCorrupt, const bool bIsOwner)
{
SaveDataLoadContext *context = (SaveDataLoadContext *)lpParam;
if (context != NULL)
if (context != nullptr)
{
context->isCorrupt = bIsCorrupt;
context->isOwner = bIsOwner;
@@ -236,12 +236,12 @@ static bool WaitForSaveInfoResult(SaveInfoQueryContext *context, DWORD timeoutMs
return true;
}
if (context->details == NULL)
if (context->details == nullptr)
{
// Some implementations fill ReturnSavesInfo before the callback
// Keep polling as a fallback instead of relying only on callback completion
SAVE_DETAILS *details = StorageManager.ReturnSavesInfo();
if (details != NULL)
if (details != nullptr)
{
context->details = details;
context->success = true;
@@ -250,7 +250,7 @@ static bool WaitForSaveInfoResult(SaveInfoQueryContext *context, DWORD timeoutMs
}
}
if (tickProc != NULL)
if (tickProc != nullptr)
{
tickProc();
}
@@ -278,7 +278,7 @@ static bool WaitForSaveLoadResult(SaveDataLoadContext *context, DWORD timeoutMs,
return true;
}
if (tickProc != NULL)
if (tickProc != nullptr)
{
tickProc();
}
@@ -370,12 +370,12 @@ static EWorldSaveLoadResult PrepareWorldSaveData(
LoadSaveDataThreadParam **outSaveData,
std::string *outResolvedSaveFilename)
{
if (outSaveData == NULL)
if (outSaveData == nullptr)
{
return eWorldSaveLoad_Failed;
}
*outSaveData = NULL;
if (outResolvedSaveFilename != NULL)
*outSaveData = nullptr;
if (outResolvedSaveFilename != nullptr)
{
outResolvedSaveFilename->clear();
}
@@ -404,11 +404,11 @@ static EWorldSaveLoadResult PrepareWorldSaveData(
return eWorldSaveLoad_Failed;
}
if (infoContext.details == NULL)
if (infoContext.details == nullptr)
{
infoContext.details = StorageManager.ReturnSavesInfo();
}
if (infoContext.details == NULL)
if (infoContext.details == nullptr)
{
LogWorldIO("failed to retrieve save list");
return eWorldSaveLoad_Failed;
@@ -486,7 +486,7 @@ static EWorldSaveLoadResult PrepareWorldSaveData(
resolvedSaveFilename = targetSaveFilename;
}
if (outResolvedSaveFilename != NULL)
if (outResolvedSaveFilename != nullptr)
{
*outResolvedSaveFilename = resolvedSaveFilename;
}
@@ -621,11 +621,11 @@ bool WaitForWorldActionIdle(
{
// Keep network and storage progressing while waiting
// If this stops, save action itself may stall and time out
if (tickProc != NULL)
if (tickProc != nullptr)
{
tickProc();
}
if (handleActionsProc != NULL)
if (handleActionsProc != nullptr)
{
handleActionsProc();
}

View File

@@ -496,6 +496,8 @@ set(_MINECRAFT_SERVER_COMMON_ROOT
"${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.Client/iob_shim.asm"
"${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.Client/stdafx.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.Client/stubs.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/ConsoleSaveFileOriginal.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/../Minecraft.World/ConsoleSaveFileOriginal.h"
"${CMAKE_CURRENT_SOURCE_DIR}/../include/lce_filesystem/lce_filesystem.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Console/ServerCliInput.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Console/ServerCliInput.h"

View File

@@ -12,6 +12,25 @@
#include "..\Minecraft.Client\Common\GameRules\LevelGenerationOptions.h"
#include "..\Minecraft.World\net.minecraft.world.level.chunk.storage.h"
#ifdef MINECRAFT_SERVER_BUILD
#include <thread>
#include <atomic>
#include <mutex>
static std::atomic<bool> s_bgSaveActive{false};
static std::mutex s_bgSaveMutex;
struct BackgroundSaveResult
{
ConsoleSaveFile *owner = nullptr;
PBYTE thumbData = nullptr;
DWORD thumbSize = 0;
BYTE textMeta[88] = {};
int textMetaBytes = 0;
bool pending = false;
};
static BackgroundSaveResult s_bgResult;
#endif
#ifdef _XBOX
#define RESERVE_ALLOCATION MEM_RESERVE | MEM_LARGE_PAGES
@@ -219,7 +238,7 @@ ConsoleSaveFileOriginal::~ConsoleSaveFileOriginal()
pagesCommitted = 0;
// Make sure we don't have any thumbnail data still waiting round - we can't need it now we've destroyed the save file anyway
#if defined _XBOX
app.GetSaveThumbnail(NULL,NULL);
app.GetSaveThumbnail(nullptr,nullptr);
#elif defined __PS3__
app.GetSaveThumbnail(nullptr,nullptr, nullptr,nullptr);
#endif
@@ -478,7 +497,7 @@ void ConsoleSaveFileOriginal::finalizeWrite()
{
unsigned int pagesRequired = ( desiredSize + (CSF_PAGE_SIZE - 1 ) ) / CSF_PAGE_SIZE;
void *pvRet = VirtualAlloc(pvHeap, pagesRequired * CSF_PAGE_SIZE, COMMIT_ALLOCATION, PAGE_READWRITE);
if( pvRet == NULL )
if( pvRet == nullptr )
{
__debugbreak();
}
@@ -678,6 +697,83 @@ void ConsoleSaveFileOriginal::Flush(bool autosave, bool updateThumbnail )
unsigned int fileSize = header.GetFileSize();
#ifdef MINECRAFT_SERVER_BUILD
// on the server we dont want to block the tick thread doing compression!!!
// sna[pshot pvSaveMem while we still hold the lock then hand it off to a background thread
byte *snap = new (std::nothrow) byte[fileSize];
if (snap)
{
// copy the save buffer while we still own the lock so nothing can write to it mid-copy
QueryPerformanceCounter(&qwTime);
memcpy(snap, pvSaveMem, fileSize);
QueryPerformanceCounter(&qwNewTime);
app.DebugPrintf("snapshot %u bytes in %.3f sec\n", fileSize,
(qwNewTime.QuadPart - qwTime.QuadPart) * fSecsPerTick);
PBYTE thumb = nullptr;
DWORD thumbSz = 0;
app.GetSaveThumbnail(&thumb, &thumbSz);
BYTE meta[88];
ZeroMemory(meta, 88);
int64_t seed = 0;
bool hasSeed = false;
if (MinecraftServer *sv = MinecraftServer::getInstance(); sv && sv->levels[0])
{
seed = sv->levels[0]->getLevelData()->getSeed();
hasSeed = true;
}
int metaLen = app.CreateImageTextData(meta, seed, hasSeed,
app.GetGameHostOption(eGameHostOption_All), Minecraft::GetInstance()->getCurrentTexturePackId());
// telemetry
INT uid = 0;
StorageManager.GetSaveUniqueNumber(&uid);
TelemetryManager->RecordLevelSaveOrCheckpoint(ProfileManager.GetPrimaryPad(), uid, fileSize);
ReleaseSaveAccess();
s_bgSaveActive.store(true, std::memory_order_release);
std::thread([snap, fileSize, thumb, thumbSz, meta, metaLen, this]() {
unsigned int compLen = fileSize + 8;
byte *buf = static_cast<byte *>(StorageManager.AllocateSaveData(compLen));
if (!buf)
{
// FAIL!! attempt precalc
compLen = 0;
Compression::getCompression()->Compress(nullptr, &compLen, snap, fileSize);
compLen += 8;
buf = static_cast<byte *>(StorageManager.AllocateSaveData(compLen));
}
if (buf)
{
// COM,PRESS
Compression::getCompression()->Compress(buf + 8, &compLen, snap, fileSize);
ZeroMemory(buf, 8);
memcpy(buf + 4, &fileSize, sizeof(fileSize));
// store the result so flushPendingBackgroundSave() can pick it up on the main thread next tick
// StorageManager isnt thread safe so we cant call SetSaveImages or SaveSaveData from here. Bwoomp
std::lock_guard<std::mutex> lk(s_bgSaveMutex);
s_bgResult.owner = this;
s_bgResult.thumbData = thumb;
s_bgResult.thumbSize = thumbSz;
memcpy(s_bgResult.textMeta, meta, sizeof(meta));
s_bgResult.textMetaBytes = metaLen;
s_bgResult.pending = true;
}
else
{
app.DebugPrintf("save buf alloc failed\n");
s_bgSaveActive.store(false, std::memory_order_release);
}
delete[] snap;
}).detach();
return;
}
app.DebugPrintf("snapshot alloc failed (%u bytes)\n", fileSize);
#endif
// Assume that the compression will make it smaller so initially attempt to allocate the current file size
// We add 4 bytes to the start so that we can signal compressed data
// And another 4 bytes to store the decompressed data size
@@ -843,6 +939,10 @@ int ConsoleSaveFileOriginal::SaveSaveDataCallback(LPVOID lpParam,bool bRes)
{
ConsoleSaveFile *pClass=static_cast<ConsoleSaveFile *>(lpParam);
#ifdef MINECRAFT_SERVER_BUILD
s_bgSaveActive.store(false, std::memory_order_release);
#endif
return 0;
}
@@ -1090,3 +1190,26 @@ void *ConsoleSaveFileOriginal::getWritePointer(FileEntry *file)
{
return static_cast<char *>(pvSaveMem) + file->currentFilePointer;;
}
#ifdef MINECRAFT_SERVER_BUILD
void ConsoleSaveFileOriginal::flushPendingBackgroundSave()
{
std::lock_guard<std::mutex> lk(s_bgSaveMutex);
if (!s_bgResult.pending)
return;
StorageManager.SetSaveImages(
s_bgResult.thumbData, s_bgResult.thumbSize,
nullptr, 0, s_bgResult.textMeta, s_bgResult.textMetaBytes);
StorageManager.SaveSaveData(&ConsoleSaveFileOriginal::SaveSaveDataCallback, s_bgResult.owner);
s_bgResult.pending = false;
// the actual write isnt done until SaveSaveDataCallback fires
}
bool ConsoleSaveFileOriginal::hasPendingBackgroundSave()
{
return s_bgSaveActive.load(std::memory_order_acquire);
}
#endif

View File

@@ -71,6 +71,11 @@ public:
virtual vector<FileEntry *> *getValidPlayerDatFiles();
#endif //__PS3__
#ifdef MINECRAFT_SERVER_BUILD
static void flushPendingBackgroundSave();
static bool hasPendingBackgroundSave();
#endif
virtual int getSaveVersion();
virtual int getOriginalSaveVersion();