fix: multiple savefile fixes

This commit is contained in:
NOTPIES 2026-03-12 00:16:48 -03:00
parent c1a9648bf4
commit ff2cb7b582
13 changed files with 102 additions and 54 deletions

View file

@ -1474,6 +1474,33 @@ void MinecraftServer::tick()
tickCount++;
#ifdef WITH_SERVER_CODE
if (tickCount % 6000 == 0 && !s_bServerHalted)
{
app.DebugPrintf("Auto-saving world...\n");
if (players != NULL)
{
players->saveAll(NULL);
}
for (unsigned int j = 0; j < levels.length; j++)
{
if (s_bServerHalted) break;
ServerLevel *level = levels[levels.length - 1 - j];
if (level) level->save(false, NULL, true);
}
if (!s_bServerHalted)
{
saveGameRules();
levels[0]->saveToDisc(NULL, true);
}
while (StorageManager.GetSaveState() != C4JStorage::ESaveGame_Idle)
{
Sleep(10);
}
app.DebugPrintf("Auto-save complete\n");
}
#endif
// 4J We need to update client difficulty levels based on the servers
Minecraft *pMinecraft = Minecraft::GetInstance();
// 4J-PB - sending this on the host changing the difficulty in the menus

View file

@ -174,11 +174,12 @@ LevelChunk *MultiPlayerChunkCache::create(int x, int z)
// 4J-JEV: We are about to use shared data, abort if the server is stopped and the data is deleted.
if (MinecraftServer::getInstance()->serverHalted()) return NULL;
// If we're the host, then don't create the chunk, share data from the server's copy
// If we're the host, then don't create the chunk, share data from the server's copy
int dimId = level->dimension->id;
#ifdef _LARGE_WORLDS
LevelChunk *serverChunk = MinecraftServer::getInstance()->getLevel(level->dimension->id)->cache->getChunkLoadedOrUnloaded(x,z);
LevelChunk *serverChunk = MinecraftServer::getInstance()->getLevel(dimId)->cache->getChunkLoadedOrUnloaded(x,z);
#else
LevelChunk *serverChunk = MinecraftServer::getInstance()->getLevel(level->dimension->id)->cache->getChunk(x,z);
LevelChunk *serverChunk = MinecraftServer::getInstance()->getLevel(dimId)->cache->getChunk(x,z);
#endif
chunk = new LevelChunk(level, x, z, serverChunk);
// Let renderer know that this chunk has been created - it might have made render data from the EmptyChunk if it got to a chunk before the server sent it

View file

