4jcraft/Minecraft.World/Level/Storage/OldChunkStorage.cpp
2026-03-13 17:06:56 -05:00

627 lines
21 KiB
C++

#include "../../Platform/stdafx.h"
#include "../../IO/Files/File.h"
#include "../../IO/Streams/InputOutputStream.h"
#include "../../Headers/net.minecraft.world.entity.h"
#include "../../Headers/net.minecraft.world.level.h"
#include "../../Headers/net.minecraft.world.level.chunk.h"
#include "../../Headers/net.minecraft.world.level.tile.entity.h"
#include "../../Headers/net.minecraft.world.level.storage.h"
#include "../../IO/Files/FileHeader.h"
#include "OldChunkStorage.h"
#if defined(_WIN32)
namespace {
inline void* OldChunkStorageTlsGetValue(DWORD key) { return TlsGetValue(key); }
inline void OldChunkStorageTlsSetValue(DWORD key, void* value) {
TlsSetValue(key, value);
}
} // namespace
DWORD OldChunkStorage::tlsIdx = TlsAlloc();
#else
namespace {
pthread_key_t CreateOldChunkStorageTlsKey() {
pthread_key_t key;
const int result = pthread_key_create(&key, nullptr);
assert(result == 0);
return key;
}
inline void* OldChunkStorageTlsGetValue(pthread_key_t key) {
return pthread_getspecific(key);
}
inline void OldChunkStorageTlsSetValue(pthread_key_t key, void* value) {
pthread_setspecific(key, value);
}
} // namespace
pthread_key_t OldChunkStorage::tlsIdx = CreateOldChunkStorageTlsKey();
#endif
OldChunkStorage::ThreadStorage* OldChunkStorage::tlsDefault = NULL;
OldChunkStorage::ThreadStorage::ThreadStorage() {
blockData = byteArray(Level::CHUNK_TILE_COUNT);
dataData = byteArray(Level::HALF_CHUNK_TILE_COUNT);
skyLightData = byteArray(Level::HALF_CHUNK_TILE_COUNT);
blockLightData = byteArray(Level::HALF_CHUNK_TILE_COUNT);
}
OldChunkStorage::ThreadStorage::~ThreadStorage() {
delete[] blockData.data;
delete[] dataData.data;
delete[] skyLightData.data;
delete[] blockLightData.data;
}
void OldChunkStorage::CreateNewThreadStorage() {
ThreadStorage* tls = new ThreadStorage();
if (tlsDefault == NULL) {
tlsDefault = tls;
}
OldChunkStorageTlsSetValue(tlsIdx, tls);
}
void OldChunkStorage::UseDefaultThreadStorage() {
OldChunkStorageTlsSetValue(tlsIdx, tlsDefault);
}
void OldChunkStorage::ReleaseThreadStorage() {
ThreadStorage* tls =
static_cast<ThreadStorage*>(OldChunkStorageTlsGetValue(tlsIdx));
if (tls == tlsDefault) return;
delete tls;
}
OldChunkStorage::OldChunkStorage(File dir, bool create) {
this->dir = dir;
this->create = create;
}
File OldChunkStorage::getFile(int x, int z) {
wchar_t name[MAX_PATH_SIZE];
wchar_t path1[MAX_PATH_SIZE];
wchar_t path2[MAX_PATH_SIZE];
wchar_t xRadix36[64];
wchar_t zRadix36[64];
#if (defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ || \
defined __linux__)
assert(0); // need a gcc verison of _itow ?
#else
_itow(x, xRadix36, 36);
_itow(z, zRadix36, 36);
swprintf(name, MAX_PATH_SIZE, L"c.%ls.%ls.dat", xRadix36, zRadix36);
_itow(x & 63, path1, 36);
_itow(z & 63, path2, 36);
#endif
// sprintf(file,"%s\\%s",dir,path1);
File file(dir, std::wstring(path1));
if (!file.exists()) {
if (create)
file.mkdir();
else {
return File(L"");
}
}
// strcat(file,"\\");
// strcat(file,path2);
file = File(file, std::wstring(path2));
if (!file.exists()) {
if (create)
file.mkdir();
else {
return File(L"");
}
}
// strcat(file,"\\");
// strcat(file,name);
// sprintf(file,"%s\\%s",file,name);
file = File(file, std::wstring(name));
if (!file.exists()) {
if (!create) {
return File(L"");
}
}
return file;
}
LevelChunk* OldChunkStorage::load(Level* level, int x, int z) {
File file = getFile(x, z);
if (!file.getPath().empty() && file.exists()) {
// 4J - removed try/catch
// try {
// System.out.println("Loading chunk "+x+", "+z);
FileInputStream fis = FileInputStream(file);
CompoundTag* tag = NbtIo::readCompressed(&fis);
if (!tag->contains(L"Level")) {
char buf[256];
sprintf(buf,
"Chunk file at %d, %d is missing level data, skipping\n", x,
z);
app.DebugPrintf(buf);
return NULL;
}
if (!tag->getCompound(L"Level")->contains(L"Blocks")) {
char buf[256];
sprintf(buf,
"Chunk file at %d, %d is missing block data, skipping\n", x,
z);
app.DebugPrintf(buf);
return NULL;
}
LevelChunk* levelChunk =
OldChunkStorage::load(level, tag->getCompound(L"Level"));
if (!levelChunk->isAt(x, z)) {
char buf[256];
sprintf(buf,
"Chunk fileat %d, %d is in the wrong location; relocating. "
"Expected %d, %d, got %d, %d\n",
x, z, x, z, levelChunk->x, levelChunk->z);
app.DebugPrintf(buf);
tag->putInt(L"xPos", x);
tag->putInt(L"zPos", z);
levelChunk =
OldChunkStorage::load(level, tag->getCompound(L"Level"));
}
return levelChunk;
// } catch (Exception e) {
// e.printStackTrace();
// }
}
return NULL;
}
void OldChunkStorage::save(Level* level, LevelChunk* levelChunk) {
level->checkSession();
File file = getFile(levelChunk->x, levelChunk->z);
if (file.exists()) {
LevelData* levelData = level->getLevelData();
levelData->setSizeOnDisk(levelData->getSizeOnDisk() - file.length());
}
// 4J - removed try/catch
// try {
// char tmpFileName[MAX_PATH_SIZE];
// sprintf(tmpFileName,"%s\\%s",dir,"tmp_chunk.dat");
File tmpFile(dir, L"tmp_chunk.dat");
// System.out.println("Saving chunk "+levelChunk.x+",
// "+levelChunk.z);
FileOutputStream fos = FileOutputStream(tmpFile);
CompoundTag* tag = new CompoundTag();
CompoundTag* levelData = new CompoundTag();
tag->put(L"Level", levelData);
OldChunkStorage::save(levelChunk, level, levelData);
NbtIo::writeCompressed(tag, &fos);
fos.close();
if (file.exists()) {
// DeleteFile(file);
file._delete();
}
// MoveFile(tmpFile,file);
tmpFile.renameTo(file);
LevelData* levelInfo = level->getLevelData();
levelInfo->setSizeOnDisk(levelInfo->getSizeOnDisk() + file.length());
// } catch (Exception e) {
// e.printStackTrace();
// }
}
bool OldChunkStorage::saveEntities(LevelChunk* lc, Level* level,
CompoundTag* tag) {
// If we saved and it had no entities, and nothing has been added since skip
// this one
if (!lc->lastSaveHadEntities) return false;
lc->lastSaveHadEntities = false;
ListTag<CompoundTag>* entityTags = new ListTag<CompoundTag>();
#ifdef _ENTITIES_RW_SECTION
EnterCriticalRWSection(&lc->m_csEntities, true);
#else
EnterCriticalSection(&lc->m_csEntities);
#endif
for (int i = 0; i < lc->ENTITY_BLOCKS_LENGTH; i++) {
AUTO_VAR(itEnd, lc->entityBlocks[i]->end());
for (std::vector<std::shared_ptr<Entity> >::iterator it =
lc->entityBlocks[i]->begin();
it != itEnd; it++) {
std::shared_ptr<Entity> e = *it;
lc->lastSaveHadEntities = true;
CompoundTag* teTag = new CompoundTag();
if (e->save(teTag)) {
entityTags->add(teTag);
}
}
}
#ifdef _ENTITIES_RW_SECTION
LeaveCriticalRWSection(&lc->m_csEntities, true);
#else
LeaveCriticalSection(&lc->m_csEntities);
#endif
tag->put(L"Entities", entityTags);
return lc->lastSaveHadEntities;
}
void OldChunkStorage::save(LevelChunk* lc, Level* level,
DataOutputStream* dos) {
dos->writeShort(SAVE_FILE_VERSION_NUMBER);
dos->writeInt(lc->x);
dos->writeInt(lc->z);
dos->writeLong(level->getTime());
PIXBeginNamedEvent(0, "Getting block data");
lc->writeCompressedBlockData(dos);
PIXEndNamedEvent();
PIXBeginNamedEvent(0, "Getting data data");
lc->writeCompressedDataData(dos);
PIXEndNamedEvent();
PIXBeginNamedEvent(0, "Getting sky and block light data");
lc->writeCompressedSkyLightData(dos);
lc->writeCompressedBlockLightData(dos);
PIXEndNamedEvent();
dos->write(lc->heightmap);
dos->writeShort(lc->terrainPopulated);
dos->write(lc->getBiomes());
PIXBeginNamedEvent(0, "Saving entities");
CompoundTag* tag = new CompoundTag();
#ifndef SPLIT_SAVES
saveEntities(lc, level, tag);
#endif
PIXBeginNamedEvent(0, "Saving tile entities");
ListTag<CompoundTag>* tileEntityTags = new ListTag<CompoundTag>();
AUTO_VAR(itEnd, lc->tileEntities.end());
for (std::unordered_map<TilePos, std::shared_ptr<TileEntity>,
TilePosKeyHash, TilePosKeyEq>::iterator it =
lc->tileEntities.begin();
it != itEnd; it++) {
std::shared_ptr<TileEntity> te = it->second;
CompoundTag* teTag = new CompoundTag();
te->save(teTag);
tileEntityTags->add(teTag);
}
tag->put(L"TileEntities", tileEntityTags);
PIXEndNamedEvent();
PIXBeginNamedEvent(0, "Saving tile tick data");
std::vector<TickNextTickData>* ticksInChunk =
level->fetchTicksInChunk(lc, false);
if (ticksInChunk != NULL) {
__int64 levelTime = level->getTime();
ListTag<CompoundTag>* tickTags = new ListTag<CompoundTag>();
for (int i = 0; i < ticksInChunk->size(); i++) {
TickNextTickData td = ticksInChunk->at(i);
CompoundTag* teTag = new CompoundTag();
teTag->putInt(L"i", td.tileId);
teTag->putInt(L"x", td.x);
teTag->putInt(L"y", td.y);
teTag->putInt(L"z", td.z);
teTag->putInt(L"t", (int)(td.m_delay - levelTime));
tickTags->add(teTag);
}
tag->put(L"TileTicks", tickTags);
}
delete ticksInChunk;
PIXEndNamedEvent();
NbtIo::write(tag, dos);
delete tag;
PIXEndNamedEvent();
}
void OldChunkStorage::save(LevelChunk* lc, Level* level, CompoundTag* tag) {
level->checkSession();
tag->putInt(L"xPos", lc->x);
tag->putInt(L"zPos", lc->z);
tag->putLong(L"LastUpdate", level->getTime());
// 4J - changes here for new storage. Now have static storage for getting
// lighting data for block, data, and sky & block lighting. This wasn't
// required in the original version as we could just reference the
// information in the level itself, but with our new storage system the full
// data doesn't normally exist & so getSkyLightData/getBlockLightData etc.
// need somewhere to output this data. Making this static so that we aren't
// dynamically allocating memory in the server thread when writing chunks as
// this causes serious stalling on the main thread. Will be fine so long as
// we only actually create tags for once chunk at a time.
// 4J Stu - As we now save on multiple threads, the static data has been
// moved to TLS
ThreadStorage* tls =
static_cast<ThreadStorage*>(OldChunkStorageTlsGetValue(tlsIdx));
PIXBeginNamedEvent(0, "Getting block data");
// static byteArray blockData = byteArray(32768);
lc->getBlockData(tls->blockData);
tag->putByteArray(L"Blocks", tls->blockData);
PIXEndNamedEvent();
PIXBeginNamedEvent(0, "Getting data data");
// static byteArray dataData = byteArray(16384);
lc->getDataData(tls->dataData);
tag->putByteArray(L"Data", tls->dataData);
PIXEndNamedEvent();
PIXBeginNamedEvent(0, "Getting sky and block light data");
// static byteArray skyLightData = byteArray(16384);
// static byteArray blockLightData = byteArray(16384);
lc->getSkyLightData(tls->skyLightData);
lc->getBlockLightData(tls->blockLightData);
tag->putByteArray(L"SkyLight", tls->skyLightData);
tag->putByteArray(L"BlockLight", tls->blockLightData);
PIXEndNamedEvent();
tag->putByteArray(L"HeightMap", lc->heightmap);
tag->putShort(
L"TerrainPopulatedFlags",
lc->terrainPopulated); // 4J - changed from "TerrainPopulated" to
// "TerrainPopulatedFlags" as now stores a
// bitfield, java stores a bool
tag->putByteArray(L"Biomes", lc->getBiomes());
PIXBeginNamedEvent(0, "Saving entities");
#ifndef SPLIT_SAVES
saveEntities(lc, level, tag);
#endif
PIXBeginNamedEvent(0, "Saving tile entities");
ListTag<CompoundTag>* tileEntityTags = new ListTag<CompoundTag>();
AUTO_VAR(itEnd, lc->tileEntities.end());
for (std::unordered_map<TilePos, std::shared_ptr<TileEntity>,
TilePosKeyHash, TilePosKeyEq>::iterator it =
lc->tileEntities.begin();
it != itEnd; it++) {
std::shared_ptr<TileEntity> te = it->second;
CompoundTag* teTag = new CompoundTag();
te->save(teTag);
tileEntityTags->add(teTag);
}
tag->put(L"TileEntities", tileEntityTags);
PIXEndNamedEvent();
PIXBeginNamedEvent(0, "Saving tile tick data");
std::vector<TickNextTickData>* ticksInChunk =
level->fetchTicksInChunk(lc, false);
if (ticksInChunk != NULL) {
__int64 levelTime = level->getTime();
ListTag<CompoundTag>* tickTags = new ListTag<CompoundTag>();
for (int i = 0; i < ticksInChunk->size(); i++) {
TickNextTickData td = ticksInChunk->at(i);
CompoundTag* teTag = new CompoundTag();
teTag->putInt(L"i", td.tileId);
teTag->putInt(L"x", td.x);
teTag->putInt(L"y", td.y);
teTag->putInt(L"z", td.z);
teTag->putInt(L"t", (int)(td.m_delay - levelTime));
tickTags->add(teTag);
}
tag->put(L"TileTicks", tickTags);
}
delete ticksInChunk;
PIXEndNamedEvent();
PIXEndNamedEvent();
}
void OldChunkStorage::loadEntities(LevelChunk* lc, Level* level,
CompoundTag* tag) {
ListTag<CompoundTag>* entityTags =
(ListTag<CompoundTag>*)tag->getList(L"Entities");
if (entityTags != NULL) {
for (int i = 0; i < entityTags->size(); i++) {
CompoundTag* teTag = entityTags->get(i);
std::shared_ptr<Entity> te = EntityIO::loadStatic(teTag, level);
lc->lastSaveHadEntities = true;
if (te != NULL) {
lc->addEntity(te);
}
}
}
ListTag<CompoundTag>* tileEntityTags =
(ListTag<CompoundTag>*)tag->getList(L"TileEntities");
if (tileEntityTags != NULL) {
for (int i = 0; i < tileEntityTags->size(); i++) {
CompoundTag* teTag = tileEntityTags->get(i);
std::shared_ptr<TileEntity> te = TileEntity::loadStatic(teTag);
if (te != NULL) {
lc->addTileEntity(te);
}
}
}
}
LevelChunk* OldChunkStorage::load(Level* level, DataInputStream* dis) {
short version = dis->readShort();
int x = dis->readInt();
int z = dis->readInt();
int time = dis->readLong();
LevelChunk* levelChunk = new LevelChunk(level, x, z);
levelChunk->readCompressedBlockData(dis);
levelChunk->readCompressedDataData(dis);
levelChunk->readCompressedSkyLightData(dis);
levelChunk->readCompressedBlockLightData(dis);
dis->readFully(levelChunk->heightmap);
levelChunk->terrainPopulated = dis->readShort();
// If all neighbours have been post-processed, then we should have done the
// post-post-processing now. Check that this is set as if it isn't then we
// won't be able to send network data for chunks, and we won't ever try and
// set it again as all the directional flags are now already set - should
// only be an issue for old maps before this flag was added.
if ((levelChunk->terrainPopulated &
LevelChunk::sTerrainPopulatedAllNeighbours) ==
LevelChunk::sTerrainPopulatedAllNeighbours) {
levelChunk->terrainPopulated |= LevelChunk::sTerrainPostPostProcessed;
}
dis->readFully(levelChunk->biomes);
CompoundTag* tag = NbtIo::read(dis);
loadEntities(levelChunk, level, tag);
if (tag->contains(L"TileTicks")) {
ListTag<CompoundTag>* tileTicks =
(ListTag<CompoundTag>*)tag->getList(L"TileTicks");
if (tileTicks != NULL) {
for (int i = 0; i < tileTicks->size(); i++) {
CompoundTag* teTag = tileTicks->get(i);
level->forceAddTileTick(
teTag->getInt(L"x"), teTag->getInt(L"y"),
teTag->getInt(L"z"), teTag->getInt(L"i"),
teTag->getInt(L"t"));
}
}
}
delete tag;
return levelChunk;
}
LevelChunk* OldChunkStorage::load(Level* level, CompoundTag* tag) {
int x = tag->getInt(L"xPos");
int z = tag->getInt(L"zPos");
LevelChunk* levelChunk = new LevelChunk(level, x, z);
// 4J - the original code uses the data in the tag directly, but this is now
// just used as a source when creating the compressed data, so we need to
// free up the data in the tag once we are done
levelChunk->setBlockData(tag->getByteArray(L"Blocks"));
delete[] tag->getByteArray(L"Blocks").data;
// levelChunk->blocks = tag->getByteArray(L"Blocks");
// 4J - the original code uses the data in the tag directly, but this is now
// just used as a source when creating the compressed data, so we need to
// free up the data in the tag once we are done
levelChunk->setDataData(tag->getByteArray(L"Data"));
delete[] tag->getByteArray(L"Data").data;
// 4J - changed to use our new methods for accessing lighting
levelChunk->setSkyLightData(tag->getByteArray(L"SkyLight"));
levelChunk->setBlockLightData(tag->getByteArray(L"BlockLight"));
// In the original code (commented out below) constructing DataLayers from
// these arrays uses the data directly and so it doesn't need deleted. The
// new setSkyLightData/setBlockLightData take a copy of the data so we need
// to delete the local one now
delete[] tag->getByteArray(L"SkyLight").data;
delete[] tag->getByteArray(L"BlockLight").data;
// levelChunk->skyLight = new DataLayer(tag->getByteArray(L"SkyLight"),
//level->depthBits); levelChunk->blockLight = new
//DataLayer(tag->getByteArray(L"BlockLight"), level->depthBits);
delete[] levelChunk->heightmap.data;
levelChunk->heightmap = tag->getByteArray(L"HeightMap");
// 4J - TerrainPopulated was a bool (java), then changed to be a byte
// bitfield, then replaced with TerrainPopulatedShort to store a wider
// bitfield
if (tag->get(L"TerrainPopulated")) {
// Java bool type or byte bitfield
levelChunk->terrainPopulated = tag->getByte(L"TerrainPopulated");
if (levelChunk->terrainPopulated >= 1)
levelChunk->terrainPopulated =
LevelChunk::sTerrainPopulatedAllNeighbours |
LevelChunk::sTerrainPostPostProcessed; // Convert from old bool
// type to new bitfield
} else {
// New style short
levelChunk->terrainPopulated = tag->getShort(L"TerrainPopulatedFlags");
// If all neighbours have been post-processed, then we should have done
// the post-post-processing now. Check that this is set as if it isn't
// then we won't be able to send network data for chunks, and we won't
// ever try and set it again as all the directional flags are now
// already set - should only be an issue for old maps before this flag
// was added.
if ((levelChunk->terrainPopulated &
LevelChunk::sTerrainPopulatedAllNeighbours) ==
LevelChunk::sTerrainPopulatedAllNeighbours) {
levelChunk->terrainPopulated |=
LevelChunk::sTerrainPostPostProcessed;
}
}
#if 0
// 4J - removed - we shouldn't need this any more
if (!levelChunk->data->isValid())
{
levelChunk->data = new DataLayer(LevelChunk::BLOCKS_LENGTH, level->depthBits); // 4J - BLOCKS_LENGTH was levelChunk->blocks.length
}
#endif
// 4J removed - we shouldn't need this any more
#if 0
if (levelChunk->heightmap.data == NULL || !levelChunk->skyLight->isValid())
{
static int chunksUpdated = 0;
delete [] levelChunk->heightmap.data;
levelChunk->heightmap = byteArray(16 * 16);
delete levelChunk->skyLight;
levelChunk->skyLight = new DataLayer(levelChunk->blocks.length, level->depthBits);
levelChunk->recalcHeightmap();
}
if (!levelChunk->blockLight->isValid())
{
delete levelChunk->blockLight;
levelChunk->blockLight = new DataLayer(levelChunk->blocks.length, level->depthBits);
levelChunk->recalcBlockLights();
}
#endif
if (tag->contains(L"Biomes")) {
levelChunk->setBiomes(tag->getByteArray(L"Biomes"));
}
loadEntities(levelChunk, level, tag);
if (tag->contains(L"TileTicks")) {
ListTag<CompoundTag>* tileTicks =
(ListTag<CompoundTag>*)tag->getList(L"TileTicks");
if (tileTicks != NULL) {
for (int i = 0; i < tileTicks->size(); i++) {
CompoundTag* teTag = tileTicks->get(i);
level->forceAddTileTick(
teTag->getInt(L"x"), teTag->getInt(L"y"),
teTag->getInt(L"z"), teTag->getInt(L"i"),
teTag->getInt(L"t"));
}
}
}
return levelChunk;
}
void OldChunkStorage::tick() {}
void OldChunkStorage::flush() {}
void OldChunkStorage::saveEntities(Level* level, LevelChunk* levelChunk) {}