mirror of
https://github.com/4jcraft/4jcraft.git
synced 2026-04-24 13:13:37 +00:00
CRITICAL_SECTION is reentrant; std::mutex is not. This caused deadlocks during world generation, post-processing, and saving.
353 lines
14 KiB
C++
353 lines
14 KiB
C++
#pragma once
|
|
#include "../../Minecraft.World/Level/Events/LevelListener.h"
|
|
#include "../../Minecraft.World/Util/Definitions.h"
|
|
#include "OffsettedRenderList.h"
|
|
#include "../../Minecraft.World/Util/JavaIntHash.h"
|
|
#include "../../Minecraft.World/Level/Level.h"
|
|
#if !defined(__linux__)
|
|
#include <xmcore.h>
|
|
#endif
|
|
#include <unordered_set>
|
|
#include <mutex>
|
|
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;
|
|
|
|
// 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.
|
|
// 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 split screen reference counting.
|
|
|
|
class LevelRenderer : public LevelListener {
|
|
friend class Chunk;
|
|
|
|
private:
|
|
static ResourceLocation MOON_LOCATION;
|
|
static ResourceLocation MOON_PHASES_LOCATION;
|
|
static ResourceLocation SUN_LOCATION;
|
|
static ResourceLocation CLOUDS_LOCATION;
|
|
static ResourceLocation END_SKY_LOCATION;
|
|
|
|
public:
|
|
static const int CHUNK_XZSIZE = 16;
|
|
#if defined(_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;
|
|
#if defined(_WINDOWS64)
|
|
static const int MAX_COMMANDBUFFER_ALLOCATIONS =
|
|
512 * 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);
|
|
std::wstring gatherStats1();
|
|
std::wstring gatherStats2();
|
|
|
|
private:
|
|
void resortChunks(int xc, int yc, int zc);
|
|
|
|
public:
|
|
int render(std::shared_ptr<LivingEntity> player, int layer, double alpha,
|
|
bool updateChunks);
|
|
|
|
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:
|
|
void renderHit(std::shared_ptr<Player> player, HitResult* h, int mode,
|
|
std::shared_ptr<ItemInstance> inventoryItem, float a);
|
|
void renderDestroyAnimation(Tesselator* t, std::shared_ptr<Player> player,
|
|
float a);
|
|
void renderHitOutline(std::shared_ptr<Player> player, HitResult* h,
|
|
int mode, float a);
|
|
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
|
|
|
|
void cull(Culler* culler, float a);
|
|
void playStreamingMusic(const std::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);
|
|
void playSound(std::shared_ptr<Entity> entity, int iSound, double x,
|
|
double y, double z, float volume, float pitch,
|
|
float fSoundClipDist = 16.0f);
|
|
void playSoundExceptPlayer(std::shared_ptr<Player> player, int iSound,
|
|
double x, double y, double z, float volume,
|
|
float pitch, float fSoundClipDist = 16.0f);
|
|
void addParticle(ePARTICLE_TYPE eParticleType, double x, double y, double z,
|
|
double xa, double ya, double za); // 4J added
|
|
std::shared_ptr<Particle> addParticleInternal(ePARTICLE_TYPE eParticleType,
|
|
double x, double y, double z,
|
|
double xa, double ya,
|
|
double za); // 4J added
|
|
void entityAdded(std::shared_ptr<Entity> entity);
|
|
void entityRemoved(std::shared_ptr<Entity> entity);
|
|
void playerRemoved(std::shared_ptr<Entity> entity) {
|
|
} // 4J added - for when a player is removed from the level's player array,
|
|
// not just the entity storage
|
|
void skyColorChanged();
|
|
void clear();
|
|
void globalLevelEvent(int type, int sourceX, int sourceY, int sourceZ,
|
|
int data);
|
|
void levelEvent(std::shared_ptr<Player> source, int type, int x, int y,
|
|
int z, int data);
|
|
void destroyTileProgress(int id, int x, int y, int z, int progress);
|
|
void registerTextures(IconRegister* iconRegister);
|
|
|
|
struct RenderableTileEntityBucket {
|
|
std::vector<std::shared_ptr<TileEntity> > tiles;
|
|
std::unordered_map<TileEntity*, size_t> indexByTile;
|
|
};
|
|
|
|
typedef std::unordered_map<int, RenderableTileEntityBucket, IntKeyHash,
|
|
IntKeyEq>
|
|
rteMap;
|
|
|
|
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
|
|
rteMap renderableTileEntities; // 4J - changed - was
|
|
// std::vector<std::shared_ptr<TileEntity>,
|
|
// now hashed by chunk so we can find them
|
|
typedef std::unordered_set<TileEntity*> rtePendingRemovalSet;
|
|
typedef std::unordered_map<int, rtePendingRemovalSet, IntKeyHash, IntKeyEq>
|
|
rtePendingRemovalMap;
|
|
rtePendingRemovalMap m_renderableTileEntitiesPendingRemoval;
|
|
std::mutex m_csRenderableTileEntities;
|
|
MultiPlayerLevel* level[4]; // 4J - now one per player
|
|
Textures* textures;
|
|
// std::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;
|
|
std::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];
|
|
void setGlobalChunkConnectivity(int index, uint64_t conn);
|
|
uint64_t getGlobalChunkConnectivity(int index);
|
|
std::vector<ClipChunk*> m_bfsGrid;
|
|
std::vector<uint8_t> m_bfsVisitedFaces[4];
|
|
std::unordered_map<int, BlockDestructionProgress*> destroyingBlocks;
|
|
Icon** breakingTextures;
|
|
|
|
void addRenderableTileEntity_Locked(
|
|
int key, const std::shared_ptr<TileEntity>& tileEntity);
|
|
void eraseRenderableTileEntity_Locked(RenderableTileEntityBucket& bucket,
|
|
TileEntity* tileEntity);
|
|
void queueRenderableTileEntityForRemoval_Locked(int key,
|
|
TileEntity* tileEntity);
|
|
void retireRenderableTileEntitiesForChunkKey(int key);
|
|
|
|
public:
|
|
void fullyFlagRenderableTileEntitiesToBeRemoved(); // 4J added
|
|
|
|
std::recursive_mutex 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() = default;
|
|
};
|
|
std::mutex m_csDestroyedTiles;
|
|
std::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
|
|
static const int MAX_LEVEL_RENDER_SIZE[3];
|
|
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)
|
|
#if defined(_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);
|
|
|
|
static uint64_t* globalChunkConnectivity;
|
|
|
|
// 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;
|
|
#if defined(_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;
|
|
int64_t lastDirtyChunkFound;
|
|
static const int FORCE_DIRTY_CHUNK_CHECK_PERIOD_MS = 250;
|
|
|
|
#if defined(_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(void* lpParam);
|
|
|
|
std::mutex m_csChunkFlags;
|
|
#endif
|
|
void nonStackDirtyChunksAdded();
|
|
|
|
int checkAllPresentChunks(bool* faultFound); // 4J - added for testing
|
|
};
|