@ -149,8 +149,10 @@ LevelChunk *ServerChunkCache::create(int x, int z, bool asyncPostProcess) // 4J
if( ( chunk == NULL ) || ( chunk->x != x ) || ( chunk->z != z ) )
{
bool wasLoaded = false;
EnterCriticalSection(&m_csLoadCreate);
chunk = load(x, z);
wasLoaded = (chunk != NULL);
if (chunk == NULL)
{
if (source == NULL)
@ -470,7 +472,7 @@ void ServerChunkCache::postProcess(ChunkSource *parent, int x, int z )
{
LevelChunk *chunk = getChunk(x, z);
if ( (chunk->terrainPopulated & LevelChunk::sTerrainPopulatedFromHere) == 0 )
{
{
if (source != NULL)
{
PIXBeginNamedEvent(0,"Main post processing");
@ -478,7 +480,7 @@ void ServerChunkCache::postProcess(ChunkSource *parent, int x, int z )
PIXEndNamedEvent();
chunk->markUnsaved();
}
}
// Flag not only this chunk as being post-processed, but also all the chunks that this post-processing might affect. We can guarantee that these
// chunks exist as that's determined before post-processing can even run

View file

@ -89,7 +89,9 @@ bool WinsockNetLayer::Initialize()
s_initialized = true;
#ifndef WITH_SERVER_CODE
StartDiscovery();
#endif
return true;
}

View file

@ -455,24 +455,27 @@ LevelChunk *OldChunkStorage::load(Level *level, DataInputStream *dis)
CompoundTag *tag = NbtIo::read(dis);
loadEntities(levelChunk, level, tag);
if (tag->contains(L"TileTicks"))
if (tag != NULL)
{
ListTag<CompoundTag> *tileTicks = (ListTag<CompoundTag> *) tag->getList(L"TileTicks");
loadEntities(levelChunk, level, tag);
if (tileTicks != NULL)
if (tag->contains(L"TileTicks"))
{
for (int i = 0; i < tileTicks->size(); i++)
{
CompoundTag *teTag = tileTicks->get(i);
ListTag<CompoundTag> *tileTicks = (ListTag<CompoundTag> *) tag->getList(L"TileTicks");
level->forceAddTileTick(teTag->getInt(L"x"), teTag->getInt(L"y"), teTag->getInt(L"z"), teTag->getInt(L"i"), teTag->getInt(L"t"));
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;
delete tag;
}
return levelChunk;
}

View file

@ -664,7 +664,7 @@ void RandomLevelSource::postProcess(ChunkSource *parent, int xt, int zt)
mineShaftFeature->postProcess(level, pprandom, xt, zt);
hasVillage = villageFeature->postProcess(level, pprandom, xt, zt);
strongholdFeature->postProcess(level, pprandom, xt, zt);
scatteredFeature->postProcess(level, random, xt, zt);
scatteredFeature->postProcess(level, pprandom, xt, zt);
}
PIXEndNamedEvent();
@ -674,7 +674,7 @@ void RandomLevelSource::postProcess(ChunkSource *parent, int xt, int zt)
int x = xo + pprandom->nextInt(16) + 8;
int y = pprandom->nextInt(Level::genDepth);
int z = zo + pprandom->nextInt(16) + 8;
LakeFeature *calmWater = new LakeFeature(Tile::calmWater_Id);
calmWater->place(level, pprandom, x, y, z);
delete calmWater;

View file

@ -38,7 +38,12 @@ RegionFile::RegionFile(ConsoleSaveFile *saveFile, File *path)
}
*/
fileEntry = m_saveFile->createFile( fileName->getName() );
wstring saveName = fileName->getPath();
for (size_t i = 0; i < saveName.size(); i++)
{
if (saveName[i] == L'\\') saveName[i] = L'/';
}
fileEntry = m_saveFile->createFile( ConsoleSavePath(saveName) );
m_saveFile->setFilePointer( fileEntry, 0, NULL, FILE_END );
if ( fileEntry->getFileSize() < SECTOR_BYTES)

View file

@ -17,15 +17,11 @@ bool RegionFileCache::useSplitSaves(ESavePlatform platform)
};
}
RegionFile *RegionFileCache::_getRegionFile(ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ) // 4J - TODO was synchronized
RegionFile *RegionFileCache::_getRegionFile(ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ) // 4J - synchronized restored
{
EnterCriticalSection(&m_cs);
// 4J Jev - changed back to use of the File class.
//char file[MAX_PATH_SIZE];
//sprintf(file,"%s\\region\\r.%d.%d.mcr",basePath,chunkX >> 5,chunkZ >> 5);
//File regionDir(basePath, L"region");
//File file(regionDir, wstring(L"r.") + _toString(chunkX>>5) + L"." + _toString(chunkZ>>5) + L".mcr" );
MemSect(31);
File file;
if(useSplitSaves(saveFile->getSavePlatform()))
@ -46,45 +42,37 @@ RegionFile *RegionFileCache::_getRegionFile(ConsoleSaveFile *saveFile, const wst
// 4J Jev, put back in.
if (ref != NULL)
{
LeaveCriticalSection(&m_cs);
return ref;
}
// 4J Stu - Remove for new save files
/*
if (!regionDir.exists())
{
regionDir.mkdirs();
}
*/
if (cache.size() >= MAX_CACHE_SIZE)
{
_clear();
}
RegionFile *reg = new RegionFile(saveFile, &file);
cache[file] = reg; // 4J - this was originally a softReferenc
cache[file] = reg; // 4J - this was originally a softReference
LeaveCriticalSection(&m_cs);
return reg;
}
void RegionFileCache::_clear() // 4J - TODO was synchronized
void RegionFileCache::_clear() // 4J - synchronized (recursive CS is safe here)
{
EnterCriticalSection(&m_cs);
AUTO_VAR(itEnd, cache.end());
for( AUTO_VAR(it, cache.begin()); it != itEnd; it++ )
{
// 4J - removed try/catch
// try {
RegionFile *regionFile = it->second;
if (regionFile != NULL)
{
regionFile->close();
}
delete regionFile;
// } catch (IOException e) {
// e.printStackTrace();
// }
}
cache.clear();
LeaveCriticalSection(&m_cs);
}
int RegionFileCache::_getSizeDelta(ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ)

View file

@ -10,12 +10,14 @@ private:
static const int MAX_CACHE_SIZE = 256;
unordered_map<File, RegionFile *, FileKeyHash, FileKeyEq> cache;
CRITICAL_SECTION m_cs;
static RegionFileCache s_defaultCache;
public:
// Made public and non-static so we can have a cache for input and output files
RegionFileCache() {}
RegionFileCache() { InitializeCriticalSectionAndSpinCount(&m_cs, 4000); }
~RegionFileCache() { DeleteCriticalSection(&m_cs); }
RegionFile *_getRegionFile(ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ); // 4J - TODO was synchronized
void _clear(); // 4J - TODO was synchronized

View file

@ -602,9 +602,10 @@ bool SparseDataStorage::isCompressed()
void SparseDataStorage::write(DataOutputStream *dos)
{
int count = ( dataAndCount >> 48 ) & 0xffff;
__int64 snapshot = dataAndCount;
int count = ( snapshot >> 48 ) & 0xffff;
dos->writeInt(count);
unsigned char *dataPointer = (unsigned char *)(dataAndCount & 0x0000ffffffffffff);
unsigned char *dataPointer = (unsigned char *)(snapshot & 0x0000ffffffffffff);
byteArray wrapper(dataPointer, count * 128 + 128);
dos->write(wrapper);
}

View file

@ -619,9 +619,10 @@ bool SparseLightStorage::isCompressed()
void SparseLightStorage::write(DataOutputStream *dos)
{
int count = ( dataAndCount >> 48 ) & 0xffff;
__int64 snapshot = dataAndCount;
int count = ( snapshot >> 48 ) & 0xffff;
dos->writeInt(count);
unsigned char *dataPointer = (unsigned char *)(dataAndCount & 0x0000ffffffffffff);
unsigned char *dataPointer = (unsigned char *)(snapshot & 0x0000ffffffffffff);
byteArray wrapper(dataPointer, count * 128 + 128);
dos->write(wrapper);
}

View file

@ -7,12 +7,20 @@
#include "net.minecraft.world.level.h"
#include "LevelData.h"
StructureFeature::StructureFeature()
{
InitializeCriticalSectionAndSpinCount(&m_csCachedStructures, 4000);
}
StructureFeature::~StructureFeature()
{
EnterCriticalSection(&m_csCachedStructures);
for( AUTO_VAR(it, cachedStructures.begin()); it != cachedStructures.end(); it++ )
{
delete it->second;
}
LeaveCriticalSection(&m_csCachedStructures);
DeleteCriticalSection(&m_csCachedStructures);
}
void StructureFeature::addFeature(Level *level, int x, int z, int xOffs, int zOffs, byteArray blocks)
@ -21,10 +29,13 @@ void StructureFeature::addFeature(Level *level, int x, int z, int xOffs, int zOf
// the chunk being generated, but not all chunks are the sources of
// structures
EnterCriticalSection(&m_csCachedStructures);
if (cachedStructures.find(ChunkPos::hashCode(x, z)) != cachedStructures.end())
{
LeaveCriticalSection(&m_csCachedStructures);
return;
}
LeaveCriticalSection(&m_csCachedStructures);
// clear random key
random->nextInt();
@ -32,7 +43,9 @@ void StructureFeature::addFeature(Level *level, int x, int z, int xOffs, int zOf
if (isFeatureChunk(x, z,level->getLevelData()->getGenerator() == LevelType::lvl_flat))
{
StructureStart *start = createStructureStart(x, z);
EnterCriticalSection(&m_csCachedStructures);
cachedStructures[ChunkPos::hashCode(x, z)] = start;
LeaveCriticalSection(&m_csCachedStructures);
}
}
@ -46,6 +59,7 @@ bool StructureFeature::postProcess(Level *level, Random *random, int chunkX, int
int cz = (chunkZ << 4); // + 8;
bool intersection = false;
EnterCriticalSection(&m_csCachedStructures);
for( AUTO_VAR(it, cachedStructures.begin()); it != cachedStructures.end(); it++ )
{
StructureStart *structureStart = it->second;
@ -61,12 +75,14 @@ bool StructureFeature::postProcess(Level *level, Random *random, int chunkX, int
}
}
}
LeaveCriticalSection(&m_csCachedStructures);
return intersection;
}
bool StructureFeature::isIntersection(int cellX, int cellZ)
{
EnterCriticalSection(&m_csCachedStructures);
for( AUTO_VAR(it, cachedStructures.begin()); it != cachedStructures.end(); it++ )
{
StructureStart *structureStart = it->second;
@ -80,12 +96,14 @@ bool StructureFeature::isIntersection(int cellX, int cellZ)
StructurePiece *next = *it2++;
if (next->getBoundingBox()->intersects(cellX, cellZ, cellX, cellZ))
{
LeaveCriticalSection(&m_csCachedStructures);
return true;
}
}
}
}
}
LeaveCriticalSection(&m_csCachedStructures);
return false;
}
@ -94,7 +112,7 @@ bool StructureFeature::isIntersection(int cellX, int cellZ)
///////////////////////////////////////////
bool StructureFeature::isInsideFeature(int cellX, int cellY, int cellZ)
{
//for (StructureStart structureStart : cachedStructures.values())
EnterCriticalSection(&m_csCachedStructures);
for(AUTO_VAR(it, cachedStructures.begin()); it != cachedStructures.end(); ++it)
{
StructureStart *pStructureStart = it->second;
@ -103,14 +121,6 @@ bool StructureFeature::isInsideFeature(int cellX, int cellY, int cellZ)
{
if (pStructureStart->getBoundingBox()->intersects(cellX, cellZ, cellX, cellZ))
{
/*
Iterator<StructurePiece> it = structureStart.getPieces().iterator();
while (it.hasNext()) {
StructurePiece next = it.next();
if (next.getBoundingBox().isInside(cellX, cellY, cellZ)) {
return true;
}
*/
list<StructurePiece *> *pieces=pStructureStart->getPieces();
for ( AUTO_VAR(it2, pieces->begin()); it2 != pieces->end(); it2++ )
@ -118,12 +128,14 @@ bool StructureFeature::isInsideFeature(int cellX, int cellY, int cellZ)
StructurePiece* piece = *it2;
if ( piece->getBoundingBox()->isInside(cellX, cellY, cellZ) )
{
LeaveCriticalSection(&m_csCachedStructures);
return true;
}
}
}
}
}
LeaveCriticalSection(&m_csCachedStructures);
return false;
}
@ -146,6 +158,7 @@ TilePos *StructureFeature::getNearestGeneratedFeature(Level *level, int cellX, i
double minDistance = DBL_MAX;
TilePos *selected = NULL;
EnterCriticalSection(&m_csCachedStructures);
for(AUTO_VAR(it, cachedStructures.begin()); it != cachedStructures.end(); ++it)
{
StructureStart *pStructureStart = it->second;
@ -169,6 +182,7 @@ TilePos *StructureFeature::getNearestGeneratedFeature(Level *level, int cellX, i
}
}
}
LeaveCriticalSection(&m_csCachedStructures);
if (selected != NULL)
{
return selected;

View file

@ -17,8 +17,10 @@ public:
protected:
unordered_map<__int64, StructureStart *> cachedStructures;
CRITICAL_SECTION m_csCachedStructures;
public:
StructureFeature();
~StructureFeature();
virtual void addFeature(Level *level, int x, int z, int xOffs, int zOffs, byteArray blocks);