diff --git a/Minecraft.Client/LevelRenderer.cpp b/Minecraft.Client/LevelRenderer.cpp index 320d6f8d..2bb48002 100644 --- a/Minecraft.Client/LevelRenderer.cpp +++ b/Minecraft.Client/LevelRenderer.cpp @@ -66,6 +66,7 @@ #include "FrustumCuller.h" #include "../Minecraft.World/BasicTypeContainers.h" #include "Common/UI/UIScene_SettingsGraphicsMenu.h" +#include "ParticleUtils.h" #include //#define DISABLE_SPU_CODE @@ -2853,7 +2854,7 @@ void LevelRenderer::addParticle(ePARTICLE_TYPE eParticleType, double x, double y shared_ptr LevelRenderer::addParticleInternal(ePARTICLE_TYPE eParticleType, double x, double y, double z, double xa, double ya, double za) { - if (mc == nullptr || mc->cameraTargetPlayer == nullptr || mc->particleEngine == nullptr) + if (mc == nullptr || mc->cameraTargetPlayer == nullptr || mc->particleEngine == nullptr || mc->options == nullptr) { return nullptr; } @@ -2863,18 +2864,46 @@ shared_ptr LevelRenderer::addParticleInternal(ePARTICLE_TYPE eParticle if( Double::isNaN(x) ) return nullptr; if( Double::isNaN(y) ) return nullptr; if( Double::isNaN(z) ) return nullptr; + if( Double::isNaN(xa) ) return nullptr; + if( Double::isNaN(ya) ) return nullptr; + if( Double::isNaN(za) ) return nullptr; int particleLevel = mc->options->particles; - Level *lev; - int playerIndex = mc->player->GetXboxPad(); // 4J added - lev = level[playerIndex]; + Level *lev = nullptr; + int playerIndex = -1; - if (particleLevel == 1) + shared_ptr sourcePlayer = mc->player; + if (sourcePlayer == nullptr && mc->cameraTargetPlayer != nullptr) + { + sourcePlayer = dynamic_pointer_cast(mc->cameraTargetPlayer); + } + if (sourcePlayer != nullptr) + { + playerIndex = sourcePlayer->GetXboxPad(); + if (playerIndex >= 0 && playerIndex < XUSER_MAX_COUNT && isReasonableLevelPointer(level[playerIndex]) && level[playerIndex]->dimension != nullptr) + { + lev = level[playerIndex]; + } + } + + if (lev == nullptr) + { + for (unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if (mc->localplayers[i] != nullptr && isReasonableLevelPointer(mc->localplayers[i]->level) && mc->localplayers[i]->level->dimension != nullptr) + { + lev = mc->localplayers[i]->level; + break; + } + } + } + + if (particleLevel == 1 && lev != nullptr) { // when playing at "decreased" particle level, randomly filter // particles by setting the level to "minimal" - if (level[playerIndex]->random->nextInt(3) == 0) + if (lev->random->nextInt(3) == 0) { particleLevel = 2; } @@ -2924,6 +2953,39 @@ shared_ptr LevelRenderer::addParticleInternal(ePARTICLE_TYPE eParticle if (lev == nullptr) return nullptr; + if (!isReasonableLevelPointer(lev) || !isReasonableDimensionPointer(lev->dimension)) + return nullptr; + + bool levIsKnown = false; + if (lev == mc->level || lev == mc->animateTickLevel) + { + levIsKnown = true; + } + else + { + for (unsigned int i = 0; i < XUSER_MAX_COUNT && !levIsKnown; ++i) + { + if (mc->localplayers[i] != nullptr && mc->localplayers[i]->level == lev && isReasonableLevelPointer(mc->localplayers[i]->level) && mc->localplayers[i]->level->dimension != nullptr) + { + levIsKnown = true; + } + } + for (int i = 0; i < 4 && !levIsKnown; ++i) + { + if (this->level[i] == lev && isReasonableLevelPointer(this->level[i]) && this->level[i]->dimension != nullptr) + { + levIsKnown = true; + } + } + } + if (!levIsKnown) + { + return nullptr; + } + + if (lev->dimension == nullptr) + return nullptr; + if (particleLevel > 1) { // TODO: If any of the particles below are necessary even if diff --git a/Minecraft.Client/Particle.cpp b/Minecraft.Client/Particle.cpp index 80dac72e..4d4b4654 100644 --- a/Minecraft.Client/Particle.cpp +++ b/Minecraft.Client/Particle.cpp @@ -1,10 +1,50 @@ #include "stdafx.h" #include "Particle.h" #include "Tesselator.h" +#include "../Minecraft.Client/Minecraft.h" +#include "../Minecraft.Client/MultiPlayerLevel.h" +#include "../Minecraft.Client/MultiPlayerLocalPlayer.h" +#include "../Minecraft.World/Level.h" +#include "../Minecraft.World/net.minecraft.world.level.dimension.h" #include "../Minecraft.World/Random.h" #include "../Minecraft.World/Mth.h" #include "../Minecraft.World/JavaMath.h" #include "../Minecraft.World/net.minecraft.world.h" +#include "ParticleUtils.h" + +static bool isKnownParticleLevel(Level *lev, Minecraft *mc) +{ + if (lev == nullptr || mc == nullptr) + return false; + + if (lev == mc->level || lev == mc->animateTickLevel || lev == mc->oldLevel) + return true; + + for (unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + if (mc->localplayers[i] != nullptr && mc->localplayers[i]->level == lev) + return true; + } + + return false; +} + +static Level *safeParticleLevel(Level *level) +{ + Minecraft *mc = Minecraft::GetInstance(); + + if (level != nullptr && isReasonableLevelPointer(level) && isKnownParticleLevel(level, mc) && isReasonableDimensionPointer(level->dimension)) + { + return level; + } + + if (mc != nullptr && mc->level != nullptr && isReasonableLevelPointer(mc->level) && isReasonableDimensionPointer(mc->level->dimension)) + { + return static_cast(mc->level); + } + + return nullptr; +} /* protected int tex; @@ -42,15 +82,37 @@ void Particle::_init(Level *level, double x, double y, double z) texY = 0; } -Particle::Particle(Level *level, double x, double y, double z) : Entity(level, false) +Particle::Particle(Level *level, double x, double y, double z) : Entity(nullptr, false) { _init(level,x,y,z); + + Level *safeLevel = safeParticleLevel(level); + if (safeLevel != nullptr && isReasonableDimensionPointer(safeLevel->dimension)) + { + this->level = safeLevel; + dimension = safeLevel->dimension->id; + } + else + { + this->level = nullptr; + } } -Particle::Particle(Level *level, double x, double y, double z, double xa, double ya, double za) : Entity(level, false) +Particle::Particle(Level *level, double x, double y, double z, double xa, double ya, double za) : Entity(nullptr, false) { _init(level,x,y,z); + Level *safeLevel = safeParticleLevel(level); + if (safeLevel != nullptr && isReasonableDimensionPointer(safeLevel->dimension)) + { + this->level = safeLevel; + dimension = safeLevel->dimension->id; + } + else + { + this->level = nullptr; + } + xd = xa + static_cast(Math::random() * 2 - 1) * 0.4f; yd = ya + static_cast(Math::random() * 2 - 1) * 0.4f; zd = za + static_cast(Math::random() * 2 - 1) * 0.4f; diff --git a/Minecraft.Client/ParticleEngine.cpp b/Minecraft.Client/ParticleEngine.cpp index 0663b033..a3553624 100644 --- a/Minecraft.Client/ParticleEngine.cpp +++ b/Minecraft.Client/ParticleEngine.cpp @@ -34,9 +34,11 @@ ParticleEngine::~ParticleEngine() void ParticleEngine::add(shared_ptr p) { + if (p == nullptr || p->level == nullptr || p->level->dimension == nullptr) return; + if (p->level != level) return; + int t = p->getParticleTexture(); - if (p->level == nullptr || p->level->dimension == nullptr) return; - int l = p->level->dimension->id == 0 ? 0 : ( p->level->dimension->id == -1 ? 1 : 2); + int l = p->level->dimension->id == 0 ? 0 : ( p->level->dimension->id == -1 ? 1 : 2); int maxParticles; switch(p->GetType()) { @@ -69,6 +71,13 @@ void ParticleEngine::tick() for (unsigned int i = 0; i < particles[l][tt][list].size(); i++) { shared_ptr p = particles[l][tt][list][i]; + if (p == nullptr || p->level == nullptr || p->level != level || p->level->dimension == nullptr) + { + particles[l][tt][list][i] = particles[l][tt][list].back(); + particles[l][tt][list].pop_back(); + i--; + continue; + } p->tick(); if (p->removed) { @@ -88,6 +97,10 @@ void ParticleEngine::render(shared_ptr player, float a, int list) { return; } + if (level == nullptr || level->dimension == nullptr) + { + return; + } // 4J - change brought forward from 1.2.3 float xa = Camera::xa; @@ -195,19 +208,19 @@ void ParticleEngine::setLevel(Level *level) { this->level = level; // 4J - we've now got a set of particle vectors for each dimension, and only clearing them when its game over & the level is set to nullptr - if( level == nullptr ) - { - for( int l = 0; l < 3; l++ ) - { - for (int tt = 0; tt < TEXTURE_COUNT; tt++) - { - for( int list = 0; list < LIST_COUNT; list++ ) - { - particles[l][tt][list].clear(); - } - } - } - } + if (level == nullptr) + { + for (int l = 0; l < 3; l++) + { + for (int tt = 0; tt < TEXTURE_COUNT; tt++) + { + for (int list = 0; list < LIST_COUNT; list++) + { + particles[l][tt][list].clear(); + } + } + } + } } void ParticleEngine::destroy(int x, int y, int z, int tid, int data) diff --git a/Minecraft.Client/ParticleUtils.h b/Minecraft.Client/ParticleUtils.h new file mode 100644 index 00000000..2d97e593 --- /dev/null +++ b/Minecraft.Client/ParticleUtils.h @@ -0,0 +1,29 @@ +#pragma once + +class Level; +class Dimension; + +inline bool isReasonablePointer(const void *ptr) +{ + if (ptr == nullptr) + return false; + + unsigned long long addr = reinterpret_cast(ptr); + if (addr <= 0x10000ULL || addr > 0x00007FFFFFFFFFFFULL) + return false; + if ((addr & 0x7ULL) != 0ULL) + return false; + if ((addr & 0xFFFFFFFFULL) == 0ULL) + return false; + return true; +} + +inline bool isReasonableLevelPointer(Level *lev) +{ + return isReasonablePointer(lev); +} + +inline bool isReasonableDimensionPointer(Dimension *dim) +{ + return isReasonablePointer(dim); +} diff --git a/Minecraft.World/Entity.cpp b/Minecraft.World/Entity.cpp index 38d964e9..7d3d5820 100644 --- a/Minecraft.World/Entity.cpp +++ b/Minecraft.World/Entity.cpp @@ -372,9 +372,13 @@ Entity::Entity(Level *level, bool useSmallId) // 4J - added useSmallId parameter // resetPos(); setPos(0, 0, 0); - if (level != nullptr && level->dimension != nullptr) + if (level != nullptr) { - dimension = level->dimension->id; + auto dimensionPtr = level->dimension; + if (dimensionPtr != nullptr) + { + dimension = dimensionPtr->id; + } } if( entityData )