2026-03-01 12:16:08 +08:00
# pragma once
# include "..\Minecraft.World\LevelListener.h"
# include "..\Minecraft.World\Definitions.h"
# include "OffsettedRenderList.h"
# include "..\Minecraft.World\JavaIntHash.h"
# include "..\Minecraft.World\Level.h"
2026-03-03 03:04:10 +08:00
# include "ResourceLocation.h"
2026-03-01 12:16:08 +08:00
# include <xmcore.h>
# ifdef __PS3__
# include "C4JSpursJob.h"
# endif
class MultiPlayerLevel ;
class Textures ;
class Chunk ;
class Minecraft ;
class TileRenderer ;
class Culler ;
class Entity ;
class TileEntity ;
class Mob ;
class Vec3 ;
class Particle ;
class BlockDestructionProgress ;
class IconRegister ;
class Tesselator ;
using namespace std ;
// AP - this is a system that works out which chunks actually need to be grouped together via the deferral system when doing chunk::rebuild. Doing this will reduce the number
// of chunks built in a single group and reduce the chance of seeing through the landscape when digging near the edges/corners of a chunk.
2026-03-02 17:39:35 +07:00
// I've added another chunk flag to mark a chunk critical so it swipes a bit from the reference count value (goes to 3 bits to 2). This works on Vita because it doesn't have
2026-03-01 12:16:08 +08:00
// split screen reference counting.
# ifdef __PSVITA__
# define _CRITICAL_CHUNKS
# endif
class LevelRenderer : public LevelListener
{
friend class Chunk ;
2026-03-03 03:04:10 +08:00
private :
static ResourceLocation MOON_LOCATION ;
static ResourceLocation MOON_PHASES_LOCATION ;
static ResourceLocation SUN_LOCATION ;
static ResourceLocation CLOUDS_LOCATION ;
static ResourceLocation END_SKY_LOCATION ;
2026-03-01 12:16:08 +08:00
public :
static const int CHUNK_XZSIZE = 16 ;
# ifdef _LARGE_WORLDS
static const int CHUNK_SIZE = 16 ;
# else
static const int CHUNK_SIZE = 16 ;
# endif
static const int CHUNK_Y_COUNT = Level : : maxBuildHeight / CHUNK_SIZE ;
2026-03-02 23:27:20 -06:00
# if (defined _XBOX_ONE || defined _WINDOWS64)
2026-03-01 14:17:45 -05:00
static const int MAX_COMMANDBUFFER_ALLOCATIONS = 2047 * 1024 * 1024 ; // Changed to 2047. 4J had set to 512.
2026-03-01 12:16:08 +08:00
# elif defined __ORBIS__
static const int MAX_COMMANDBUFFER_ALLOCATIONS = 448 * 1024 * 1024 ; // 4J - added - hard limit is 512 so giving a lot of headroom here for fragmentation (have seen 16MB lost to fragmentation in multiplayer crash dump before)
# elif defined __PS3__
static const int MAX_COMMANDBUFFER_ALLOCATIONS = 110 * 1024 * 1024 ; // 4J - added
# else
static const int MAX_COMMANDBUFFER_ALLOCATIONS = 55 * 1024 * 1024 ; // 4J - added
# endif
public :
LevelRenderer ( Minecraft * mc , Textures * textures ) ;
private :
void renderStars ( ) ;
void createCloudMesh ( ) ; // 4J added
public :
void setLevel ( int playerIndex , MultiPlayerLevel * level ) ;
void allChanged ( ) ;
void allChanged ( int playerIndex ) ;
// 4J-PB added
void AddDLCSkinsToMemTextures ( ) ;
public :
void renderEntities ( Vec3 * cam , Culler * culler , float a ) ;
wstring gatherStats1 ( ) ;
wstring gatherStats2 ( ) ;
private :
void resortChunks ( int xc , int yc , int zc ) ;
public :
2026-03-03 03:04:10 +08:00
int render ( shared_ptr < LivingEntity > player , int layer , double alpha , bool updateChunks ) ;
2026-03-01 12:16:08 +08:00
private :
int renderChunks ( int from , int to , int layer , double alpha ) ;
public :
int activePlayers ( ) ; // 4J - added
public :
void renderSameAsLast ( int layer , double alpha ) ;
void tick ( ) ;
void renderSky ( float alpha ) ;
void renderHaloRing ( float alpha ) ;
void renderClouds ( float alpha ) ;
bool isInCloud ( double x , double y , double z , float alpha ) ;
void renderAdvancedClouds ( float alpha ) ;
bool updateDirtyChunks ( ) ;
public :
2026-03-02 17:37:16 +07:00
void renderHit ( shared_ptr < Player > player , HitResult * h , int mode , shared_ptr < ItemInstance > inventoryItem , float a ) ;
void renderDestroyAnimation ( Tesselator * t , shared_ptr < Player > player , float a ) ;
2026-03-03 03:04:10 +08:00
void renderHitOutline ( shared_ptr < Player > player , HitResult * h , int mode , float a ) ;
2026-03-01 12:16:08 +08:00
void render ( AABB * b ) ;
void setDirty ( int x0 , int y0 , int z0 , int x1 , int y1 , int z1 , Level * level ) ; // 4J - added level param
void tileChanged ( int x , int y , int z ) ;
void tileLightChanged ( int x , int y , int z ) ;
void setTilesDirty ( int x0 , int y0 , int z0 , int x1 , int y1 , int z1 , Level * level ) ; // 4J - added level param
# ifdef __PS3__
void cull_SPU ( int playerIndex , Culler * culler , float a ) ;
void waitForCull_SPU ( ) ;
C4JSpursJobQueue : : Port * m_jobPort_CullSPU ;
C4JSpursJobQueue : : Port * m_jobPort_FindNearestChunk ;
bool m_bSPUCullStarted [ 4 ] ;
# endif // __PS3__
void cull ( Culler * culler , float a ) ;
void playStreamingMusic ( const wstring & name , int x , int y , int z ) ;
void playSound ( int iSound , double x , double y , double z , float volume , float pitch , float fSoundClipDist = 16.0f ) ;
2026-03-02 17:37:16 +07:00
void playSound ( shared_ptr < Entity > entity , int iSound , double x , double y , double z , float volume , float pitch , float fSoundClipDist = 16.0f ) ;
2026-03-03 03:04:10 +08:00
void playSoundExceptPlayer ( shared_ptr < Player > player , int iSound , double x , double y , double z , float volume , float pitch , float fSoundClipDist = 16.0f ) ;
2026-03-01 12:16:08 +08:00
void addParticle ( ePARTICLE_TYPE eParticleType , double x , double y , double z , double xa , double ya , double za ) ; // 4J added
2026-03-02 17:37:16 +07:00
shared_ptr < Particle > addParticleInternal ( ePARTICLE_TYPE eParticleType , double x , double y , double z , double xa , double ya , double za ) ; // 4J added
void entityAdded ( shared_ptr < Entity > entity ) ;
void entityRemoved ( shared_ptr < Entity > entity ) ;
void playerRemoved ( shared_ptr < Entity > entity ) { } // 4J added - for when a player is removed from the level's player array, not just the entity storage
2026-03-01 12:16:08 +08:00
void skyColorChanged ( ) ;
void clear ( ) ;
2026-03-03 03:04:10 +08:00
void globalLevelEvent ( int type , int sourceX , int sourceY , int sourceZ , int data ) ;
2026-03-02 17:37:16 +07:00
void levelEvent ( shared_ptr < Player > source , int type , int x , int y , int z , int data ) ;
2026-03-01 12:16:08 +08:00
void destroyTileProgress ( int id , int x , int y , int z , int progress ) ;
void registerTextures ( IconRegister * iconRegister ) ;
2026-03-02 17:37:16 +07:00
typedef unordered_map < int , vector < shared_ptr < TileEntity > > , IntKeyHash , IntKeyEq > rteMap ;
2026-03-01 12:16:08 +08:00
private :
// debug
int m_freezeticks ; // used to freeze the clouds
// 4J - this block of declarations was scattered round the code but have gathered everything into one place
2026-03-02 17:37:16 +07:00
rteMap renderableTileEntities ; // 4J - changed - was vector<shared_ptr<TileEntity>, now hashed by chunk so we can find them
2026-03-01 12:16:08 +08:00
CRITICAL_SECTION m_csRenderableTileEntities ;
MultiPlayerLevel * level [ 4 ] ; // 4J - now one per player
Textures * textures ;
// vector<Chunk *> *sortedChunks[4]; // 4J - removed - not sorting our chunks anymore
ClipChunkArray chunks [ 4 ] ; // 4J - now one per player
int lastPlayerCount [ 4 ] ; // 4J - added
int xChunks , yChunks , zChunks ;
int chunkLists ;
Minecraft * mc ;
TileRenderer * tileRenderer [ 4 ] ; // 4J - now one per player
int ticks ;
int starList , skyList , darkList , haloRingList ;
int cloudList ; // 4J added
int xMinChunk , yMinChunk , zMinChunk ;
int xMaxChunk , yMaxChunk , zMaxChunk ;
int lastViewDistance ;
int noEntityRenderFrames ;
int totalEntities ;
int renderedEntities ;
int culledEntities ;
int chunkFixOffs ;
vector < Chunk * > _renderChunks ;
int frame ;
int repeatList ;
double xOld [ 4 ] ; // 4J - now one per player
double yOld [ 4 ] ; // 4J - now one per player
double zOld [ 4 ] ; // 4J - now one per player
int totalChunks , offscreenChunks , occludedChunks , renderedChunks , emptyChunks ;
static const int RENDERLISTS_LENGTH = 4 ; // 4J - added
OffsettedRenderList renderLists [ RENDERLISTS_LENGTH ] ;
unordered_map < int , BlockDestructionProgress * > destroyingBlocks ;
Icon * * breakingTextures ;
public :
void fullyFlagRenderableTileEntitiesToBeRemoved ( ) ; // 4J added
CRITICAL_SECTION m_csDirtyChunks ;
bool m_nearDirtyChunk ;
// 4J - Destroyed Tile Management - these things added so we can track tiles which have been recently destroyed, and
// provide temporary collision for them until the render data has been updated to reflect this change
class DestroyedTileManager
{
private :
class RecentTile
{
public :
int x ;
int y ;
int z ;
Level * level ;
AABBList boxes ;
int timeout_ticks ;
bool rebuilt ;
RecentTile ( int x , int y , int z , Level * level ) ;
~ RecentTile ( ) ;
} ;
CRITICAL_SECTION m_csDestroyedTiles ;
vector < RecentTile * > m_destroyedTiles ;
public :
void destroyingTileAt ( Level * level , int x , int y , int z ) ; // For game to let this manager know that a tile is about to be destroyed (must be called before it actually is)
void updatedChunkAt ( Level * level , int x , int y , int z , int veryNearCount ) ; // For chunk rebuilding to inform the manager that a chunk (a 16x16x16 tile render chunk) has been updated
void addAABBs ( Level * level , AABB * box , AABBList * boxes ) ; // For game to get any AABBs that the user should be colliding with as render data has not yet been updated
void tick ( ) ;
DestroyedTileManager ( ) ;
~ DestroyedTileManager ( ) ;
} ;
DestroyedTileManager * destroyedTileManager ;
float destroyProgress ;
// 4J - added for new render list handling
// This defines the maximum size of renderable level, must be big enough to cope with actual size of level + view distance at each side
// so that we can render the "infinite" sea at the edges
2026-03-02 17:39:35 +07:00
static const int MAX_LEVEL_RENDER_SIZE [ 3 ] ;
2026-03-01 12:16:08 +08:00
static const int DIMENSION_OFFSETS [ 3 ] ;
// This is the TOTAL area of columns of chunks to be allocated for render round the players. So for one player, it would be a region of
// sqrt(PLAYER_RENDER_AREA) x sqrt(PLAYER_RENDER_AREA)
# ifdef _LARGE_WORLDS
static const int PLAYER_VIEW_DISTANCE = 18 ; // Straight line distance from centre to extent of visible world
static const int PLAYER_RENDER_AREA = ( PLAYER_VIEW_DISTANCE * PLAYER_VIEW_DISTANCE * 4 ) ;
# else
static const int PLAYER_RENDER_AREA = 400 ;
# endif
static int getDimensionIndexFromId ( int id ) ;
static int getGlobalIndexForChunk ( int x , int y , int z , Level * level ) ;
static int getGlobalIndexForChunk ( int x , int y , int z , int dimensionId ) ;
static bool isGlobalIndexInSameDimension ( int idx , Level * level ) ;
static int getGlobalChunkCount ( ) ;
static int getGlobalChunkCountForOverworld ( ) ;
// Get/set/clear individual flags
bool getGlobalChunkFlag ( int x , int y , int z , Level * level , unsigned char flag , unsigned char shift = 0 ) ;
void setGlobalChunkFlag ( int x , int y , int z , Level * level , unsigned char flag , unsigned char shift = 0 ) ;
void setGlobalChunkFlag ( int index , unsigned char flag , unsigned char shift = 0 ) ;
void clearGlobalChunkFlag ( int x , int y , int z , Level * level , unsigned char flag , unsigned char shift = 0 ) ;
// Get/set whole byte of flags
unsigned char getGlobalChunkFlags ( int x , int y , int z , Level * level ) ;
void setGlobalChunkFlags ( int x , int y , int z , Level * level , unsigned char flags ) ;
// Reference counting
unsigned char incGlobalChunkRefCount ( int x , int y , int z , Level * level ) ;
unsigned char decGlobalChunkRefCount ( int x , int y , int z , Level * level ) ;
// Actual storage for flags
unsigned char * globalChunkFlags ;
// The flag definitions
static const int CHUNK_FLAG_COMPILED = 0x01 ;
static const int CHUNK_FLAG_DIRTY = 0x02 ;
static const int CHUNK_FLAG_EMPTY0 = 0x04 ;
static const int CHUNK_FLAG_EMPTY1 = 0x08 ;
static const int CHUNK_FLAG_EMPTYBOTH = 0x0c ;
static const int CHUNK_FLAG_NOTSKYLIT = 0x10 ;
# ifdef _CRITICAL_CHUNKS
static const int CHUNK_FLAG_CRITICAL = 0x20 ;
static const int CHUNK_FLAG_CUT_OUT = 0x40 ;
static const int CHUNK_FLAG_REF_MASK = 0x01 ;
static const int CHUNK_FLAG_REF_SHIFT = 7 ;
# else
static const int CHUNK_FLAG_REF_MASK = 0x07 ;
static const int CHUNK_FLAG_REF_SHIFT = 5 ;
# endif
XLockFreeStack < int > dirtyChunksLockFreeStack ;
bool dirtyChunkPresent ;
2026-03-02 17:39:35 +07:00
__int64 lastDirtyChunkFound ;
2026-03-01 12:16:08 +08:00
static const int FORCE_DIRTY_CHUNK_CHECK_PERIOD_MS = 250 ;
# ifdef _LARGE_WORLDS
static const int MAX_CONCURRENT_CHUNK_REBUILDS = 4 ;
static const int MAX_CHUNK_REBUILD_THREADS = MAX_CONCURRENT_CHUNK_REBUILDS - 1 ;
static Chunk permaChunk [ MAX_CONCURRENT_CHUNK_REBUILDS ] ;
static C4JThread * rebuildThreads [ MAX_CHUNK_REBUILD_THREADS ] ;
static C4JThread : : EventArray * s_rebuildCompleteEvents ;
static C4JThread : : Event * s_activationEventA [ MAX_CHUNK_REBUILD_THREADS ] ;
static void staticCtor ( ) ;
static int rebuildChunkThreadProc ( LPVOID lpParam ) ;
CRITICAL_SECTION m_csChunkFlags ;
# endif
void nonStackDirtyChunksAdded ( ) ;
2026-03-03 03:04:10 +08:00
int checkAllPresentChunks ( bool * faultFound ) ; // 4J - added for testing
2026-03-01 12:16:08 +08:00
} ;