Performance optimizations: sound caching, renderer culling, entity and lighting improvements

- Sound engine: cache filesystem probe results to avoid repeated file existence checks; add MA_SOUND_FLAG_DECODE for pre-decoded playback
- Level renderer: column-level frustum culling, compact visible chunk lists to skip empty iteration, lightweight second-pass render path, early-out for non-dirty chunks, scaled recheck period at high render distances
- Entity: cache shared_from_this() to reduce reference counting overhead in move() and checkInsideTiles()
- Level: skip checkLight() when tile light properties unchanged; enable entity locking on all platforms (not just Vita)
- LevelChunk: only rescan min height when the minimum column changes; defer lightGap processing
- LivingEntity: use raw pointer cast instead of dynamic_pointer_cast; cache friction tile lookup
- ServerPlayerGameMode: return whether block was destroyed to avoid redundant tile update packets
This commit is contained in:
Revela 2026-03-18 17:22:04 -05:00
parent 80ba2e0762
commit 0ba923f085
12 changed files with 311 additions and 170 deletions

View file

@ -468,65 +468,69 @@ void SoundEngine::play(int iSound, float x, float y, float z, float volume, floa
sprintf_s(basePath, "Windows64Media/Sound/%s", (char*)szSoundName);
char finalPath[256];
sprintf_s(finalPath, "%s.wav", basePath);
const char* extensions[] = { ".ogg", ".wav", ".mp3" };
size_t extCount = sizeof(extensions) / sizeof(extensions[0]);
bool found = false;
for (size_t extIdx = 0; extIdx < extCount; extIdx++)
// Check path cache first to avoid expensive filesystem probing
auto cacheIt = m_soundPathCache.find(iSound);
if (cacheIt != m_soundPathCache.end())
{
char basePlusExt[256];
sprintf_s(basePlusExt, "%s%s", basePath, extensions[extIdx]);
DWORD attr = GetFileAttributesA(basePlusExt);
if (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY))
{
sprintf_s(finalPath, "%s", basePlusExt);
found = true;
break;
}
const auto& paths = cacheIt->second;
if (paths.empty())
return; // previously probed, no files found
const std::string& chosen = paths[rand() % paths.size()];
sprintf_s(finalPath, "%s", chosen.c_str());
}
if (!found)
else
{
int count = 0;
// Cache miss — probe filesystem and store results
std::vector<std::string> validPaths;
const char* extensions[] = { ".ogg", ".wav", ".mp3" };
size_t extCount = sizeof(extensions) / sizeof(extensions[0]);
bool found = false;
// Check base name with each extension (non-numbered)
for (size_t extIdx = 0; extIdx < extCount; extIdx++)
{
for (size_t i = 1; i < 32; i++)
char basePlusExt[256];
sprintf_s(basePlusExt, "%s%s", basePath, extensions[extIdx]);
DWORD attr = GetFileAttributesA(basePlusExt);
if (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY))
{
char numberedPath[256];
sprintf_s(numberedPath, "%s%d%s", basePath, i, extensions[extIdx]);
DWORD attr = GetFileAttributesA(numberedPath);
if (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY))
validPaths.push_back(basePlusExt);
found = true;
break; // non-numbered: only one base file needed
}
}
if (!found)
{
// Check numbered variants (e.g. sound1.ogg, sound2.ogg, ...)
for (size_t extIdx = 0; extIdx < extCount; extIdx++)
{
for (int i = 1; i < 32; i++)
{
count = i;
char numberedPath[256];
sprintf_s(numberedPath, "%s%d%s", basePath, i, extensions[extIdx]);
DWORD attr = GetFileAttributesA(numberedPath);
if (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY))
{
validPaths.push_back(numberedPath);
}
}
}
}
if (count > 0)
m_soundPathCache[iSound] = validPaths;
if (validPaths.empty())
{
int chosen = (rand() % count) + 1;
for (size_t extIdx = 0; extIdx < extCount; extIdx++)
{
char numberedPath[256];
sprintf_s(numberedPath, "%s%d%s", basePath, chosen, extensions[extIdx]);
DWORD attr = GetFileAttributesA(numberedPath);
if (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY))
{
sprintf_s(finalPath, "%s", numberedPath);
found = true;
break;
}
}
if (!found)
{
sprintf_s(finalPath, "%s%d.wav", basePath, chosen);
}
sprintf_s(finalPath, "%s.wav", basePath); // fallback for debug print
}
else
{
const std::string& chosen = validPaths[rand() % validPaths.size()];
sprintf_s(finalPath, "%s", chosen.c_str());
}
}
@ -546,7 +550,7 @@ void SoundEngine::play(int iSound, float x, float y, float z, float volume, floa
if (ma_sound_init_from_file(
&m_engine,
finalPath,
MA_SOUND_FLAG_ASYNC,
MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC,
nullptr,
nullptr,
&s->sound) != MA_SUCCESS)
@ -602,24 +606,40 @@ void SoundEngine::playUI(int iSound, float volume, float pitch)
sprintf_s(basePath, "Windows64Media/Sound/Minecraft/UI/%s", ConvertSoundPathToName(name));
char finalPath[256];
sprintf_s(finalPath, "%s.wav", basePath);
const char* extensions[] = { ".ogg", ".wav", ".mp3" };
size_t count = sizeof(extensions) / sizeof(extensions[0]);
bool found = false;
for (size_t i = 0; i < count; i++)
// Check UI sound path cache first
auto cacheIt = m_uiSoundPathCache.find(iSound);
if (cacheIt != m_uiSoundPathCache.end())
{
sprintf_s(finalPath, "%s%s", basePath, extensions[i]);
if (FileExists(finalPath))
if (cacheIt->second.empty())
{
found = true;
break;
app.DebugPrintf("No sound file found for UI sound (cached): %s\n", basePath);
return;
}
sprintf_s(finalPath, "%s", cacheIt->second.c_str());
}
if (!found)
else
{
app.DebugPrintf("No sound file found for UI sound: %s\n", basePath);
return;
// Cache miss — probe filesystem and store result
const char* extensions[] = { ".ogg", ".wav", ".mp3" };
size_t count = sizeof(extensions) / sizeof(extensions[0]);
bool found = false;
for (size_t i = 0; i < count; i++)
{
sprintf_s(finalPath, "%s%s", basePath, extensions[i]);
if (FileExists(finalPath))
{
found = true;
break;
}
}
if (!found)
{
m_uiSoundPathCache[iSound] = ""; // cache negative result
app.DebugPrintf("No sound file found for UI sound: %s\n", basePath);
return;
}
m_uiSoundPathCache[iSound] = finalPath;
}
MiniAudioSound* s = new MiniAudioSound();
@ -633,7 +653,7 @@ void SoundEngine::playUI(int iSound, float volume, float pitch)
if (ma_sound_init_from_file(
&m_engine,
finalPath,
MA_SOUND_FLAG_ASYNC,
MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC,
nullptr,
nullptr,
&s->sound) != MA_SUCCESS)

View file

@ -5,6 +5,8 @@ using namespace std;
#include "..\..\Minecraft.World\SoundTypes.h"
#include "miniaudio.h"
#include <unordered_map>
#include <string>
constexpr float SFX_3D_MIN_DISTANCE = 1.0f;
constexpr float SFX_3D_MAX_DISTANCE = 16.0f;
@ -187,6 +189,9 @@ private:
int m_iStream_CD_1;
bool *m_bHeardTrackA;
std::unordered_map<int, std::vector<std::string>> m_soundPathCache; // play(): sound ID → all valid variant paths
std::unordered_map<int, std::string> m_uiSoundPathCache; // playUI(): sound ID → resolved path
#ifdef __ORBIS__
int32_t m_hBGMAudio;
#endif

View file

@ -1545,7 +1545,7 @@ void GameRenderer::renderLevel(float a, int64_t until)
if (visibleWaterChunks > 0)
{
PIXBeginNamedEvent(0,"Fancy second pass - actual rendering");
levelRenderer->render(cameraEntity, 1, a, updateChunks); // 4J - chanaged, used to be renderSameAsLast but we don't support that anymore
levelRenderer->renderChunksDirect(1, a); // Lightweight path — skips redundant allChanged/resortChunks checks
PIXEndNamedEvent();
}

View file

@ -157,6 +157,11 @@ LevelRenderer::LevelRenderer(Minecraft *mc, Textures *textures)
dirtyChunkPresent = false;
lastDirtyChunkFound = 0;
visibleLists_layer0 = nullptr;
visibleLists_layer1 = nullptr;
visibleCount_layer0 = 0;
visibleCount_layer1 = 0;
this->mc = mc;
this->textures = textures;
@ -455,6 +460,12 @@ void LevelRenderer::allChanged(int playerIndex)
// delete sortedChunks[playerIndex]; // 4J - removed - not sorting our chunks anymore
}
// Free old visible chunk lists
delete[] visibleLists_layer0;
delete[] visibleLists_layer1;
visibleLists_layer0 = nullptr;
visibleLists_layer1 = nullptr;
chunks[playerIndex] = ClipChunkArray(xChunks * yChunks * zChunks);
// sortedChunks[playerIndex] = new vector<Chunk *>(xChunks * yChunks * zChunks); // 4J - removed - not sorting our chunks anymore
int id = 0;
@ -487,6 +498,13 @@ void LevelRenderer::allChanged(int playerIndex)
}
nonStackDirtyChunksAdded();
// Allocate visible chunk lists (worst case: all chunks visible)
int totalChunkCount = xChunks * yChunks * zChunks;
visibleLists_layer0 = new int[totalChunkCount];
visibleLists_layer1 = new int[totalChunkCount];
visibleCount_layer0 = 0;
visibleCount_layer1 = 0;
if (level != nullptr)
{
shared_ptr<Entity> player = mc->cameraTargetPlayer;
@ -705,42 +723,42 @@ int LevelRenderer::render(shared_ptr<LivingEntity> player, int layer, double alp
{
int playerIndex = mc->player->GetXboxPad();
// 4J - added - if the number of players has changed, we need to rebuild things for the new draw distance this will require
if( lastPlayerCount[playerIndex] != activePlayers() )
{
allChanged();
}
else if (mc->options->viewDistance != lastViewDistance)
{
allChanged();
}
// Only check allChanged/resortChunks on layer 0 — they only need to run once per frame
if (layer == 0)
{
// 4J - added - if the number of players has changed, we need to rebuild things for the new draw distance this will require
if( lastPlayerCount[playerIndex] != activePlayers() )
{
allChanged();
}
else if (mc->options->viewDistance != lastViewDistance)
{
allChanged();
}
totalChunks = 0;
offscreenChunks = 0;
occludedChunks = 0;
renderedChunks = 0;
emptyChunks = 0;
double xd = player->x - xOld[playerIndex];
double yd = player->y - yOld[playerIndex];
double zd = player->z - zOld[playerIndex];
if (xd * xd + yd * yd + zd * zd > 4 * 4)
{
xOld[playerIndex] = player->x;
yOld[playerIndex] = player->y;
zOld[playerIndex] = player->z;
resortChunks(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z));
}
}
double xOff = player->xOld + (player->x - player->xOld) * alpha;
double yOff = player->yOld + (player->y - player->yOld) * alpha;
double zOff = player->zOld + (player->z - player->zOld) * alpha;
double xd = player->x - xOld[playerIndex];
double yd = player->y - yOld[playerIndex];
double zd = player->z - zOld[playerIndex];
if (xd * xd + yd * yd + zd * zd > 4 * 4)
{
xOld[playerIndex] = player->x;
yOld[playerIndex] = player->y;
zOld[playerIndex] = player->z;
resortChunks(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z));
// sort(sortedChunks[playerIndex]->begin(),sortedChunks[playerIndex]->end(), DistanceChunkSorter(player)); // 4J - removed - not sorting our chunks anymore
}
Lighting::turnOff();
int count = renderChunks(0, static_cast<int>(chunks[playerIndex].length), layer, alpha);
@ -749,6 +767,38 @@ int LevelRenderer::render(shared_ptr<LivingEntity> player, int layer, double alp
}
// Lightweight render path for the second layer 1 pass — skips allChanged/resortChunks checks
// and just does GL setup + visible list iteration + cleanup.
// Assumes Lighting::turnOff() was already called by the prior render() invocation.
void LevelRenderer::renderChunksDirect(int layer, double alpha)
{
shared_ptr<LivingEntity> player = mc->cameraTargetPlayer;
if (player == nullptr) return;
mc->gameRenderer->turnOnLightLayer(alpha);
double xOff = player->xOld + (player->x - player->xOld) * alpha;
double yOff = player->yOld + (player->y - player->yOld) * alpha;
double zOff = player->zOld + (player->z - player->zOld) * alpha;
glPushMatrix();
glTranslatef(static_cast<float>(-xOff), static_cast<float>(-yOff), static_cast<float>(-zOff));
int *lists = (layer == 0) ? visibleLists_layer0 : visibleLists_layer1;
int numVisible = (layer == 0) ? visibleCount_layer0 : visibleCount_layer1;
bool first = true;
if (lists != nullptr)
{
for (int i = 0; i < numVisible; i++)
{
if (RenderManager.CBuffCall(lists[i], first))
first = false;
}
}
glPopMatrix();
mc->gameRenderer->turnOffLightLayer(alpha);
}
#ifdef __PSVITA__
#include <stdlib.h>
@ -819,23 +869,35 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha)
bool first = true;
int count = 0;
ClipChunk *pClipChunk = chunks[playerIndex].data;
unsigned char emptyFlag = LevelRenderer::CHUNK_FLAG_EMPTY0 << layer;
for( int i = 0; i < chunks[playerIndex].length; i++, pClipChunk++ )
// Use compact visible lists built during cull() instead of iterating all chunks
int *lists = (layer == 0) ? visibleLists_layer0 : visibleLists_layer1;
int numVisible = (layer == 0) ? visibleCount_layer0 : visibleCount_layer1;
if (lists != nullptr)
{
if( !pClipChunk->visible ) continue; // This will be set if the chunk isn't visible, or isn't compiled, or has both empty flags set
if( pClipChunk->globalIdx == -1 ) continue; // Not sure if we should ever encounter this... TODO check
if( ( globalChunkFlags[pClipChunk->globalIdx] & emptyFlag ) == emptyFlag ) continue; // Check that this particular layer isn't empty
// List can be calculated directly from the chunk's global idex
int list = pClipChunk->globalIdx * 2 + layer;
list += chunkLists;
if(RenderManager.CBuffCall(list, first))
for (int i = 0; i < numVisible; i++)
{
first = false;
if (RenderManager.CBuffCall(lists[i], first))
first = false;
count++;
}
}
else
{
// Fallback: iterate all chunks (before visible lists are allocated)
ClipChunk *pClipChunk = chunks[playerIndex].data;
unsigned char emptyFlag = LevelRenderer::CHUNK_FLAG_EMPTY0 << layer;
for( int i = 0; i < chunks[playerIndex].length; i++, pClipChunk++ )
{
if( !pClipChunk->visible ) continue;
if( pClipChunk->globalIdx == -1 ) continue;
if( ( globalChunkFlags[pClipChunk->globalIdx] & emptyFlag ) == emptyFlag ) continue;
int list = pClipChunk->globalIdx * 2 + layer;
list += chunkLists;
if(RenderManager.CBuffCall(list, first))
first = false;
count++;
}
count++;
}
#ifdef __PSVITA__
@ -1968,6 +2030,9 @@ bool LevelRenderer::updateDirtyChunks()
for( int y = 0; y < CHUNK_Y_COUNT; y++ )
{
ClipChunk *pClipChunk = &chunks[p][(z * yChunks + y) * xChunks + x];
// Early-out for non-dirty chunks - avoids distance calculation for the vast majority at steady state
if( !(globalChunkFlags[ pClipChunk->globalIdx ] & CHUNK_FLAG_DIRTY) )
continue;
// Get distance to this chunk - deliberately not calling the chunk's method of doing this to avoid overheads (passing entitie, type conversion etc.) that this involves
int xd = pClipChunk->xm - px;
int yd = pClipChunk->ym - py;
@ -2163,7 +2228,9 @@ bool LevelRenderer::updateDirtyChunks()
else
{
// Nothing to do - clear flags that there are things to process, unless it's been a while since we found any dirty chunks in which case force a check next time through
if( ( System::currentTimeMillis() - lastDirtyChunkFound ) > FORCE_DIRTY_CHUNK_CHECK_PERIOD_MS )
// Scale recheck period with render distance to reduce wasted full-scans at high distances
int recheckPeriod = (xChunks >= 60) ? 1000 : (xChunks >= 40) ? 500 : FORCE_DIRTY_CHUNK_CHECK_PERIOD_MS;
if( ( System::currentTimeMillis() - lastDirtyChunkFound ) > recheckPeriod )
{
dirtyChunkPresent = true;
}
@ -2564,32 +2631,81 @@ void LevelRenderer::cull(Culler *culler, float a)
fdraw[i * 4 + 3] = static_cast<float>(fd->m_Frustum[i][3] + (fx * -fc->xOff) + (fy * -fc->yOff) + (fz * -fc->zOff));
}
ClipChunk *pClipChunk = chunks[playerIndex].data;
int vis = 0;
int total = 0;
int numWrong = 0;
for (unsigned int i = 0; i < chunks[playerIndex].length; i++)
// Reset visible chunk lists for this frame
visibleCount_layer0 = 0;
visibleCount_layer1 = 0;
// Column-level frustum culling: test one AABB per XZ column before testing individual Y chunks.
// At dist 64 this reduces ~278K clip() calls to ~17K column tests + per-chunk tests only for visible columns.
for (int x = 0; x < xChunks; x++)
{
unsigned char flags = pClipChunk->globalIdx == -1 ? 0 : globalChunkFlags[ pClipChunk->globalIdx ];
for (int z = 0; z < zChunks; z++)
{
// Build column AABB from bottom and top chunks in this column
ClipChunk *bottomChunk = &chunks[playerIndex][(z * yChunks + 0) * xChunks + x];
ClipChunk *topChunk = &chunks[playerIndex][(z * yChunks + (yChunks - 1)) * xChunks + x];
float columnAABB[6] = {
bottomChunk->aabb[0], bottomChunk->aabb[1], bottomChunk->aabb[2], // minX, minY, minZ
bottomChunk->aabb[3], topChunk->aabb[4], bottomChunk->aabb[5] // maxX, maxY(top), maxZ
};
// Always perform frustum cull test
bool clipres = clip(pClipChunk->aabb, fdraw);
// Test entire column against frustum
if (!clip(columnAABB, fdraw))
{
// Entire column outside frustum — mark all Y chunks invisible
for (int y = 0; y < yChunks; y++)
{
ClipChunk *pClipChunk = &chunks[playerIndex][(z * yChunks + y) * xChunks + x];
pClipChunk->visible = false;
}
continue;
}
if ( (flags & CHUNK_FLAG_COMPILED ) && ( ( flags & CHUNK_FLAG_EMPTYBOTH ) != CHUNK_FLAG_EMPTYBOTH ) )
{
pClipChunk->visible = clipres;
if( pClipChunk->visible ) vis++;
total++;
// Column is (partially) in frustum — test individual chunks
for (int y = 0; y < yChunks; y++)
{
ClipChunk *pClipChunk = &chunks[playerIndex][(z * yChunks + y) * xChunks + x];
unsigned char flags = pClipChunk->globalIdx == -1 ? 0 : globalChunkFlags[ pClipChunk->globalIdx ];
// Skip frustum test for confirmed-empty compiled chunks - they have nothing to render
if ((flags & CHUNK_FLAG_COMPILED) && (flags & CHUNK_FLAG_EMPTYBOTH) == CHUNK_FLAG_EMPTYBOTH)
{
pClipChunk->visible = false;
continue;
}
bool clipres = clip(pClipChunk->aabb, fdraw);
if ( (flags & CHUNK_FLAG_COMPILED ) && ( ( flags & CHUNK_FLAG_EMPTYBOTH ) != CHUNK_FLAG_EMPTYBOTH ) )
{
pClipChunk->visible = clipres;
if( pClipChunk->visible ) vis++;
total++;
}
else if (clipres)
{
pClipChunk->visible = true;
}
else
{
pClipChunk->visible = false;
}
// Build compact visible chunk lists for renderChunks()
if (pClipChunk->visible && pClipChunk->globalIdx != -1 && visibleLists_layer0 != nullptr)
{
int list = pClipChunk->globalIdx * 2 + chunkLists;
if (!((flags & CHUNK_FLAG_EMPTY0) == CHUNK_FLAG_EMPTY0))
visibleLists_layer0[visibleCount_layer0++] = list;
if (!((flags & CHUNK_FLAG_EMPTY1) == CHUNK_FLAG_EMPTY1))
visibleLists_layer1[visibleCount_layer1++] = list + 1;
}
}
}
else if (clipres)
{
pClipChunk->visible = true;
}
else
{
pClipChunk->visible = false;
}
pClipChunk++;
}
}

View file

@ -83,6 +83,7 @@ private:
void resortChunks(int xc, int yc, int zc);
public:
int render(shared_ptr<LivingEntity> player, int layer, double alpha, bool updateChunks);
void renderChunksDirect(int layer, double alpha);
private:
int renderChunks(int from, int to, int layer, double alpha);
public:
@ -270,6 +271,12 @@ public:
XLockFreeStack<int> dirtyChunksLockFreeStack;
// Visible chunk lists built by cull(), consumed by renderChunks()
int *visibleLists_layer0;
int *visibleLists_layer1;
int visibleCount_layer0;
int visibleCount_layer1;
bool dirtyChunkPresent;
int64_t lastDirtyChunkFound;
static const int FORCE_DIRTY_CHUNK_CHECK_PERIOD_MS = 125; // decreased from 250 to 125 - updated by detectiveren

View file

@ -491,9 +491,9 @@ void PlayerConnection::handlePlayerAction(shared_ptr<PlayerActionPacket> packet)
}
else if (packet->action == PlayerActionPacket::STOP_DESTROY_BLOCK)
{
player->gameMode->stopDestroyBlock(x, y, z);
bool destroyed = player->gameMode->stopDestroyBlock(x, y, z);
server->getPlayers()->prioritiseTileChanges(x, y, z, level->dimension->id); // 4J added - make sure that the update packets for this get prioritised over other general world updates
if (level->getTile(x, y, z) != 0) player->connection->send(std::make_shared<TileUpdatePacket>(x, y, z, level));
if (!destroyed && level->getTile(x, y, z) != 0) player->connection->send(std::make_shared<TileUpdatePacket>(x, y, z, level));
}
else if (packet->action == PlayerActionPacket::ABORT_DESTROY_BLOCK)
{

View file

@ -172,7 +172,7 @@ void ServerPlayerGameMode::startDestroyBlock(int x, int y, int z, int face)
}
}
void ServerPlayerGameMode::stopDestroyBlock(int x, int y, int z)
bool ServerPlayerGameMode::stopDestroyBlock(int x, int y, int z)
{
if (x == xDestroyBlock && y == yDestroyBlock && z == zDestroyBlock)
{
@ -188,6 +188,7 @@ void ServerPlayerGameMode::stopDestroyBlock(int x, int y, int z)
isDestroyingBlock = false;
level->destroyTileProgress(player->entityId, x, y, z, -1);
destroyBlock(x, y, z);
return true;
}
else if (!hasDelayedDestroy)
{
@ -198,9 +199,11 @@ void ServerPlayerGameMode::stopDestroyBlock(int x, int y, int z)
delayedDestroyY = y;
delayedDestroyZ = z;
delayedTickStart = destroyProgressStart;
return true;
}
}
}
return false;
}
void ServerPlayerGameMode::abortDestroyBlock(int x, int y, int z)

