mirror of
https://github.com/neoStudiosLCE/neoLegacy.git
synced 2026-06-09 01:42:55 +00:00
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:
parent
80ba2e0762
commit
0ba923f085
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
Loading…
Reference in a new issue