View file

@ -45,7 +45,7 @@ public:
void tick();
void startDestroyBlock(int x, int y, int z, int face);
void stopDestroyBlock(int x, int y, int z);
bool stopDestroyBlock(int x, int y, int z);
void abortDestroyBlock(int x, int y, int z);
private:

View file

@ -705,6 +705,8 @@ void Entity::move(double xa, double ya, double za, bool noEntityCubes) // 4J -
return;
}
auto self = shared_from_this();
ySlideOffset *= 0.4f;
double xo = x;
@ -734,21 +736,21 @@ void Entity::move(double xa, double ya, double za, bool noEntityCubes) // 4J -
if (isPlayerSneaking)
{
double d = 0.05;
while (xa != 0 && level->getCubes(shared_from_this(), bb->cloneMove(xa, -1.0, 0))->empty())
while (xa != 0 && level->getCubes(self, bb->cloneMove(xa, -1.0, 0), true)->empty())
{
if (xa < d && xa >= -d) xa = 0;
else if (xa > 0) xa -= d;
else xa += d;
xaOrg = xa;
}
while (za != 0 && level->getCubes(shared_from_this(), bb->cloneMove(0, -1.0, za))->empty())
while (za != 0 && level->getCubes(self, bb->cloneMove(0, -1.0, za), true)->empty())
{
if (za < d && za >= -d) za = 0;
else if (za > 0) za -= d;
else za += d;
zaOrg = za;
}
while (xa != 0 && za != 0 && level->getCubes(shared_from_this(), bb->cloneMove(xa, -1.0, za))->empty())
while (xa != 0 && za != 0 && level->getCubes(self, bb->cloneMove(xa, -1.0, za), true)->empty())
{
if (xa < d && xa >= -d) xa = 0;
else if (xa > 0) xa -= d;
@ -761,7 +763,7 @@ void Entity::move(double xa, double ya, double za, bool noEntityCubes) // 4J -
}
}
AABBList *aABBs = level->getCubes(shared_from_this(), bb->expand(xa, ya, za), noEntityCubes, true);
AABBList *aABBs = level->getCubes(self, bb->expand(xa, ya, za), noEntityCubes, true);
// 4J Stu - Particles (and possibly other entities) don't have xChunk and zChunk set, so calculate the chunk instead
@ -816,7 +818,7 @@ void Entity::move(double xa, double ya, double za, bool noEntityCubes) // 4J -
bb->set(bbOrg);
// 4J - added extra expand, as if we don't move up by footSize by hitting a block above us, then overall we could be trying to move as much as footSize downwards,
// so we'd better include cubes under our feet in this list of things we might possibly collide with
aABBs = level->getCubes(shared_from_this(), bb->expand(xa, ya, za)->expand(0,-ya,0),false,true);
aABBs = level->getCubes(self, bb->expand(xa, ya, za)->expand(0,-ya,0),false,true);
if(!level->isClientSide || level->reallyHasChunk(xc, zc))
{
@ -926,7 +928,7 @@ void Entity::move(double xa, double ya, double za, bool noEntityCubes) // 4J -
playSound(eSoundType_LIQUID_SWIM, speed, 1 + (random->nextFloat() - random->nextFloat()) * 0.4f);
}
playStepSound(xt, yt, zt, t);
Tile::tiles[t]->stepOn(level, xt, yt, zt, shared_from_this());
Tile::tiles[t]->stepOn(level, xt, yt, zt, self);
}
}
@ -969,6 +971,7 @@ void Entity::checkInsideTiles()
if (level->hasChunksAt(x0, y0, z0, x1, y1, z1))
{
auto self = shared_from_this();
for (int x = x0; x <= x1; x++)
for (int y = y0; y <= y1; y++)
for (int z = z0; z <= z1; z++)
@ -976,7 +979,7 @@ void Entity::checkInsideTiles()
int t = level->getTile(x, y, z);
if (t > 0)
{
Tile::tiles[t]->entityInside(level, x, y, z, shared_from_this());
Tile::tiles[t]->entityInside(level, x, y, z, self);
}
}
}

View file

@ -932,6 +932,7 @@ bool Level::setTileAndData(int x, int y, int z, int tile, int data, int updateFl
int old = c->getTile(x & 15, y, z & 15);
int olddata = c->getData( x & 15, y, z & 15);
#endif
int prevTile = c->getTile(x & 15, y, z & 15);
result = c->setTileAndData(x & 15, y, z & 15, tile, data);
if( updateFlags != Tile::UPDATE_INVISIBLE_NO_LIGHT)
{
@ -939,7 +940,11 @@ bool Level::setTileAndData(int x, int y, int z, int tile, int data, int updateFl
PIXBeginNamedEvent(0,"Checking light %d %d %d",x,y,z);
PIXBeginNamedEvent(0,"was %d, %d now %d, %d",old,olddata,tile,data);
#endif
checkLight(x, y, z);
if (Tile::lightBlock[tile & 0xff] != Tile::lightBlock[prevTile & 0xff] ||
Tile::lightEmission[tile & 0xff] != Tile::lightEmission[prevTile & 0xff])
{
checkLight(x, y, z);
}
PIXEndNamedEvent();
PIXEndNamedEvent();
}
@ -3687,13 +3692,11 @@ vector<shared_ptr<Entity> > *Level::getEntities(shared_ptr<Entity> except, AABB
int zc0 = Mth::floor((bb->z0 - 2) / 16);
int zc1 = Mth::floor((bb->z1 + 2) / 16);
#ifdef __PSVITA__
#ifdef _ENTITIES_RW_SECTION
// AP - RW critical sections are expensive so enter it here so we only have to call it once instead of X times
EnterCriticalRWSection(&LevelChunk::m_csEntities, false);
#else
EnterCriticalSection(&LevelChunk::m_csEntities);
#endif
#endif
for (int xc = xc0; xc <= xc1; xc++)
@ -3706,12 +3709,10 @@ vector<shared_ptr<Entity> > *Level::getEntities(shared_ptr<Entity> except, AABB
}
MemSect(0);
#ifdef __PSVITA__
#ifdef _ENTITIES_RW_SECTION
LeaveCriticalRWSection(&LevelChunk::m_csEntities, false);
#else
LeaveCriticalSection(&LevelChunk::m_csEntities);
#endif
#endif
return &es;
@ -3730,13 +3731,11 @@ vector<shared_ptr<Entity> > *Level::getEntitiesOfClass(const type_info& baseClas
int zc1 = Mth::floor((bb->z1 + 2) / 16);
vector<shared_ptr<Entity> > *es = new vector<shared_ptr<Entity> >();
#ifdef __PSVITA__
#ifdef _ENTITIES_RW_SECTION
// AP - RW critical sections are expensive so enter it here so we only have to call it once instead of X times
EnterCriticalRWSection(&LevelChunk::m_csEntities, false);
#else
EnterCriticalSection(&LevelChunk::m_csEntities);
#endif
#endif
for (int xc = xc0; xc <= xc1; xc++)
@ -3750,12 +3749,10 @@ vector<shared_ptr<Entity> > *Level::getEntitiesOfClass(const type_info& baseClas
}
}
#ifdef __PSVITA__
#ifdef _ENTITIES_RW_SECTION
LeaveCriticalRWSection(&LevelChunk::m_csEntities, false);
#else
LeaveCriticalSection(&LevelChunk::m_csEntities);
#endif
#endif
return es;

View file

@ -817,8 +817,9 @@ void LevelChunk::recalcHeight(int x, int yStart, int z)
{
minHeight = y;
}
else
else if (yOld == minHeight)
{
// Only rescan when the column that was at the minimum changed height
int min = Level::maxBuildHeight - 1;
for (int _x = 0; _x < 16; _x++)
for (int _z = 0; _z < 16; _z++)
@ -886,11 +887,16 @@ void LevelChunk::recalcHeight(int x, int yStart, int z)
if (!level->dimension->hasCeiling)
{
PIXBeginNamedEvent(0,"Light gaps");
lightGap(xOffs - 1, zOffs, y1, y2);
lightGap(xOffs + 1, zOffs, y1, y2);
lightGap(xOffs, zOffs - 1, y1, y2);
lightGap(xOffs, zOffs + 1, y1, y2);
lightGap(xOffs, zOffs, y1, y2);
// Flag columns for gap rechecking — processed by recheckGaps() on next tick
auto flagGap = [&](int wx, int wz) {
LevelChunk *c = level->getChunkAt(wx, wz);
if (c && !c->isEmpty()) c->lightGaps(wx & 15, wz & 15);
};
flagGap(xOffs - 1, zOffs);
flagGap(xOffs + 1, zOffs);
flagGap(xOffs, zOffs - 1);
flagGap(xOffs, zOffs + 1);
flagGap(xOffs, zOffs);
PIXEndNamedEvent();
}
@ -1643,10 +1649,7 @@ void LevelChunk::getEntities(shared_ptr<Entity> except, AABB *bb, vector<shared_
if (yc0 < 0) yc0 = 0;
if (yc1 >= ENTITY_BLOCKS_LENGTH) yc1 = ENTITY_BLOCKS_LENGTH - 1;
#ifndef __PSVITA__
// AP - RW critical sections are expensive so enter once in Level::getEntities
EnterCriticalSection(&m_csEntities);
#endif
// Lock is now always held by Level::getEntities()
for (int yc = yc0; yc <= yc1; yc++)
{
vector<shared_ptr<Entity> > *entities = entityBlocks[yc];
@ -1671,9 +1674,6 @@ void LevelChunk::getEntities(shared_ptr<Entity> except, AABB *bb, vector<shared_
}
}
}
#ifndef __PSVITA__
LeaveCriticalSection(&m_csEntities);
#endif
}
void LevelChunk::getEntitiesOfClass(const type_info& ec, AABB *bb, vector<shared_ptr<Entity> > &es, const EntitySelector *selector)
@ -1698,10 +1698,7 @@ void LevelChunk::getEntitiesOfClass(const type_info& ec, AABB *bb, vector<shared
yc1 = 0;
}
#ifndef __PSVITA__
// AP - RW critical sections are expensive so enter once in Level::getEntitiesOfClass
EnterCriticalSection(&m_csEntities);
#endif
// Lock is now always held by Level::getEntitiesOfClass()
for (int yc = yc0; yc <= yc1; yc++)
{
vector<shared_ptr<Entity> > *entities = entityBlocks[yc];
@ -1729,9 +1726,6 @@ void LevelChunk::getEntitiesOfClass(const type_info& ec, AABB *bb, vector<shared
// 4J - note needs to be equivalent to baseClass.isAssignableFrom(e.getClass())
}
}
#ifndef __PSVITA__
LeaveCriticalSection(&m_csEntities);
#endif
}
int LevelChunk::countEntities()

View file

@ -1380,16 +1380,12 @@ void LivingEntity::jumpFromGround()
void LivingEntity::travel(float xa, float ya)
{
#ifdef __PSVITA__
// AP - dynamic_pointer_cast is a non-trivial call
// AP - dynamic_pointer_cast is a non-trivial call, use raw pointer instead
Player *thisPlayer = nullptr;
if( this->instanceof(eTYPE_PLAYER) )
if (this->instanceof(eTYPE_PLAYER))
{
thisPlayer = (Player*) this;
}
#else
shared_ptr<Player> thisPlayer = dynamic_pointer_cast<Player>(shared_from_this());
#endif
if (isInWater() && !(thisPlayer && thisPlayer->abilities.flying) )
{
double yo = y;
@ -1424,13 +1420,14 @@ void LivingEntity::travel(float xa, float ya)
else
{
float friction = 0.91f;
int frictionTile = 0;
if (onGround)
{
frictionTile = level->getTile(Mth::floor(x), Mth::floor(bb->y0) - 1, Mth::floor(z));
friction = 0.6f * 0.91f;
int t = level->getTile(Mth::floor(x), Mth::floor(bb->y0) - 1, Mth::floor(z));
if (t > 0)
if (frictionTile > 0)
{
friction = Tile::tiles[t]->friction * 0.91f;
friction = Tile::tiles[frictionTile]->friction * 0.91f;
}
}
@ -1452,10 +1449,9 @@ void LivingEntity::travel(float xa, float ya)
if (onGround)
{
friction = 0.6f * 0.91f;
int t = level->getTile( Mth::floor(x), Mth::floor(bb->y0) - 1, Mth::floor(z));
if (t > 0)
if (frictionTile > 0)
{
friction = Tile::tiles[t]->friction * 0.91f;
friction = Tile::tiles[frictionTile]->friction * 0.91f;
}
}
if (onLadder())