Finish up the level split of 3 type of levels: ClientOnly, ClientServer, and ServerOnly

This commit is contained in:
TomTheFurry
2022-06-23 13:22:16 +08:00
parent 74e58f5043
commit b780a975ee
63 changed files with 528 additions and 1228 deletions
@@ -0,0 +1,13 @@
package com.seibel.lod.core.a7;
import com.seibel.lod.core.a7.datatype.column.ColumnRenderLoader;
import com.seibel.lod.core.a7.render.LodQuadTree;
public class Initializer {
public static void init() {
ColumnRenderLoader columnRenderLoader = new ColumnRenderLoader();
LodQuadTree.registerLayerLoader(columnRenderLoader, (byte) 7); // 7 or above
}
}
@@ -1,11 +0,0 @@
package com.seibel.lod.core.a7;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import com.seibel.lod.core.a7.render.RenderDataSource;
import com.seibel.lod.core.a7.render.RenderDataSourceLoader;
import java.util.concurrent.CompletableFuture;
public interface RenderDataProvider {
CompletableFuture<RenderDataSource> createRenderData(RenderDataSourceLoader renderSourceLoader, DhSectionPos pos);
}
@@ -1,8 +1,8 @@
package com.seibel.lod.core.a7;
@Deprecated // Use the DhXXXWorld
public class Server {
public final boolean isSinglePlayer;
public Server(boolean isSinglePlayer) {
this.isSinglePlayer = isSinglePlayer;
}
@@ -1,10 +0,0 @@
package com.seibel.lod.core.a7.data;
import com.seibel.lod.core.a7.pos.DhSectionPos;
public interface LodDataSource {
DataSourceLoader getLatestLoader();
DhSectionPos getSectionPos();
byte getDataDetail();
void setLocalVersion(int localVer);
}
@@ -1,10 +0,0 @@
package com.seibel.lod.core.a7.data;
import com.seibel.lod.core.a7.level.DhClientServerLevel;
import java.io.File;
import java.util.List;
public interface OldFileConverter {
List<DataFile> scanAndConvert(File levelFolder, DhClientServerLevel level);
}
@@ -1,34 +1,42 @@
package com.seibel.lod.core.a7.data;
package com.seibel.lod.core.a7.datatype;
import com.google.common.collect.HashMultimap;
import com.seibel.lod.core.a7.save.io.file.DataMetaFile;
import com.seibel.lod.core.a7.level.ILevel;
import com.seibel.lod.core.a7.save.io.file.DataMetaFile;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
public abstract class DataSourceLoader {
public static final HashMultimap<Long, DataSourceLoader> loaderRegistry = HashMultimap.create();
public static final HashMultimap<Class<? extends LodDataSource>, DataSourceLoader> loaderRegistry = HashMultimap.create();
public final Class<? extends LodDataSource> clazz;
public static final HashMap<Long, Class<? extends LodDataSource>> datatypeIdRegistry = new HashMap<>();
public static DataSourceLoader getLoader(long dataTypeId, byte dataVersion) {
return loaderRegistry.get(datatypeIdRegistry.get(dataTypeId)).stream()
.filter(l -> Arrays.binarySearch(l.loaderSupportedVersions, dataVersion) >= 0)
.findFirst().orElse(null);
}
public static DataSourceLoader getLoader(Class<? extends LodDataSource> clazz, byte dataVersion) {
return loaderRegistry.get(clazz).stream()
.filter(l -> Arrays.binarySearch(l.loaderSupportedVersions, dataVersion) >= 0)
.findFirst().orElse(null);
}
public final long datatypeId;
public final byte[] loaderSupportedVersions;
public final Class<? extends LodDataSource> clazz;
public DataSourceLoader(Class<? extends LodDataSource> clazz, long datatypeId, byte[] loaderSupportedVersions) {
this.datatypeId = datatypeId;
this.loaderSupportedVersions = loaderSupportedVersions;
Arrays.sort(loaderSupportedVersions); // sort to allow fast access
this.clazz = clazz;
if (datatypeIdRegistry.containsKey(datatypeId) && datatypeIdRegistry.get(datatypeId) != clazz) {
throw new IllegalArgumentException("Loader for datatypeId " + datatypeId + " already registered with different class: "
+ datatypeIdRegistry.get(datatypeId) + " != " + clazz);
}
Set<DataSourceLoader> loaders = loaderRegistry.get(datatypeId);
Set<DataSourceLoader> loaders = loaderRegistry.get(clazz);
if (loaders.stream().anyMatch(other -> {
// see if any loaderSupportsVersion conflicts with this one
for (byte otherVer : other.loaderSupportedVersions) {
@@ -40,20 +48,11 @@ public abstract class DataSourceLoader {
+ Arrays.toString(loaderSupportedVersions) + " already registered!");
}
datatypeIdRegistry.put(datatypeId, clazz);
loaderRegistry.put(datatypeId, this);
loaderRegistry.put(clazz, this);
}
// Can return null as meaning the requirement is not met
public abstract LodDataSource loadData(DataMetaFile dataFile, InputStream data, ILevel level) throws IOException;
public List<File> foldersToScan(File levelFolderPath) {
return Collections.emptyList();
}
public static DataSourceLoader getLoader(long dataTypeId, byte loaderVersion) {
return loaderRegistry.get(dataTypeId).stream()
.filter(l -> Arrays.binarySearch(l.loaderSupportedVersions, loaderVersion) >= 0)
.findFirst().orElse(null);
}
}
@@ -0,0 +1,46 @@
package com.seibel.lod.core.a7.datatype;
import com.seibel.lod.core.a7.level.IClientLevel;
import com.seibel.lod.core.a7.render.LodQuadTree;
import com.seibel.lod.core.a7.render.RenderBuffer;
import com.seibel.lod.core.a7.save.io.render.RenderMetaFile;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.atomic.AtomicReference;
public class EmptyRenderSource implements LodRenderSource {
public static final EmptyRenderSource INSTANCE = new EmptyRenderSource();
@Override
public void enableRender(LodQuadTree quadTree) {
}
@Override
public void disableRender() {
}
@Override
public boolean isRenderReady() {
return false;
}
@Override
public void dispose() {
}
@Override
public byte getDetailOffset() {
return 0;
}
@Override
public boolean trySwapRenderBuffer(LodQuadTree quadTree, AtomicReference<RenderBuffer> referenceSlot) {
return false;
}
@Override
public void saveRender(IClientLevel level, RenderMetaFile file, OutputStream dataStream) throws IOException {
throw new UnsupportedOperationException("EmptyRenderSource should NEVER be saved!");
}
}
@@ -0,0 +1,25 @@
package com.seibel.lod.core.a7.datatype;
import com.seibel.lod.core.a7.level.ILevel;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import com.seibel.lod.core.a7.save.io.file.DataMetaFile;
import com.seibel.lod.core.a7.util.IOUtil;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
public interface LodDataSource {
DhSectionPos getSectionPos();
byte getDataDetail();
void setLocalVersion(int localVer);
byte getDataVersion();
// Saving related
void saveData(ILevel level, DataMetaFile file, OutputStream dataStream) throws IOException;
default File generateFilePathAndName(File levelFolderPath, ILevel level, DhSectionPos sectionPos) {
return new File(levelFolderPath, String.format("%s_v%d-%s%s", getClass().getSimpleName(), getDataVersion(),
sectionPos.serialize(), IOUtil.LOD_FILE_EXTENSION));
}
}
@@ -0,0 +1,46 @@
package com.seibel.lod.core.a7.datatype;
import com.seibel.lod.core.a7.level.IClientLevel;
import com.seibel.lod.core.a7.level.ILevel;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import com.seibel.lod.core.a7.render.LodQuadTree;
import com.seibel.lod.core.a7.render.RenderBuffer;
import com.seibel.lod.core.a7.save.io.file.DataMetaFile;
import com.seibel.lod.core.a7.save.io.render.RenderMetaFile;
import com.seibel.lod.core.objects.DHRegionPos;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.atomic.AtomicReference;
/**
* Example on how to register a loader:
* <pre>
public static RenderDataSource testAndConstruct(LodDataSource dataSource, DhSectionPos sectionPos) {
ColumnRenderContainer container = new ColumnRenderContainer(10, -100);
container.startFillData(dataSource);
return container;
}
static {
RenderDataSource.registorLoader(ColumnRenderContainer::testAndConstruct, 0);
}
</pre>
*/
public interface LodRenderSource {
void enableRender(LodQuadTree quadTree);
void disableRender();
boolean isRenderReady();
void dispose(); // notify the container that the parent lodSection is now disposed (can be in loaded or unloaded state)
byte getDetailOffset();
/**
* Try and swap in new render buffer for this section. Note that before this call, there should be no other
* places storing or referencing the render buffer.
* @param referenceSlot The slot for swapping in the new buffer.
* @return True if the swap was successful. False if swap is not needed or if it is in progress.
*/
boolean trySwapRenderBuffer(LodQuadTree quadTree, AtomicReference<RenderBuffer> referenceSlot);
void saveRender(IClientLevel level, RenderMetaFile file, OutputStream dataStream) throws IOException;
}
@@ -0,0 +1,60 @@
package com.seibel.lod.core.a7.datatype;
import com.google.common.collect.HashMultimap;
import com.seibel.lod.core.a7.level.IClientLevel;
import com.seibel.lod.core.a7.save.io.render.RenderMetaFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
public abstract class RenderSourceLoader {
public static final HashMultimap<Class<? extends LodRenderSource>, RenderSourceLoader> loaderRegistry = HashMultimap.create();
public static final HashMap<Long, Class<? extends LodRenderSource>> renderTypeIdRegistry = new HashMap<>();
public static RenderSourceLoader getLoader(long renderTypeId, byte loaderVersion) {
return loaderRegistry.get(renderTypeIdRegistry.get(renderTypeId)).stream()
.filter(l -> Arrays.binarySearch(l.loaderSupportedVersions, loaderVersion) >= 0)
.findFirst().orElse(null);
}
public static RenderSourceLoader getLoader(Class<? extends LodRenderSource> clazz, byte loaderVersion) {
return loaderRegistry.get(clazz).stream()
.filter(l -> Arrays.binarySearch(l.loaderSupportedVersions, loaderVersion) >= 0)
.findFirst().orElse(null);
}
public final Class<? extends LodRenderSource> clazz;
public final long renderTypeId;
public final byte[] loaderSupportedVersions;
public final byte detailOffset;
public RenderSourceLoader(Class<? extends LodRenderSource> clazz, long renderTypeId, byte[] loaderSupportedVersions, byte detailOffset) {
this.renderTypeId = renderTypeId;
this.loaderSupportedVersions = loaderSupportedVersions;
Arrays.sort(loaderSupportedVersions); // sort to allow fast access
this.clazz = clazz;
if (renderTypeIdRegistry.containsKey(renderTypeId) && renderTypeIdRegistry.get(renderTypeId) != clazz) {
throw new IllegalArgumentException("Loader for renderTypeId " + renderTypeId + " already registered with different class: "
+ renderTypeIdRegistry.get(renderTypeId) + " != " + clazz);
}
Set<RenderSourceLoader> loaders = loaderRegistry.get(clazz);
if (loaders.stream().anyMatch(other -> {
// see if any loaderSupportsVersion conflicts with this one
for (byte otherVer : other.loaderSupportedVersions) {
if (Arrays.binarySearch(loaderSupportedVersions, otherVer) >= 0) return true;
}
return false;
})) {
throw new IllegalArgumentException("Loader for class " + clazz + " that supports one of the version in "
+ Arrays.toString(loaderSupportedVersions) + " already registered!");
}
renderTypeIdRegistry.put(renderTypeId, clazz);
loaderRegistry.put(clazz, this);
this.detailOffset = detailOffset;
}
// Can return null as meaning the file is out of date or something
public abstract LodRenderSource loadRender(RenderMetaFile renderFile, InputStream data, IClientLevel level) throws IOException;
public abstract LodRenderSource createRender(LodDataSource dataSource, IClientLevel level);
}
@@ -1,103 +0,0 @@
package com.seibel.lod.core.a7.datatype.column;
import com.seibel.lod.core.a7.data.DataFileHandler;
import com.seibel.lod.core.a7.data.LodDataSource;
import com.seibel.lod.core.a7.data.OldFileConverter;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import com.seibel.lod.core.enums.config.EVerticalQuality;
import com.seibel.lod.core.a7.level.DhClientServerLevel;
import com.seibel.lod.core.objects.a7.data.*;
import com.seibel.lod.core.a7.save.io.file.DataMetaFile;
import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class Alpha6DataLoader extends OldDataSourceLoader implements OldFileConverter {
public static final Alpha6DataLoader INSTANCE = new Alpha6DataLoader();
private Alpha6DataLoader() {
super(OldColumnDatatype.class, OldColumnDatatype.DATA_TYPE_ID, new byte[]{0});
DataFileHandler.CONVERTERS.add(this);
}
@Override
public LodDataSource loadData(DataMetaFile dataFile, InputStream data, DhClientServerLevel level) {
//TODO: Add decompressor here
try (
XZCompressorInputStream xzIn = new XZCompressorInputStream(data);
DataInputStream dis = new DataInputStream(xzIn);
) {
return new OldColumnDatatype(dataFile.pos, dis, dataFile.loaderVersion, level, 1);
} catch (IOException e) {
//FIXME: Log error
return null;
}
}
@Override
public DataSourceSaver getNewSaver() {
return null; // No re-saving of old datatype as any data should be converted to new format before saving
}
private static DataFile convert(File file, int detailLevel, EVerticalQuality quality) {
String oldName = file.getName();
String regionStr = oldName.substring("lod.".length(), oldName.length() - ".xz".length());
String[] parts = regionStr.split("\\.");
if (parts.length != 2) return null;
int regionX = Integer.parseInt(parts[0]);
int regionZ = Integer.parseInt(parts[1]);
ColumnDatatype datatype;
try (FileInputStream fileInStream = new FileInputStream(file)) {
XZCompressorInputStream inputStream = new XZCompressorInputStream(fileInStream);
int fileVersion = inputStream.read();
DhSectionPos pos;
//TODO: Implement
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public List<DataFile> scanAndConvert(File levelFolder, DhClientServerLevel level) {
List<DataFile> files = new ArrayList<>();
List<File> foldersToScan = new ArrayList<>(EVerticalQuality.values().length);
for (EVerticalQuality q : EVerticalQuality.values()) {
File qualityFolder = new File(levelFolder, q.toString());
for (int i = 0; i < 10; i++) {
foldersToScan.add(new File(qualityFolder, "detail-"+i));
}
}
for (EVerticalQuality q : EVerticalQuality.values()) {
for (int i = 0; i < 10; i++) {
File detailFolder = new File(levelFolder, q.toString() + File.pathSeparator + "detail-" + i);
if (!detailFolder.exists() || !detailFolder.isDirectory()) continue;
File[] filesToScan = detailFolder.listFiles();
if (filesToScan == null) continue;
for (File f : filesToScan) {
String fileName = f.getName();
if (!fileName.endsWith(".xz") || fileName.startsWith("lod.")) continue;
DataFile converted = convert(f, i, q);
}
}
}
return files;
}
}
@@ -1,65 +0,0 @@
package com.seibel.lod.core.a7.datatype.column;
import com.seibel.lod.core.a7.data.DataFileHandler;
import com.seibel.lod.core.a7.data.LodDataSource;
import com.seibel.lod.core.a7.save.io.MetaFile;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import com.seibel.lod.core.config.Config;
import com.seibel.lod.core.enums.config.EVerticalQuality;
import com.seibel.lod.core.a7.level.DhClientServerLevel;
import com.seibel.lod.core.a7.save.io.file.DataMetaFile;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class ColumnDataLoader extends DataSourceSaver {
private static final byte COLUMN_DATA_LOADER_VERSION = 1;
public static final ColumnDataLoader INSTANCE = new ColumnDataLoader();
private ColumnDataLoader() {
super(ColumnDatatype.class, ColumnDatatype.DATA_TYPE_ID, new byte[]{COLUMN_DATA_LOADER_VERSION});
}
@Override
public LodDataSource loadData(DataMetaFile dataFile, InputStream data, DhClientServerLevel level) {
try (
//TODO: Add decompressor here
DataInputStream dis = new DataInputStream(data);
) {
return new ColumnDatatype(dataFile.pos, dis, dataFile.loaderVersion, level);
} catch (IOException e) {
//FIXME: Log error
return null;
}
}
@Override
public void saveData(DhClientServerLevel level, LodDataSource loadedData, MetaFile file, OutputStream out) throws IOException {
//TODO: Add compressor here
try (DataOutputStream dos = new DataOutputStream(out)) {
((ColumnDatatype) loadedData).writeData(dos);
}
}
@Override
public File generateFilePathAndName(File levelFolderPath, DhClientServerLevel level, DhSectionPos sectionPos) {
return generateFilePathAndName(levelFolderPath, sectionPos, Config.Client.Graphics.Quality.verticalQuality.get());
}
public File generateFilePathAndName(File levelFolderPath, DhSectionPos sectionPos, EVerticalQuality quality) {
return new File(levelFolderPath, "cache" + File.separator + quality.toString() + File.separator +
String.format("%s_v%d-%s%s", clazz.getSimpleName(), COLUMN_DATA_LOADER_VERSION,
sectionPos.serialize(), DataFileHandler.FILE_EXTENSION));
}
@Override
public List<File> foldersToScan(File levelFolderPath) {
File cacheFolder = new File(levelFolderPath, "cache");
List<File> foldersToScan = new ArrayList<>(EVerticalQuality.values().length);
for (EVerticalQuality q : EVerticalQuality.values()) {
foldersToScan.add(new File(cacheFolder, q.toString()));
}
return foldersToScan;
}
}
@@ -19,6 +19,8 @@
package com.seibel.lod.core.a7.datatype.column;
import com.seibel.lod.core.a7.datatype.column.accessor.ColumnArrayView;
import com.seibel.lod.core.a7.datatype.column.accessor.ColumnDataView;
import com.seibel.lod.core.logging.SpamReducedLogger;
import com.seibel.lod.core.util.ColorUtil;
@@ -30,7 +32,7 @@ import java.util.Arrays;
* @author Leonardo Amato
* @version ??
*/
public class ColumnDataPoint
public class ColumnFormat
{
/*
@@ -1,190 +1,35 @@
package com.seibel.lod.core.a7.datatype.column;
import com.seibel.lod.core.a7.data.LodDataSource;
import com.seibel.lod.core.a7.datatype.full.FullDatatype;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import com.seibel.lod.core.a7.render.RenderDataSource;
import com.seibel.lod.core.a7.render.RenderDataSourceLoader;
import com.seibel.lod.core.a7.level.DhClientServerLevel;
import com.seibel.lod.core.objects.a7.data.DataFile;
import com.seibel.lod.core.util.DetailDistanceUtil;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.a7.datatype.LodDataSource;
import com.seibel.lod.core.a7.level.IClientLevel;
import com.seibel.lod.core.a7.datatype.LodRenderSource;
import com.seibel.lod.core.a7.datatype.RenderSourceLoader;
import com.seibel.lod.core.a7.save.io.render.RenderMetaFile;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
class ColumnRenderLoader extends RenderDataSourceLoader {
public class ColumnRenderLoader extends RenderSourceLoader {
public ColumnRenderLoader() {
super(4);
super(ColumnRenderSource.class, ColumnRenderSource.TYPE_ID, new byte[]{ColumnRenderSource.LATEST_VERSION}, ColumnRenderSource.SECTION_SIZE_OFFSET);
}
@Override
public RenderDataSource construct(List<LodDataSource> dataSources, DhSectionPos sectionPos, DhClientServerLevel level) {
if (dataSources.size() == 0) return null;
// Check for direct casting
if (dataSources.size() == 1 && dataSources.get(0) instanceof ColumnDatatype
&& dataSources.get(0).getSectionPos().equals(sectionPos)
&& dataSources.get(0).getDataDetail() == sectionPos.sectionDetail - ColumnDatatype.SECTION_SIZE_OFFSET) {
// Directly using the data source as the render data source is possible.
return (ColumnDatatype) dataSources.get(0);
public LodRenderSource loadRender(RenderMetaFile dataFile, InputStream data, IClientLevel level) throws IOException {
try (
//TODO: Add decompressor here
DataInputStream dis = new DataInputStream(data);
) {
return new ColumnRenderSource(dataFile.pos, dis, dataFile.dataVersion, level);
}
// Otherwise, we need to create a new render data source, and copy the data from the data sources.
ColumnDatatype renderDataSource = new ColumnDatatype(sectionPos,
DetailDistanceUtil.getMaxVerticalData(sectionPos.sectionDetail - ColumnDatatype.SECTION_SIZE_OFFSET),
level.getMinY());
boolean completeCopy = dataSources.get(0).getSectionPos().getWidth().toBlock() >= sectionPos.getWidth().toBlock();
if (completeCopy) {
// If there is only one data source, we need to insure on copy, we don't copy out of bounds as we
// may just need to copy partial section of the data source.
LodUtil.assertTrue(dataSources.size() == 1, "Expected only one data source for complete copy");
byte targetDataLevel = (byte) (sectionPos.sectionDetail - ColumnDatatype.SECTION_SIZE_OFFSET);
byte sourceDataLevel = dataSources.get(0).getDataDetail();
LodUtil.assertTrue(targetDataLevel >= sourceDataLevel);
if (dataSources.get(0) instanceof IColumnDatatype) {
IColumnDatatype dataSource = (IColumnDatatype) dataSources.get(0);
DhSectionPos srcPos = dataSource.getSectionPos();
// Note that in here, the source data level will be always < target section level
int trgX = sectionPos.getCorner().getX().toBlock();
int trgZ = sectionPos.getCorner().getZ().toBlock();
int trgMaxX = trgX + sectionPos.getWidth().toBlock() - 1;
int trgMaxZ = trgZ + sectionPos.getWidth().toBlock() - 1;
int trgXSizeInSrc = (trgX >> sourceDataLevel) - (trgMaxX >> sourceDataLevel) + 1;
int trgZSizeInSrc = (trgZ >> sourceDataLevel) - (trgMaxZ >> sourceDataLevel) + 1;
int trgXInSrc = (trgX >> sourceDataLevel) % srcPos.getWidth(sourceDataLevel).value;
int trgZInSrc = (trgZ >> sourceDataLevel) % srcPos.getWidth(sourceDataLevel).value;
ColumnQuadView srcView = dataSource.getDataInQuad(trgXInSrc, trgZInSrc, trgXSizeInSrc, trgZSizeInSrc);
ColumnQuadView trgView = renderDataSource.getFullQuad();
trgView.mergeMultiColumnFrom(srcView);
} else {
if (!(dataSources.get(0) instanceof FullDatatype))
throw new IllegalArgumentException("Unsupported data source type: " + dataSources.get(0).getClass().getName());
FullDatatype dataSource = (FullDatatype) dataSources.get(0);
DhSectionPos srcPos = dataSource.getSectionPos();
//TODO: Impl this
LodUtil.assertTrue(false, "Not implemented yet");
}
} else {
// If there are multiple data sources, we need to merge them into the target data source
for (LodDataSource dataSource : dataSources) {
byte targetDataLevel = (byte) (sectionPos.sectionDetail - ColumnDatatype.SECTION_SIZE_OFFSET);
byte sourceDataLevel = dataSource.getDataDetail();
DhSectionPos srcPos = dataSource.getSectionPos();
if (dataSource instanceof IColumnDatatype) {
IColumnDatatype clDataSource = (IColumnDatatype) dataSource;
// Note that targetDataLevel can be > source section level
int srcX = srcPos.getCorner().getX().toBlock();
int srcZ = srcPos.getCorner().getZ().toBlock();
int srcMaxX = srcX + srcPos.getWidth().toBlock() - 1;
int srcMaxZ = srcZ + srcPos.getWidth().toBlock() - 1;
int srcXSizeInTrg = (srcX >> targetDataLevel) - (srcMaxX >> targetDataLevel) + 1;
int srcZSizeInTrg = (srcZ >> targetDataLevel) - (srcMaxZ >> targetDataLevel) + 1;
int srcXInTrg = (srcX >> targetDataLevel) % ColumnDatatype.SECTION_SIZE;
int srcZInTrg = (srcZ >> targetDataLevel) % ColumnDatatype.SECTION_SIZE;
ColumnQuadView srcView = clDataSource.getFullQuad();
ColumnQuadView trgView = renderDataSource.getDataInQuad(srcXInTrg, srcZInTrg, srcXSizeInTrg, srcZSizeInTrg);
trgView.mergeMultiColumnFrom(srcView);
} else {
if (!(dataSource instanceof FullDatatype))
throw new IllegalArgumentException("Unsupported data source type: " + dataSource.getClass().getName());
FullDatatype flDataSource = (FullDatatype) dataSource;
//TODO: Impl this
LodUtil.assertTrue(false, "Not implemented yet");
}
}
}
return renderDataSource;
}
private static boolean IsColumnDatatype(Class<?> clazz) {
return IColumnDatatype.class.isAssignableFrom(clazz);
}
@Override
public List<DataFile> selectFiles(DhSectionPos sectionPos, DhClientServerLevel level, List<DataFile>[] availableFiles) {
byte targetDataLevel = (byte) (sectionPos.sectionDetail - ColumnDatatype.SECTION_SIZE_OFFSET);
//No support for loading higher than the target level yet.
byte maxDataLevel = LodUtil.min((byte) (availableFiles.length - 1), targetDataLevel);
byte topValidDataLevel = Byte.MIN_VALUE;
List<DataFile> selectedFiles = new LinkedList<>();
for (int detail = maxDataLevel; detail >= 0; detail--) {
if (availableFiles[detail] == null) continue;
if (topValidDataLevel == Byte.MIN_VALUE) {
for (DataFile dataFile : availableFiles[detail]) {
if (dataFile.dataLevel > targetDataLevel) continue;
if (IsColumnDatatype(dataFile.dataType) || dataFile.dataType == FullDatatype.class
|| dataFile.dataType == OldColumnDatatype.class) {
topValidDataLevel = LodUtil.max(topValidDataLevel, dataFile.dataLevel);
break;
}
}
}
if (topValidDataLevel == Byte.MIN_VALUE) continue;
DataFile singleCoveringColumnFile = null;
DataFile singleCoveringFullFile = null;
for (DataFile dataFile : availableFiles[detail]) {
if (dataFile.pos.getWidth().toBlock() == sectionPos.getWidth().toBlock()) {
if (IsColumnDatatype(dataFile.dataType)) {
singleCoveringColumnFile = dataFile;
break;
} else if (dataFile.dataType == FullDatatype.class) {
singleCoveringFullFile = dataFile;
// Don't break as there may be a column file later.
}
} else if (dataFile.pos.getWidth().toBlock() > sectionPos.getWidth().toBlock()) {
if (IsColumnDatatype(dataFile.dataType) && singleCoveringColumnFile == null)
singleCoveringColumnFile = dataFile;
else if (dataFile.dataType == FullDatatype.class && singleCoveringFullFile == null)
singleCoveringFullFile = dataFile;
}
}
// First, try select single file that has enough width to cover the section
if (singleCoveringColumnFile != null) return Collections.singletonList(singleCoveringColumnFile);
if (singleCoveringFullFile != null) return Collections.singletonList(singleCoveringFullFile);
// If no single file covers the section, try to select all files without any duplicates
for (DataFile dataFile : availableFiles[detail]) {
boolean isDuplicate = false;
boolean isSet = false;
for (int i = 0; i < selectedFiles.size(); i++) {
DataFile selectedFile = selectedFiles.get(i);
if (selectedFile == null) continue;
if (selectedFile.pos.overlaps(dataFile.pos)) {
// Now, the already selected file muct have same or higher data level
// so, we just select the file with a position that covers the most area.
// Therefore, we choose the file with the higher section level.
if (selectedFile.pos.sectionDetail < dataFile.pos.sectionDetail) {
if (isSet) selectedFiles.set(i, null);
else selectedFiles.set(i, dataFile);
isSet = true;
} else {
LodUtil.assertTrue(!isSet); // We should not have encountered a smaller section level.
// This mean its completely covered by the selected file, so we can skip it.
isDuplicate = true;
break;
}
}
}
if (!isDuplicate && !isSet) selectedFiles.add(dataFile);
}
}
if (topValidDataLevel == Byte.MIN_VALUE) return Collections.emptyList();
selectedFiles.removeIf(Objects::isNull);
return selectedFiles;
public LodRenderSource createRender(LodDataSource dataSource, IClientLevel level) {
//TODO
return null;
}
}
@@ -1,29 +1,35 @@
package com.seibel.lod.core.a7.datatype.column;
import com.seibel.lod.core.a7.data.DataSourceLoader;
import com.seibel.lod.core.a7.datatype.column.accessor.ColumnArrayView;
import com.seibel.lod.core.a7.datatype.column.accessor.ColumnQuadView;
import com.seibel.lod.core.a7.datatype.column.accessor.IColumnDatatype;
import com.seibel.lod.core.a7.datatype.column.render.ColumnRenderBuffer;
import com.seibel.lod.core.a7.level.IClientLevel;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import com.seibel.lod.core.a7.render.RenderBuffer;
import com.seibel.lod.core.a7.save.io.render.RenderMetaFile;
import com.seibel.lod.core.enums.ELodDirection;
import com.seibel.lod.core.objects.LodDataView;
import com.seibel.lod.core.a7.level.DhClientServerLevel;
import com.seibel.lod.core.a7.LodQuadTree;
import com.seibel.lod.core.a7.LodSection;
import com.seibel.lod.core.a7.render.RenderDataSource;
import com.seibel.lod.core.a7.level.ILevel;
import com.seibel.lod.core.a7.render.LodQuadTree;
import com.seibel.lod.core.a7.render.LodSection;
import com.seibel.lod.core.a7.datatype.LodRenderSource;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
public class ColumnDatatype implements RenderDataSource, IColumnDatatype {
public class ColumnRenderSource implements LodRenderSource, IColumnDatatype {
public static final boolean DO_SAFETY_CHECKS = true;
public static final byte SECTION_SIZE_OFFSET = 6;
public static final int SECTION_SIZE = 1 << SECTION_SIZE_OFFSET;
public static final int LATEST_VERSION = 10;
public static final long DATA_TYPE_ID = "ColumnDatatype".hashCode();
public static final byte LATEST_VERSION = 1;
public static final long TYPE_ID = "ColumnRenderSource".hashCode();
public static final int AIR_LODS_SIZE = 16;
public static final int AIR_SECTION_SIZE = SECTION_SIZE/AIR_LODS_SIZE;
@@ -38,7 +44,7 @@ public class ColumnDatatype implements RenderDataSource, IColumnDatatype {
* Constructor of the ColumnDataType
* @param maxVerticalSize the maximum vertical size of the container
*/
public ColumnDatatype(DhSectionPos sectionPos, int maxVerticalSize, int yOffset) {
public ColumnRenderSource(DhSectionPos sectionPos, int maxVerticalSize, int yOffset) {
verticalSize = maxVerticalSize;
dataContainer = new long[SECTION_SIZE * SECTION_SIZE * verticalSize];
airDataContainer = new int[AIR_SECTION_SIZE * AIR_SECTION_SIZE * verticalSize];
@@ -64,13 +70,13 @@ public class ColumnDatatype implements RenderDataSource, IColumnDatatype {
bb.asLongBuffer().get(result);
if (tempMinHeight != yOffset) {
for (int i=0; i<result.length; i++) {
result[i] = ColumnDataPoint.shiftHeightAndDepth(result[i], (short) (tempMinHeight - yOffset));
result[i] = ColumnFormat.shiftHeightAndDepth(result[i], (short) (tempMinHeight - yOffset));
}
}
return result;
}
// Load from data stream with maxVerticalSize loaded from the data stream
public ColumnDatatype(DhSectionPos sectionPos, DataInputStream inputData, int version, DhClientServerLevel level) throws IOException {
public ColumnRenderSource(DhSectionPos sectionPos, DataInputStream inputData, int version, ILevel level) throws IOException {
this.sectionPos = sectionPos;
yOffset = level.getMinY();
byte detailLevel = inputData.readByte();
@@ -87,7 +93,7 @@ public class ColumnDatatype implements RenderDataSource, IColumnDatatype {
{
for (int verticalIndex = 0; verticalIndex < verticalSize; verticalIndex++)
dataContainer[posX * SECTION_SIZE * verticalSize + posZ * verticalSize + verticalIndex] =
ColumnDataPoint.EMPTY_DATA;
ColumnFormat.EMPTY_DATA;
}
@@ -109,7 +115,7 @@ public class ColumnDatatype implements RenderDataSource, IColumnDatatype {
throw new IllegalArgumentException("Z position is out of bounds");
}
int index = posX * SECTION_SIZE * verticalSize + posZ * verticalSize;
int compare = ColumnDataPoint.compareDatapointPriority(data.get(0), dataContainer[index]);
int compare = ColumnFormat.compareDatapointPriority(data.get(0), dataContainer[index]);
if (override) {
if (compare<0) return false;
} else {
@@ -158,12 +164,11 @@ public class ColumnDatatype implements RenderDataSource, IColumnDatatype {
@Override
public boolean doesItExist(int posX, int posZ)
{
return ColumnDataPoint.doesItExist(getSingleData(posX, posZ));
return ColumnFormat.doesItExist(getSingleData(posX, posZ));
}
@Override
public void generateData(ColumnDatatype lowerDataContainer, int posX, int posZ)
public void generateData(IColumnDatatype lowerDataContainer, int posX, int posZ)
{
ColumnQuadView quadView = lowerDataContainer.getDataInQuad(posX*2, posZ*2, 2,2);
ColumnArrayView outputView = getVerticalDataView(posX, posZ);
@@ -185,7 +190,7 @@ public class ColumnDatatype implements RenderDataSource, IColumnDatatype {
long current = dataContainer[i * verticalSize + j];
output.writeLong(Long.reverseBytes(current));
}
if (!ColumnDataPoint.doesItExist(dataContainer[i]))
if (!ColumnFormat.doesItExist(dataContainer[i]))
allGenerated = false;
}
return allGenerated;
@@ -228,27 +233,10 @@ public class ColumnDatatype implements RenderDataSource, IColumnDatatype {
return (long) dataContainer.length * Long.BYTES;
}
public static ColumnRenderLoader COLUMN_LAYER_LOADER;
private static boolean hasRendered = false;
public static void REGISTER() { //FIXME: THIS IS A MESS
if (hasRendered) return;
COLUMN_LAYER_LOADER = new ColumnRenderLoader();
LodQuadTree.registerLayerLoader(COLUMN_LAYER_LOADER, (byte) 7); // 7 or above
hasRendered = true;
}
@Override
public DataSourceLoader getLatestLoader() {
return ColumnDataLoader.INSTANCE;
}
@Override
public DhSectionPos getSectionPos() {
return sectionPos;
}
@Override
public byte getDataDetail() {
return (byte) (sectionPos.sectionDetail - SECTION_SIZE_OFFSET);
}
@@ -264,11 +252,11 @@ public class ColumnDatatype implements RenderDataSource, IColumnDatatype {
private void tryBuildBuffer(LodQuadTree quadTree) {
if (inBuildRenderBuffer == null) {
ColumnDatatype[] data = new ColumnDatatype[ELodDirection.ADJ_DIRECTIONS.length];
ColumnRenderSource[] data = new ColumnRenderSource[ELodDirection.ADJ_DIRECTIONS.length];
for (ELodDirection direction : ELodDirection.ADJ_DIRECTIONS) {
LodSection section = quadTree.getSection(sectionPos.getAdjacent(direction)); //FIXME: Handle traveling through different detail levels
if (section.getRenderContainer() != null && section.getRenderContainer() instanceof ColumnRenderBuffer) {
data[direction.ordinal()-2] = ((ColumnDatatype) section.getRenderContainer());
data[direction.ordinal()-2] = ((ColumnRenderSource) section.getRenderContainer());
}
}
inBuildRenderBuffer = ColumnRenderBuffer.build(usedBuffer, this, data);
@@ -305,7 +293,7 @@ public class ColumnDatatype implements RenderDataSource, IColumnDatatype {
public boolean trySwapRenderBuffer(LodQuadTree quadTree, AtomicReference<RenderBuffer> referenceSlot) {
if (inBuildRenderBuffer != null && inBuildRenderBuffer.isDone()) {
RenderBuffer oldBuffer = referenceSlot.getAndSet(inBuildRenderBuffer.join());
if (oldBuffer != null && oldBuffer instanceof ColumnRenderBuffer) usedBuffer = (ColumnRenderBuffer) oldBuffer;
if (oldBuffer instanceof ColumnRenderBuffer) usedBuffer = (ColumnRenderBuffer) oldBuffer;
inBuildRenderBuffer = null;
return true;
} else {
@@ -314,6 +302,10 @@ public class ColumnDatatype implements RenderDataSource, IColumnDatatype {
return false;
}
@Override
public void saveRender(IClientLevel level, RenderMetaFile file, OutputStream dataStream) throws IOException {
try (DataOutputStream dos = new DataOutputStream(dataStream)) {
writeData(dos);
}
}
}
@@ -1,31 +0,0 @@
package com.seibel.lod.core.a7.datatype.column;
import com.seibel.lod.core.a7.data.DataFileHandler;
import com.seibel.lod.core.a7.data.DataSourceLoader;
import com.seibel.lod.core.a7.data.LodDataSource;
import com.seibel.lod.core.a7.save.io.MetaFile;
import com.seibel.lod.core.a7.level.ILevel;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import com.seibel.lod.core.a7.level.DhClientServerLevel;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
public abstract class DataSourceSaver extends DataSourceLoader {
public DataSourceSaver(Class<? extends LodDataSource> clazz, long datatypeId, byte[] loaderSupportedVersions) {
super(clazz, datatypeId, loaderSupportedVersions);
}
public abstract void saveData(ILevel level, LodDataSource loadedData, MetaFile file, OutputStream dataStream) throws IOException;
// generate the default file path and file name based on various parameters.
// Ensure the file extension is '.lod'!
public File generateFilePathAndName(File levelFolderPath, DhClientServerLevel level, DhSectionPos sectionPos) {
return new File(levelFolderPath, String.format("%s_v%d-%s%s", clazz.getSimpleName(), getSaverVersion(),
sectionPos.serialize(), DataFileHandler.FILE_EXTENSION));
}
public byte getSaverVersion() {
return loaderSupportedVersions[loaderSupportedVersions.length - 1];
}
}
@@ -1,249 +0,0 @@
package com.seibel.lod.core.a7.datatype.column;
import com.seibel.lod.core.a7.data.DataSourceLoader;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import com.seibel.lod.core.objects.LodDataView;
import com.seibel.lod.core.a7.level.DhClientServerLevel;
import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class OldColumnDatatype implements IColumnDatatype {
public static final boolean DO_SAFETY_CHECKS = true;
public static final int LATEST_VERSION = 10;
public static final long DATA_TYPE_ID = "OldColumnDatatype".hashCode();
public final byte sectionSizeOffset;
public final int getSectSize() {
return 1 << sectionSizeOffset;
}
public final int verticalSize;
public final DhSectionPos sectionPos;
public final int yOffset;
public final long[] dataContainer;
/**
* Constructor of the ColumnDataType
* @param maxVerticalSize the maximum vertical size of the container
*/
public OldColumnDatatype(DhSectionPos sectionPos, int maxVerticalSize, int yOffset, int sectionSizeOffset) {
this.sectionSizeOffset = (byte) sectionSizeOffset;
verticalSize = maxVerticalSize;
dataContainer = new long[getSectSize() * getSectSize() * verticalSize];
this.sectionPos = sectionPos;
this.yOffset = yOffset;
}
private long[] loadData(DataInputStream inputData, int version, int verticalSize) throws IOException {
switch (version) {
case 6:
return readDataVersion6(inputData, verticalSize);
case 7:
return readDataVersion7(inputData, verticalSize);
case 8:
return readDataVersion8(inputData, verticalSize);
case 9:
case 10:
return readDataVersion9(inputData, verticalSize);
default:
throw new IOException("Invalid Data: The version of the data is not supported");
}
}
// Load from data stream with maxVerticalSize loaded from the data stream
public OldColumnDatatype(DhSectionPos sectionPos, DataInputStream inputData, int version, DhClientServerLevel level, int sectionSizeOffset) throws IOException {
this.sectionSizeOffset = (byte) sectionSizeOffset;
this.sectionPos = sectionPos;
yOffset = level.getMinY();
byte detailLevel = inputData.readByte();
if (sectionPos.sectionDetail - sectionSizeOffset != detailLevel) {
throw new IOException("Invalid data: detail level does not match");
}
verticalSize = inputData.readByte() & 0b01111111;
dataContainer = loadData(inputData, version, verticalSize);
}
@Override
public byte getDetailOffset() {
return sectionSizeOffset;
}
public void clear(int posX, int posZ)
{
throw new UnsupportedOperationException("OldColumnDatatype only supports read-only access." +
" Convert to ColumnDatatype first before doing any modifications.");
}
public boolean addData(long data, int posX, int posZ, int verticalIndex)
{
throw new UnsupportedOperationException("OldColumnDatatype only supports read-only access." +
" Convert to ColumnDatatype first before doing any modifications.");
}
public boolean copyVerticalData(LodDataView data, int posX, int posZ, boolean override) {
throw new UnsupportedOperationException("OldColumnDatatype only supports read-only access." +
" Convert to ColumnDatatype first before doing any modifications.");
}
public long getData(int posX, int posZ, int verticalIndex)
{
return dataContainer[posX * getSectSize() * verticalSize + posZ * verticalSize + verticalIndex];
}
public long[] getAllData(int posX, int posZ)
{
long[] result = new long[verticalSize];
int index = posX * getSectSize() * verticalSize + posZ * verticalSize;
System.arraycopy(dataContainer, index, result, 0, verticalSize);
return result;
}
public ColumnArrayView getVerticalDataView(int posX, int posZ) {
return new ColumnArrayView(dataContainer, verticalSize,
posX * getSectSize() * verticalSize + posZ * verticalSize, verticalSize);
}
public ColumnQuadView getDataInQuad(int quadX, int quadZ, int quadXSize, int quadZSize) {
return new ColumnQuadView(dataContainer, getSectSize(), verticalSize, quadX, quadZ, quadXSize, quadZSize);
}
public ColumnQuadView getFullQuad() {
return new ColumnQuadView(dataContainer, getSectSize(), verticalSize, 0, 0, getSectSize(), getSectSize());
}
public int getVerticalSize()
{
return verticalSize;
}
public boolean doesItExist(int posX, int posZ)
{
return ColumnDataPoint.doesItExist(getSingleData(posX, posZ));
}
@Override
public void generateData(ColumnDatatype lowerDataContainer, int posX, int posZ) {
throw new UnsupportedOperationException("OldColumnDatatype only supports read-only access." +
" Convert to ColumnDatatype first before doing any modifications.");
}
private long[] readDataVersion6(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
int x = getSectSize() * getSectSize() * tempMaxVerticalData;
byte[] data = new byte[x * Long.BYTES];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
inputData.readFully(data);
long[] result = new long[x];
bb.asLongBuffer().get(result);
patchVersion9Reorder(result);
patchHeightAndDepth(result,-yOffset);
return result;
}
private long[] readDataVersion7(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
int x = getSectSize() * getSectSize() * tempMaxVerticalData;
byte[] data = new byte[x * Long.BYTES];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
inputData.readFully(data);
long[] result = new long[x];
bb.asLongBuffer().get(result);
patchVersion9Reorder(result);
patchHeightAndDepth(result, 64 - yOffset);
return result;
}
private long[] readDataVersion8(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
int x = getSectSize() * getSectSize() * tempMaxVerticalData;
byte[] data = new byte[x * Long.BYTES];
short tempMinHeight = Short.reverseBytes(inputData.readShort());
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
inputData.readFully(data);
long[] result = new long[x];
bb.asLongBuffer().get(result);
patchVersion9Reorder(result);
if (tempMinHeight != yOffset) {
patchHeightAndDepth(result,tempMinHeight - yOffset);
}
return result;
}
private long[] readDataVersion9(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
int x = getSectSize() * getSectSize() * tempMaxVerticalData;
byte[] data = new byte[x * Long.BYTES];
short tempMinHeight = Short.reverseBytes(inputData.readShort());
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
inputData.readFully(data);
long[] result = new long[x];
bb.asLongBuffer().get(result);
if (tempMinHeight != yOffset) {
patchHeightAndDepth(result,tempMinHeight - yOffset);
}
return result;
}
private static void patchHeightAndDepth(long[] data, int offset) {
for (int i=0; i<data.length; i++) {
data[i] = ColumnDataPoint.shiftHeightAndDepth(data[i], (short)offset);
}
}
private static void patchVersion9Reorder(long[] data) {
for (int i=0; i<data.length; i++) {
data[i] = ColumnDataPoint.version9Reorder(data[i]);
}
}
@Override
public String toString()
{
String LINE_DELIMITER = "\n";
String DATA_DELIMITER = " ";
String SUBDATA_DELIMITER = ",";
StringBuilder stringBuilder = new StringBuilder();
int size = sectionPos.getWidth().value;
stringBuilder.append(sectionPos);
stringBuilder.append("(LEGACY-READ-ONLY)");
stringBuilder.append(LINE_DELIMITER);
for (int z = 0; z < size; z++)
{
for (int x = 0; x < size; x++)
{
for (int y = 0; y < verticalSize; y++) {
//Converting the dataToHex
stringBuilder.append(Long.toHexString(getData(x,z,y)));
if (y != verticalSize-1) stringBuilder.append(SUBDATA_DELIMITER);
}
if (x != size-1) stringBuilder.append(DATA_DELIMITER);
}
if (z != size-1) stringBuilder.append(LINE_DELIMITER);
}
return stringBuilder.toString();
}
@Override
public int getMaxNumberOfLods()
{
return getSectSize() * getSectSize() * getVerticalSize();
}
@Override
public long getRoughRamUsage()
{
return (long) dataContainer.length * Long.BYTES;
}
@Override
public DataSourceLoader getLatestLoader() {
return Alpha6DataLoader.INSTANCE;
}
@Override
public DhSectionPos getSectionPos() {
return sectionPos;
}
@Override
public byte getDataDetail() {
return (byte) (sectionPos.sectionDetail - sectionSizeOffset);
}
}
@@ -1,14 +0,0 @@
package com.seibel.lod.core.a7.datatype.column;
import com.seibel.lod.core.a7.data.DataSourceLoader;
import com.seibel.lod.core.a7.data.LodDataSource;
public abstract class OldDataSourceLoader extends DataSourceLoader {
// Note: clazz can be null if the class no longer exists, as long as
// the datatypeId have not been changed or overwritten.
public OldDataSourceLoader(Class<? extends LodDataSource> clazz, long datatypeId, byte[] loaderVersions) {
super(clazz, datatypeId, loaderVersions);
}
abstract public DataSourceSaver getNewSaver();
}
@@ -1,6 +1,8 @@
package com.seibel.lod.core.a7.datatype.column;
package com.seibel.lod.core.a7.datatype.column.accessor;
import com.seibel.lod.core.a7.datatype.column.ColumnFormat;
import java.util.Arrays;
public final class ColumnArrayView implements ColumnDataView {
@@ -78,12 +80,12 @@ public final class ColumnArrayView implements ColumnDataView {
boolean anyChange = false;
for (int o=0; o<(source.size()*vertSize); o+=vertSize) {
if (override) {
if (ColumnDataPoint.compareDatapointPriority(source.get(o), get(o)) >= 0) {
if (ColumnFormat.compareDatapointPriority(source.get(o), get(o)) >= 0) {
anyChange = true;
System.arraycopy(source.data, source.offset+o, data, offset+o, vertSize);
}
} else {
if (ColumnDataPoint.compareDatapointPriority(source.get(o), get(o)) > 0) {
if (ColumnFormat.compareDatapointPriority(source.get(o), get(o)) > 0) {
anyChange = true;
System.arraycopy(source.data, source.offset+o, data, offset+o, vertSize);
}
@@ -100,7 +102,7 @@ public final class ColumnArrayView implements ColumnDataView {
copyFrom(source);
} else {
for (int i=0; i<dataCount(); i++) {
ColumnDataPoint.mergeMultiData(source.subView(i, 1), subView(i, 1));
ColumnFormat.mergeMultiData(source.subView(i, 1), subView(i, 1));
}
}
}
@@ -109,6 +111,6 @@ public final class ColumnArrayView implements ColumnDataView {
if (dataCount() != 1) {
throw new IllegalArgumentException("output dataCount must be 1");
}
ColumnDataPoint.mergeMultiData(source, this);
ColumnFormat.mergeMultiData(source, this);
}
}
@@ -1,4 +1,4 @@
package com.seibel.lod.core.a7.datatype.column;
package com.seibel.lod.core.a7.datatype.column.accessor;
import java.util.Iterator;
@@ -1,4 +1,4 @@
package com.seibel.lod.core.a7.datatype.column;
package com.seibel.lod.core.a7.datatype.column.accessor;
public class ColumnQuadView implements ColumnDataView {
private final long[] data;
@@ -1,9 +1,8 @@
package com.seibel.lod.core.a7.datatype.column;
package com.seibel.lod.core.a7.datatype.column.accessor;
import com.seibel.lod.core.a7.data.LodDataSource;
import com.seibel.lod.core.objects.LodDataView;
public interface IColumnDatatype extends LodDataSource {
public interface IColumnDatatype {
byte getDetailOffset();
default int getDataSize() {
return 1 << getDetailOffset();
@@ -33,5 +32,5 @@ public interface IColumnDatatype extends LodDataSource {
* @param override if override is true we can override data created with same generation mode
*/
boolean copyVerticalData(LodDataView data, int posX, int posZ, boolean override);
void generateData(ColumnDatatype lowerDataContainer, int posX, int posZ);
void generateData(IColumnDatatype lowerDataContainer, int posX, int posZ);
}
@@ -17,8 +17,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.core.a7.datatype.column;
package com.seibel.lod.core.a7.datatype.column.render;
import com.seibel.lod.core.a7.datatype.column.accessor.ColumnArrayView;
import com.seibel.lod.core.builders.lodBuilding.bufferBuilding.LodQuadBuilder;
import com.seibel.lod.core.enums.ELodDirection;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
@@ -1,6 +1,8 @@
package com.seibel.lod.core.a7.datatype.column;
package com.seibel.lod.core.a7.datatype.column.render;
import com.seibel.lod.core.a7.UncheckedInterruptedException;
import com.seibel.lod.core.a7.datatype.column.ColumnRenderSource;
import com.seibel.lod.core.a7.datatype.column.accessor.ColumnArrayView;
import com.seibel.lod.core.a7.util.UncheckedInterruptedException;
import com.seibel.lod.core.a7.render.RenderBuffer;
import com.seibel.lod.core.config.Config;
import com.seibel.lod.core.api.internal.ClientApi;
@@ -180,7 +182,7 @@ public class ColumnRenderBuffer extends RenderBuffer {
}
public static CompletableFuture<ColumnRenderBuffer> build(ColumnRenderBuffer usedBuffer, ColumnDatatype data, ColumnDatatype[] adjData) {
public static CompletableFuture<ColumnRenderBuffer> build(ColumnRenderBuffer usedBuffer, ColumnRenderSource data, ColumnRenderSource[] adjData) {
EVENT_LOGGER.trace("RenderRegion startBuild @ {}", data.sectionPos);
return CompletableFuture.supplyAsync(() -> {
try {
@@ -236,7 +238,7 @@ public class ColumnRenderBuffer extends RenderBuffer {
private static void makeLodRenderData(LodQuadBuilder quadBuilder, ColumnDatatype region, ColumnDatatype[] adjRegions) {
private static void makeLodRenderData(LodQuadBuilder quadBuilder, ColumnRenderSource region, ColumnRenderSource[] adjRegions) {
// Variable initialization
EDebugMode debugMode = Config.Client.Advanced.Debugging.debugMode.get();
@@ -274,7 +276,7 @@ public class ColumnRenderBuffer extends RenderBuffer {
int zAdj = z + lodDirection.getNormal().z;
boolean isCrossRegionBoundary = (xAdj < 0 || xAdj >= dataSize) ||
(zAdj < 0 || zAdj >= dataSize);
ColumnDatatype adjRegion;
ColumnRenderSource adjRegion;
byte adjDetail;
//we check if the detail of the adjPos is equal to the correct one (region border fix)
@@ -1,4 +1,4 @@
package com.seibel.lod.core.a7.data;
package com.seibel.lod.core.a7.datatype.full;
// Static class for the data format:
// ID: blockState id Y: Height(signed) DP: Depth(signed?)
@@ -1,4 +1,4 @@
package com.seibel.lod.core.a7;
package com.seibel.lod.core.a7.datatype.full;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockDetailWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
@@ -1,22 +0,0 @@
package com.seibel.lod.core.a7.datatype.full;
import com.seibel.lod.core.a7.data.DataSourceLoader;
import com.seibel.lod.core.a7.data.LodDataSource;
import com.seibel.lod.core.a7.pos.DhSectionPos;
public class FullDatatype implements LodDataSource {
@Override
public DataSourceLoader getLatestLoader() {
return null;
}
@Override
public DhSectionPos getSectionPos() {
return null;
}
@Override
public byte getDataDetail() {
return 0;
}
}
@@ -1,25 +1,25 @@
package com.seibel.lod.core.a7.data;
package com.seibel.lod.core.a7.datatype.full;
import com.seibel.lod.core.a7.IdMappingUtil;
import com.seibel.lod.core.a7.level.ILevel;
import com.seibel.lod.core.a7.save.io.file.DataMetaFile;
import com.seibel.lod.core.a7.datatype.LodDataSource;
import com.seibel.lod.core.a7.util.IdMappingUtil;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
public class CompleteDataContainer implements LodDataSource { // 1 chunk
public class FullFormat implements LodDataSource { // 1 chunk
private DhSectionPos sectionPos;
ArrayList<String> idMap;
protected CompleteDataContainer() {
protected FullFormat() {
idMap = new ArrayList<String>();
}
@Override
public DataSourceLoader getLatestLoader() {
return null;
}
@Override
public DhSectionPos getSectionPos() {
return sectionPos;
@@ -30,8 +30,23 @@ public class CompleteDataContainer implements LodDataSource { // 1 chunk
return 0;
}
public static CompleteDataContainer createNewFromChunk(IChunkWrapper chunk) {
CompleteDataContainer dataContainer = new CompleteDataContainer();
@Override
public void setLocalVersion(int localVer) {
}
@Override
public byte getDataVersion() {
return 0;
}
@Override
public void saveData(ILevel level, DataMetaFile file, OutputStream dataStream) throws IOException {
}
public static FullFormat createNewFromChunk(IChunkWrapper chunk) {
FullFormat dataContainer = new FullFormat();
HashMap<String, Integer> idMap = new HashMap<String, Integer>();
idMap.put(IdMappingUtil.BLOCKSTATE_ID_AIR, 0);
@@ -1,5 +1,6 @@
package com.seibel.lod.core.a7;
package com.seibel.lod.core.a7.datatype.full;
import com.seibel.lod.core.a7.render.LodSection;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockDetailWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
@@ -1,7 +1,7 @@
package com.seibel.lod.core.a7.level;
import com.seibel.lod.core.a7.LodQuadTree;
import com.seibel.lod.core.a7.save.io.FileScanner;
import com.seibel.lod.core.a7.render.LodQuadTree;
import com.seibel.lod.core.a7.util.FileScanner;
import com.seibel.lod.core.a7.save.io.file.RemoteDataFileHandler;
import com.seibel.lod.core.a7.save.io.render.RenderFileHandler;
import com.seibel.lod.core.a7.pos.DhBlockPos2D;
@@ -15,7 +15,6 @@ import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.ILevelWrapper;
import java.util.Scanner;
import java.util.concurrent.CompletableFuture;
public class DhClientLevel implements IClientLevel {
@@ -49,12 +48,6 @@ public class DhClientLevel implements IClientLevel {
//TODO
}
@Override
public void startRenderer() {
//TODO
}
@Override
public void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler) {
if (renderer == null) {
@@ -63,12 +56,6 @@ public class DhClientLevel implements IClientLevel {
renderer.drawLODs(mcModelViewMatrix, mcProjectionMatrix, partialTicks, profiler);
}
@Override
public void stopRenderer() {
//TODO
}
@Override
public RenderBufferHandler getRenderBufferHandler() {
return renderBufferHandler;
@@ -1,7 +1,7 @@
package com.seibel.lod.core.a7.level;
import com.seibel.lod.core.a7.LodQuadTree;
import com.seibel.lod.core.a7.save.io.FileScanner;
import com.seibel.lod.core.a7.render.LodQuadTree;
import com.seibel.lod.core.a7.util.FileScanner;
import com.seibel.lod.core.a7.save.io.file.LocalDataFileHandler;
import com.seibel.lod.core.a7.save.io.render.RenderFileHandler;
import com.seibel.lod.core.a7.pos.DhBlockPos2D;
@@ -38,12 +38,14 @@ public class DhClientServerLevel implements IClientLevel, IServerLevel {
FileScanner.scanFile(save, level, dataFileHandler, renderFileHandler);
}
public void tick() {
public void clientTick() {
tree.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()));
renderBufferHandler.update();
}
@Override
public void serverTick() {
//TODO Update network packet and stuff or state or etc..
}
public void startRenderer() {
//TODO
}
@@ -56,7 +58,6 @@ public class DhClientServerLevel implements IClientLevel, IServerLevel {
renderer.drawLODs(mcModelViewMatrix, mcProjectionMatrix, partialTicks, profiler);
}
@Override
public void stopRenderer() {
renderFileHandler.flushAndSave(); //Ignore the completion feature so that this action is async
//TODO
@@ -1,6 +1,6 @@
package com.seibel.lod.core.a7.level;
import com.seibel.lod.core.a7.save.io.FileScanner;
import com.seibel.lod.core.a7.util.FileScanner;
import com.seibel.lod.core.a7.save.io.file.LocalDataFileHandler;
import com.seibel.lod.core.a7.save.structure.LocalSaveStructure;
import com.seibel.lod.core.wrapperInterfaces.world.ILevelWrapper;
@@ -19,7 +19,7 @@ public class DhServerLevel implements IServerLevel {
FileScanner.scanFile(save, level, dataFileHandler, null);
}
public void tick() {
public void serverTick() {
//Nothing for now
}
@@ -5,9 +5,9 @@ import com.seibel.lod.core.objects.math.Mat4f;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
public interface IClientLevel extends ILevel {
void startRenderer();
void clientTick();
void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler);
void stopRenderer();
RenderBufferHandler getRenderBufferHandler();
}
@@ -1,5 +1,6 @@
package com.seibel.lod.core.a7.level;
public interface IServerLevel extends ILevel {
void serverTick();
void doWorldGen();
}
@@ -1,42 +0,0 @@
package com.seibel.lod.core.a7.render;
import com.seibel.lod.core.a7.LodQuadTree;
import java.util.concurrent.atomic.AtomicReference;
public class EmptyRenderContainer implements RenderDataSource {
public static final EmptyRenderContainer INSTANCE = new EmptyRenderContainer();
// NOTE: No register() needed since this should never be loaded from a actual data.
@Override
public void enableRender(LodQuadTree quadTree) {
}
@Override
public void disableRender() {
}
@Override
public boolean isRenderReady() {
return false;
}
@Override
public void dispose() {
}
@Override
public byte getDetailOffset() {
return 0;
}
@Override
public boolean trySwapRenderBuffer(LodQuadTree quadTree, AtomicReference<RenderBuffer> referenceSlot) {
return false; // no swap
}
}
@@ -1,9 +1,9 @@
package com.seibel.lod.core.a7;
package com.seibel.lod.core.a7.render;
import com.seibel.lod.core.a7.datatype.column.ColumnDatatype;
import com.seibel.lod.core.a7.datatype.RenderSourceLoader;
import com.seibel.lod.core.a7.pos.DhBlockPos2D;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import com.seibel.lod.core.a7.render.RenderDataSourceLoader;
import com.seibel.lod.core.a7.save.io.render.IRenderSourceProvider;
import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.lod.core.util.DetailDistanceUtil;
import com.seibel.lod.core.util.LodUtil;
@@ -34,11 +34,11 @@ public class LodQuadTree {
public final byte startingSectionLevel;
private final MovableGridRingList<LodSection>[] ringLists;
static final ArrayList<RenderDataSourceLoader> layerLoaderConfig = new ArrayList<>();
static final ArrayList<RenderSourceLoader> layerLoaderConfig = new ArrayList<>();
static final Logger LOGGER = DhLoggerBuilder.getLogger("LodQuadTree");
public static void registerLayerLoader(RenderDataSourceLoader loader, byte sectionLevel) {
public static void registerLayerLoader(RenderSourceLoader loader, byte sectionLevel) {
LOGGER.info("Registering loader for section level " + sectionLevel + " for " + loader.getClass().getSimpleName());
while (layerLoaderConfig.size() <= sectionLevel) {
layerLoaderConfig.add(null);
@@ -65,8 +65,8 @@ public class LodQuadTree {
static class SectionDetailLayer {
final byte targetDataDetail;
final RenderDataSourceLoader containerType;
public SectionDetailLayer(byte targetDataDetail, RenderDataSourceLoader containerType) {
final RenderSourceLoader containerType;
public SectionDetailLayer(byte targetDataDetail, RenderSourceLoader containerType) {
this.targetDataDetail = targetDataDetail;
this.containerType = containerType;
}
@@ -77,7 +77,7 @@ public class LodQuadTree {
for (int i = 0; i < layerLoaderConfig.size(); i++) {
if (layerLoaderConfig.get(i) == null) continue;
isInFront = false;
RenderDataSourceLoader entry = layerLoaderConfig.get(i);
RenderSourceLoader entry = layerLoaderConfig.get(i);
if (i - entry.detailOffset < 0) {
throw new RuntimeException("ContainerTypeConfigEntry " + i + " has a levelOffset of "
+ entry.detailOffset + " which makes the dataDetail be " + (i - entry.detailOffset) + "," +
@@ -95,7 +95,7 @@ public class LodQuadTree {
final SectionDetailLayer[] sectionDetailLayers;
public final int viewDistance;
private final RenderDataProvider renderDataProvider;
private final IRenderSourceProvider renderSourceProvider;
/**
* Constructor of the quadTree
@@ -103,9 +103,8 @@ public class LodQuadTree {
* @param initialPlayerX player x coordinate
* @param initialPlayerZ player z coordinate
*/
public LodQuadTree(int viewDistance, int initialPlayerX, int initialPlayerZ, RenderDataProvider provider) {
ColumnDatatype.REGISTER(); //FIXME: This is a hack to make sure the datatype is registered
renderDataProvider = provider;
public LodQuadTree(int viewDistance, int initialPlayerX, int initialPlayerZ, IRenderSourceProvider provider) {
renderSourceProvider = provider;
assertContainerTypeConfigCorrect();
this.viewDistance = viewDistance;
@@ -113,7 +112,7 @@ public class LodQuadTree {
//FIXME: Rework this mess of code!
{ // Calculate the max section detail
byte maxDetailLevel = getMaxDetailInRange(viewDistance * Math.sqrt(2));
RenderDataSourceLoader finalEntry = null;
RenderSourceLoader finalEntry = null;
byte topSectionLevel = 0;
byte firstLevel = -1;
for (; topSectionLevel < layerLoaderConfig.size(); topSectionLevel++) {
@@ -137,7 +136,7 @@ public class LodQuadTree {
byte lastNonNullEntry = -1;
for (byte i = startingSectionLevel; i < numbersOfSectionLevels; i++) {
byte targetDataDetail;
RenderDataSourceLoader containerType;
RenderSourceLoader containerType;
if (i < layerLoaderConfig.size()) {
if (layerLoaderConfig.get(i) == null) {
@@ -146,14 +145,14 @@ public class LodQuadTree {
containerType = null;
} else {
lastNonNullEntry = i;
RenderDataSourceLoader entry = layerLoaderConfig.get(i);
RenderSourceLoader entry = layerLoaderConfig.get(i);
targetDataDetail = (byte) (i - entry.detailOffset);
containerType = entry;
}
} else {
LodUtil.assertTrue(layerLoaderConfig.get(layerLoaderConfig.size() - 1) != null,
"The last entry must not be null!");
RenderDataSourceLoader entry = layerLoaderConfig.get(layerLoaderConfig.size() - 1);
RenderSourceLoader entry = layerLoaderConfig.get(layerLoaderConfig.size() - 1);
targetDataDetail = (byte) (i - entry.detailOffset);
containerType = entry;
}
@@ -326,7 +325,7 @@ public class LodQuadTree {
final MovableGridRingList<LodSection> parentRingList =
sectLevel == numbersOfSectionLevels - 1 ? null : ringLists[sectLevel - startingSectionLevel + 1];
final byte f_sectLevel = sectLevel;
RenderDataSourceLoader containerType = sectionDetailLayers[sectLevel - startingSectionLevel].containerType;
RenderSourceLoader containerType = sectionDetailLayers[sectLevel - startingSectionLevel].containerType;
ringList.forEachPosOrdered((section, pos) -> {
if (f_sectLevel == 0 && section != null) {
section.childCount = 0;
@@ -418,7 +417,7 @@ public class LodQuadTree {
final MovableGridRingList<LodSection> childRingList =
sectLevel == startingSectionLevel ? null : ringLists[sectLevel - startingSectionLevel - 1];
final boolean doCacsade = sectionDetailLayers[sectLevel - startingSectionLevel].containerType == null;
RenderDataSourceLoader containerType = sectionDetailLayers[sectLevel - startingSectionLevel].containerType;
RenderSourceLoader containerType = sectionDetailLayers[sectLevel - startingSectionLevel].containerType;
ringList.forEachPosOrdered((section, pos) -> {
if (section == null) return;
@@ -464,7 +463,7 @@ public class LodQuadTree {
section.dispose();
} else {
if (!section.isLoaded() && !section.isLoading()) {
section.load(renderDataProvider, containerType);
section.load(renderSourceProvider, containerType);
}
if (section.childCount == 4) section.enableRender(this);
if (section.childCount == 0) section.disableRender();
@@ -1,8 +1,9 @@
package com.seibel.lod.core.a7;
package com.seibel.lod.core.a7.render;
import com.seibel.lod.core.a7.datatype.RenderSourceLoader;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import com.seibel.lod.core.a7.render.RenderDataSource;
import com.seibel.lod.core.a7.render.RenderDataSourceLoader;
import com.seibel.lod.core.a7.datatype.LodRenderSource;
import com.seibel.lod.core.a7.save.io.render.IRenderSourceProvider;
import java.util.concurrent.CompletableFuture;
@@ -17,8 +18,8 @@ public class LodSection {
public byte childCount = 0;
// TODO: Should I provide a way to change the render source?
private RenderDataSource renderDataSource;
private CompletableFuture<RenderDataSource> loadFuture;
private LodRenderSource lodRenderSource;
private CompletableFuture<LodRenderSource> loadFuture;
private boolean isRenderEnabled = false;
// Create sub region
@@ -28,56 +29,56 @@ public class LodSection {
public void enableRender(LodQuadTree quadTree) {
if (isRenderEnabled) return;
if (renderDataSource != null) {
renderDataSource.enableRender(quadTree);
if (lodRenderSource != null) {
lodRenderSource.enableRender(quadTree);
}
isRenderEnabled = true;
}
public void disableRender() {
if (!isRenderEnabled) return;
if (renderDataSource != null) {
renderDataSource.disableRender();
if (lodRenderSource != null) {
lodRenderSource.disableRender();
}
isRenderEnabled = false;
}
public void load(RenderDataProvider renderDataProvider, RenderDataSourceLoader renderDataSourceClass) {
if (loadFuture != null || renderDataSource != null) throw new IllegalStateException("Reloading is not supported!");
public void load(IRenderSourceProvider renderDataProvider, RenderSourceLoader renderDataSourceClass) {
if (loadFuture != null || lodRenderSource != null) throw new IllegalStateException("Reloading is not supported!");
loadFuture = renderDataProvider.createRenderData(renderDataSourceClass, pos);
}
public void tick(LodQuadTree quadTree) {
if (loadFuture != null && loadFuture.isDone()) {
renderDataSource = loadFuture.join();
lodRenderSource = loadFuture.join();
loadFuture = null;
if (isRenderEnabled) {
renderDataSource.enableRender(quadTree);
lodRenderSource.enableRender(quadTree);
}
}
}
public void dispose() {
if (renderDataSource != null) {
renderDataSource.dispose();
if (lodRenderSource != null) {
lodRenderSource.dispose();
} else if (loadFuture != null) {
loadFuture.cancel(true);
}
}
public boolean canRender() {
return isLoaded() && renderDataSource.isRenderReady();
return isLoaded() && lodRenderSource.isRenderReady();
}
public boolean isLoaded() {
return renderDataSource != null;
return lodRenderSource != null;
}
public boolean isLoading() {
return loadFuture != null;
}
public RenderDataSource getRenderContainer() {
return renderDataSource;
public LodRenderSource getRenderContainer() {
return lodRenderSource;
}
}
@@ -1,8 +1,7 @@
package com.seibel.lod.core.a7.render;
import com.seibel.lod.core.a7.datatype.LodRenderSource;
import com.seibel.lod.core.objects.Pos2D;
import com.seibel.lod.core.a7.LodQuadTree;
import com.seibel.lod.core.a7.LodSection;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import com.seibel.lod.core.render.LodRenderProgram;
import com.seibel.lod.core.util.LodUtil;
@@ -45,7 +44,7 @@ public class RenderBufferHandler {
// If this fails, there may be concurrent modification of the quad tree
// (as this update() should be called from the same thread that calls update() on the quad tree)
LodUtil.assertTrue(section != null);
RenderDataSource container = section.getRenderContainer();
LodRenderSource container = section.getRenderContainer();
// Update self's render buffer state
boolean shouldRender = section.isLoaded();
@@ -1,60 +0,0 @@
package com.seibel.lod.core.a7.render;
import com.seibel.lod.core.a7.LodQuadTree;
import java.util.concurrent.atomic.AtomicReference;
/**
* Example on how to register a loader:
* <pre>
public static RenderDataSource testAndConstruct(LodDataSource dataSource, DhSectionPos sectionPos) {
ColumnRenderContainer container = new ColumnRenderContainer(10, -100);
container.startFillData(dataSource);
return container;
}
static {
RenderDataSource.registorLoader(ColumnRenderContainer::testAndConstruct, 0);
}
</pre>
*/
public interface RenderDataSource {
// Don't think this is needed with the newer quad tree structure...
// interface RenderContainerConstructor {
// // Can return null as meaning the requirement is not met
// RenderDataSource testAndConstruct(LodDataSource dataSource, DhSectionPos sectionPos);
// }
// SortedMap<Integer, RenderContainerConstructor>
// renderContainerLoaderRegistry = new TreeMap<Integer, RenderContainerConstructor>();
// static void registorLoader(RenderContainerConstructor func, int priority) {
// if (func == null) {
// throw new IllegalArgumentException("loader must be non-null");
// }
// renderContainerLoaderRegistry.put(priority, func);
// }
//
// static RenderDataSource tryConstruct(LodDataSource dataSource, DhSectionPos pos) {
// for (RenderContainerConstructor func : renderContainerLoaderRegistry.values()) {
// RenderDataSource container = func.testAndConstruct(dataSource, pos);
// if (container != null) {
// return container;
// }
// }
// return null;
// }
void enableRender(LodQuadTree quadTree);
void disableRender();
boolean isRenderReady();
void dispose(); // notify the container that the parent lodSection is now disposed (can be in loaded or unloaded state)
byte getDetailOffset();
/**
* Try and swap in new render buffer for this section. Note that before this call, there should be no other
* places storing or referencing the render buffer.
* @param referenceSlot The slot for swapping in the new buffer.
* @return True if the swap was successful. False if swap is not needed or if it is in progress.
*/
boolean trySwapRenderBuffer(LodQuadTree quadTree, AtomicReference<RenderBuffer> referenceSlot);
}
@@ -1,23 +0,0 @@
package com.seibel.lod.core.a7.render;
import com.seibel.lod.core.a7.data.LodDataSource;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import com.seibel.lod.core.a7.level.DhClientServerLevel;
import com.seibel.lod.core.a7.data.DataFile;
import java.util.*;
import java.util.stream.Collectors;
public abstract class RenderDataSourceLoader {
public final int detailOffset;
public RenderDataSourceLoader(int detailOffset) {
this.detailOffset = detailOffset;
}
public abstract RenderDataSource construct(List<LodDataSource> dataSources, DhSectionPos sectionPos, DhClientServerLevel level);
public List<DataFile> selectFiles(DhSectionPos sectionPos, DhClientServerLevel level, List<DataFile>[] availableFiles) {
return Arrays.stream(availableFiles).flatMap(Collection::stream).collect(Collectors.toList());
}
}
@@ -1,105 +0,0 @@
package com.seibel.lod.core.a7.save.io;
import com.seibel.lod.core.config.Config;
import com.seibel.lod.core.enums.config.EServerFolderNameMode;
import com.seibel.lod.core.handlers.LodDimensionFinder;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.logging.ConfigBasedLogger;
import com.seibel.lod.core.objects.ParsedIp;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import org.apache.logging.log4j.LogManager;
import java.io.File;
import java.io.IOException;
public class DHFolderHandler {
/**
* This regex finds any characters that are invalid for use in a windows
* (and by extension mac and linux) file path
*/
public static final String INVALID_FILE_CHARACTERS_REGEX = "[\\\\/:*?\"<>|]";
private static final IMinecraftClientWrapper MC = SingletonHandler.get(IMinecraftClientWrapper.class);
public static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(LodDimensionFinder.class),
() -> Config.Client.Advanced.Debugging.DebugSwitch.logFileSubDimEvent.get());
public static File getCurrentWorldFolder() {
File dimensionFolder;
try
{
if (MC.hasSinglePlayerServer())
{
// local world
dimensionFolder = new File(MC.getSinglePlayerServerFolder(), "lod");
}
else
{
// multiplayer world
dimensionFolder = new File(MC.getGameDirectory().getCanonicalFile().getPath() +
File.separatorChar + "Distant_Horizons_server_data" + File.separatorChar + getServerFolderName());
}
}
catch (IOException e)
{
LOGGER.error("Unable to get world folder directory: ", e);
throw new RuntimeException("Critical error: Unable to get world folder directory", e);
}
return dimensionFolder;
}
private static String getServerFolderName()
{
// parse the current server's IP
ParsedIp parsedIp = new ParsedIp(MC.getCurrentServerIp());
String serverIpCleaned = parsedIp.ip.replaceAll(INVALID_FILE_CHARACTERS_REGEX, "");
String serverPortCleaned = parsedIp.port != null ? parsedIp.port.replaceAll(INVALID_FILE_CHARACTERS_REGEX, "") : "";
// determine the format of the folder name
EServerFolderNameMode folderNameMode = Config.Client.Multiplayer.serverFolderNameMode.get();
if (folderNameMode == EServerFolderNameMode.AUTO)
{
if (parsedIp.isLan())
{
// LAN
folderNameMode = EServerFolderNameMode.NAME_IP;
}
else
{
// normal multiplayer
folderNameMode = EServerFolderNameMode.NAME_IP_PORT;
}
}
String serverName = MC.getCurrentServerName().replaceAll(INVALID_FILE_CHARACTERS_REGEX, "");
String serverMcVersion = MC.getCurrentServerVersion().replaceAll(INVALID_FILE_CHARACTERS_REGEX, "");
// generate the folder name
String folderName = "";
switch (folderNameMode)
{
// default and auto shouldn't be used
// and are just here to make the compiler happy
default:
case AUTO:
case NAME_ONLY:
folderName = serverName;
break;
case NAME_IP:
folderName = serverName + ", IP " + serverIpCleaned;
break;
case NAME_IP_PORT:
folderName = serverName + ", IP " + serverIpCleaned + (serverPortCleaned.length() != 0 ? ("-" + serverPortCleaned) : "");
break;
case NAME_IP_PORT_MC_VERSION:
folderName = serverName + ", IP " + serverIpCleaned + (serverPortCleaned.length() != 0 ? ("-" + serverPortCleaned) : "") + ", GameVersion " + serverMcVersion;
break;
}
return folderName;
}
}
@@ -12,7 +12,7 @@ import java.util.function.BiConsumer;
import java.util.zip.Adler32;
import java.util.zip.CheckedOutputStream;
import com.seibel.lod.core.a7.data.DataSourceLoader;
import com.seibel.lod.core.a7.datatype.DataSourceLoader;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import com.seibel.lod.core.util.LodUtil;
@@ -49,7 +49,7 @@ public class MetaFile {
//Loader stuff
public DataSourceLoader loader;
public Class<?> dataType;
public byte loaderVersion;
public byte dataVersion;
// Load a metaFile in this path. It also automatically read the metadata.
protected MetaFile(File path) throws IOException {
@@ -80,7 +80,7 @@ public class MetaFile {
throw new IOException("Invalid file: Data type loader not found: " + dataTypeId + "(v" + loaderVersion + ")");
}
this.dataType = loader.clazz;
this.loaderVersion = loaderVersion;
this.dataVersion = loaderVersion;
}
}
@@ -128,7 +128,7 @@ public class MetaFile {
throw new IOException("Invalid file: Data type loader not found: " + dataTypeId + "(v" + loaderVersion + ")");
}
this.dataType = loader.clazz;
this.loaderVersion = loaderVersion;
this.dataVersion = loaderVersion;
}
}
@@ -159,7 +159,7 @@ public class MetaFile {
buff.putInt(checksum);
buff.put(pos.sectionDetail);
buff.put(dataLevel);
buff.put(loaderVersion);
buff.put(dataVersion);
buff.put(Byte.MIN_VALUE); // Unused
buff.putLong(loader.datatypeId);
buff.putLong(timestamp);
@@ -9,17 +9,14 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.BiConsumer;
import com.seibel.lod.core.a7.data.DataSourceLoader;
import com.seibel.lod.core.a7.data.LodDataSource;
import com.seibel.lod.core.a7.datatype.full.FullDatatype;
import com.seibel.lod.core.a7.datatype.LodDataSource;
import com.seibel.lod.core.a7.datatype.DataSourceLoader;
import com.seibel.lod.core.a7.datatype.full.Data;
import com.seibel.lod.core.a7.save.io.MetaFile;
import com.seibel.lod.core.a7.level.ILevel;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.lod.core.a7.datatype.column.DataSourceSaver;
import com.seibel.lod.core.a7.datatype.column.OldDataSourceLoader;
import com.seibel.lod.core.util.LodUtil;
import org.apache.logging.log4j.Logger;
@@ -39,13 +36,13 @@ public class DataMetaFile extends MetaFile {
//TODO: use ConcurrentAppendSingleSwapContainer<LodDataSource> instead of below:
private static class GuardedMultiAppendQueue {
ReentrantReadWriteLock appendLock = new ReentrantReadWriteLock();
ConcurrentLinkedQueue<FullDatatype> queue = new ConcurrentLinkedQueue<>();
ConcurrentLinkedQueue<Data> queue = new ConcurrentLinkedQueue<>();
}
AtomicReference<GuardedMultiAppendQueue> writeQueue =
new AtomicReference<>(new GuardedMultiAppendQueue());
GuardedMultiAppendQueue _backQueue = new GuardedMultiAppendQueue();
public void addToWriteQueue(FullDatatype datatype) {
public void addToWriteQueue(Data datatype) {
GuardedMultiAppendQueue queue = writeQueue.get();
// Using read lock is OK, because the queue's underlying data structure is thread-safe.
// This lock is only used to insure on polling the queue, that the queue is not being
@@ -205,25 +202,18 @@ public class DataMetaFile extends MetaFile {
}
private void write(LodDataSource data) {
DataSourceSaver saver;
if (loader instanceof DataSourceSaver) saver = (DataSourceSaver) loader;
else if (loader instanceof OldDataSourceLoader) saver = ((OldDataSourceLoader) loader).getNewSaver();
else saver = null;
if (saver == null) return;
BiConsumer<MetaFile, OutputStream> dataWriter = (meta, out) -> {
meta.dataLevel = data.getDataDetail();
meta.dataType = DataSourceLoader.datatypeIdRegistry.get(saver.datatypeId);
meta.loader = saver;
meta.loaderVersion = saver.getSaverVersion();
try {
saver.saveData(level, data, this, out);
} catch (IOException e) {
LOGGER.error("Failed to save data for file {}", path, e);
}
};
try {
super.writeData(dataWriter);
super.writeData((meta, out) -> {
meta.dataLevel = data.getDataDetail();
meta.dataType = data.getClass();
meta.loader = DataSourceLoader.getLoader(data.getClass(), data.getDataVersion());
meta.dataVersion = data.getDataVersion();
try {
data.saveData(level, this, out);
} catch (IOException e) {
LOGGER.error("Failed to save data for file {}", path, e);
}
});
} catch (IOException e) {
LOGGER.error("Failed to write data for file {}", path, e);
}
@@ -1,17 +1,17 @@
package com.seibel.lod.core.a7.save.io.file;
import com.seibel.lod.core.a7.data.LodDataSource;
import com.seibel.lod.core.a7.datatype.full.FullDatatype;
import com.seibel.lod.core.a7.datatype.LodDataSource;
import com.seibel.lod.core.a7.datatype.full.Data;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import java.io.File;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
public interface DataSourceProvider extends AutoCloseable {
public interface IDataSourceProvider extends AutoCloseable {
void addScannedFile(Collection<File> detectedFiles);
CompletableFuture<LodDataSource> read(DhSectionPos pos);
void write(DhSectionPos sectionPos, FullDatatype chunkData);
void write(DhSectionPos sectionPos, Data chunkData);
CompletableFuture<Void> flushAndSave();
}
@@ -1,8 +1,8 @@
package com.seibel.lod.core.a7.save.io.file;
import com.google.common.collect.HashMultimap;
import com.seibel.lod.core.a7.data.LodDataSource;
import com.seibel.lod.core.a7.datatype.full.FullDatatype;
import com.seibel.lod.core.a7.datatype.LodDataSource;
import com.seibel.lod.core.a7.datatype.full.Data;
import com.seibel.lod.core.a7.level.IServerLevel;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import com.seibel.lod.core.logging.DhLoggerBuilder;
@@ -19,7 +19,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
public class LocalDataFileHandler implements DataSourceProvider {
public class LocalDataFileHandler implements IDataSourceProvider {
// Note: Single main thread only for now. May make it multi-thread later, depending on the usage.
ExecutorService fileReaderThread = LodUtil.makeSingleThreadPool("FileReaderThread");
Logger logger = DhLoggerBuilder.getLogger("LocalDataFileHandler");
@@ -110,7 +110,7 @@ public class LocalDataFileHandler implements DataSourceProvider {
* This call is concurrent. I.e. it supports multiple threads calling this method at the same time.
*/
@Override
public void write(DhSectionPos sectionPos, FullDatatype chunkData) {
public void write(DhSectionPos sectionPos, Data chunkData) {
DataMetaFile metaFile = files.get(sectionPos);
if (metaFile != null) { // Fast path: if there is a file for this section, just write to it.
metaFile.addToWriteQueue(chunkData);
@@ -1,14 +1,14 @@
package com.seibel.lod.core.a7.save.io.file;
import com.seibel.lod.core.a7.data.LodDataSource;
import com.seibel.lod.core.a7.datatype.full.FullDatatype;
import com.seibel.lod.core.a7.datatype.LodDataSource;
import com.seibel.lod.core.a7.datatype.full.Data;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import java.io.File;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
public class RemoteDataFileHandler implements DataSourceProvider {
public class RemoteDataFileHandler implements IDataSourceProvider {
@Override
public void addScannedFile(Collection<File> detectedFiles) {
@@ -20,7 +20,7 @@ public class RemoteDataFileHandler implements DataSourceProvider {
}
@Override
public void write(DhSectionPos sectionPos, FullDatatype chunkData) {
public void write(DhSectionPos sectionPos, Data chunkData) {
}
@@ -1,15 +1,14 @@
package com.seibel.lod.core.a7.save.io.render;
import com.seibel.lod.core.a7.RenderDataProvider;
import com.seibel.lod.core.a7.datatype.full.FullDatatype;
import com.seibel.lod.core.a7.datatype.full.Data;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import java.io.File;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
public interface RenderSourceProvider extends RenderDataProvider, AutoCloseable {
public interface IRenderSourceProvider extends AutoCloseable {
void addScannedFile(Collection<File> detectedFiles);
void write(DhSectionPos sectionPos, FullDatatype chunkData);
void write(DhSectionPos sectionPos, Data chunkData);
CompletableFuture<Void> flushAndSave();
}
@@ -1,11 +1,11 @@
package com.seibel.lod.core.a7.save.io.render;
import com.seibel.lod.core.a7.datatype.full.FullDatatype;
import com.seibel.lod.core.a7.save.io.file.DataSourceProvider;
import com.seibel.lod.core.a7.datatype.RenderSourceLoader;
import com.seibel.lod.core.a7.datatype.full.Data;
import com.seibel.lod.core.a7.save.io.file.IDataSourceProvider;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.lod.core.a7.render.RenderDataSource;
import com.seibel.lod.core.a7.render.RenderDataSourceLoader;
import com.seibel.lod.core.a7.datatype.LodRenderSource;
import com.seibel.lod.core.util.LodUtil;
import org.apache.logging.log4j.Logger;
@@ -14,19 +14,19 @@ import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
public class RenderFileHandler implements RenderSourceProvider {
public class RenderFileHandler implements IRenderSourceProvider {
final File renderCacheFolder;
final DataSourceProvider dataSourceProvider;
final IDataSourceProvider dataSourceProvider;
ExecutorService renderCacheThread = LodUtil.makeSingleThreadPool("RenderCacheThread");
Logger logger = DhLoggerBuilder.getLogger("RenderCache");
public RenderFileHandler(DataSourceProvider sourceProvider, File renderCacheFolder) {
public RenderFileHandler(IDataSourceProvider sourceProvider, File renderCacheFolder) {
this.dataSourceProvider = sourceProvider;
this.renderCacheFolder = renderCacheFolder;
}
@Override
public CompletableFuture<RenderDataSource> createRenderData(RenderDataSourceLoader renderSourceLoader, DhSectionPos pos) {
public CompletableFuture<LodRenderSource> createRenderData(RenderSourceLoader renderSourceLoader, DhSectionPos pos) {
return null;
}
@@ -36,7 +36,7 @@ public class RenderFileHandler implements RenderSourceProvider {
}
@Override
public void write(DhSectionPos sectionPos, FullDatatype chunkData) {
public void write(DhSectionPos sectionPos, Data chunkData) {
}
@@ -1,7 +1,7 @@
package com.seibel.lod.core.a7.save.io;
package com.seibel.lod.core.a7.util;
import com.seibel.lod.core.a7.save.io.file.DataSourceProvider;
import com.seibel.lod.core.a7.save.io.render.RenderSourceProvider;
import com.seibel.lod.core.a7.save.io.file.IDataSourceProvider;
import com.seibel.lod.core.a7.save.io.render.IRenderSourceProvider;
import com.seibel.lod.core.a7.save.structure.SaveStructure;
import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.lod.core.wrapperInterfaces.world.ILevelWrapper;
@@ -19,8 +19,8 @@ public class FileScanner {
public static final int MAX_SCAN_DEPTH = 5;
public static final String LOD_FILE_POSTFIX = ".lod";
public static void scanFile(SaveStructure save, ILevelWrapper level,
@Nullable DataSourceProvider dataSource,
@Nullable RenderSourceProvider renderSource) {
@Nullable IDataSourceProvider dataSource,
@Nullable IRenderSourceProvider renderSource) {
if (dataSource != null) {
try (Stream<Path> pathStream = Files.walk(save.getDataFolder(level).toPath(), MAX_SCAN_DEPTH)) {
dataSource.addScannedFile(pathStream.filter((
@@ -0,0 +1,6 @@
package com.seibel.lod.core.a7.util;
public class IOUtil {
public static final String LOD_FILE_EXTENSION = ".lod";
}
@@ -1,4 +1,4 @@
package com.seibel.lod.core.a7;
package com.seibel.lod.core.a7.util;
public class IdMappingUtil {
public static final String BLOCKSTATE_ID_AIR = "air";
@@ -1,4 +1,4 @@
package com.seibel.lod.core.a7;
package com.seibel.lod.core.a7.util;
public class UncheckedInterruptedException extends RuntimeException {
public UncheckedInterruptedException(String message) {
@@ -1,9 +1,9 @@
package com.seibel.lod.core.a7.world;
import com.seibel.lod.core.a7.WorldEnvironment;
import com.seibel.lod.core.a7.level.DhClientServerLevel;
import com.seibel.lod.core.a7.save.structure.LocalSaveStructure;
import com.seibel.lod.core.config.Config;
import com.seibel.lod.core.util.EventLoop;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.wrapperInterfaces.world.ILevelWrapper;
@@ -11,10 +11,13 @@ import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
public class DhClientServerWorld extends DhWorld implements IClientWorld, IServerWorld {
private final HashMap<ILevelWrapper, DhClientServerLevel> levels;
public final LocalSaveStructure saveStructure;
public ExecutorService dhTickerThread = LodUtil.makeSingleThreadPool("DHTickerThread", 2);
public EventLoop eventLoop = new EventLoop(dhTickerThread, this::_clientTick);
public DhClientServerWorld() {
super(WorldEnvironment.Client_Server);
@@ -45,18 +48,25 @@ public class DhClientServerWorld extends DhWorld implements IClientWorld, IServe
}
}
public void tick() {
private void _clientTick() {
int newViewDistance = Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * 16;
Iterator<DhClientServerLevel> iterator = levels.values().iterator();
while (iterator.hasNext()) {
DhClientServerLevel level = iterator.next();
if (level.tree.viewDistance != newViewDistance) {
level.close();
level.close(); //FIXME: Is this fine for current logic?
iterator.remove();
}
}
//DetailDistanceUtil.updateSettings();
levels.values().forEach(DhClientServerLevel::tick);
levels.values().forEach(DhClientServerLevel::clientTick);
}
public void clientTick() {
eventLoop.tick();
}
public void serverTick() {
levels.values().forEach(DhClientServerLevel::serverTick);
}
public void doWorldGen() {
@@ -77,4 +87,15 @@ public class DhClientServerWorld extends DhWorld implements IClientWorld, IServe
}
levels.clear();
}
public void enableRendering(ILevelWrapper wrapper) {
DhClientServerLevel level = levels.get(wrapper);
if (level==null) return;
level.startRenderer();
}
public void disableRendering(ILevelWrapper wrapper) {
DhClientServerLevel level = levels.get(wrapper);
if (level==null) return;
level.stopRenderer();
}
}
@@ -1,7 +1,7 @@
package com.seibel.lod.core.a7.world;
import com.seibel.lod.core.a7.WorldEnvironment;
import com.seibel.lod.core.a7.level.DhClientLevel;
import com.seibel.lod.core.a7.level.DhClientServerLevel;
import com.seibel.lod.core.a7.save.structure.ClientOnlySaveStructure;
import com.seibel.lod.core.config.Config;
import com.seibel.lod.core.util.DetailDistanceUtil;
@@ -16,12 +16,10 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
public class DhClientWorld extends DhWorld implements IClientWorld {
private final HashMap<ILevelWrapper, DhClientLevel> levels;
public final ClientOnlySaveStructure saveStructure;
public ExecutorService dhTickerThread = LodUtil.makeSingleThreadPool("DHTickerThread", 2);
public EventLoop eventLoop = new EventLoop(dhTickerThread, this::tick);
public EventLoop eventLoop = new EventLoop(dhTickerThread, this::_clientTick);
public DhClientWorld() {
super(WorldEnvironment.Client_Only);
@@ -52,7 +50,7 @@ public class DhClientWorld extends DhWorld implements IClientWorld {
}
}
private void tick() {
private void _clientTick() {
int newViewDistance = Config.Client.Graphics.Quality.lodChunkRenderDistance.get() * 16;
Iterator<DhClientLevel> iterator = levels.values().iterator();
while (iterator.hasNext()) {
@@ -63,9 +61,10 @@ public class DhClientWorld extends DhWorld implements IClientWorld {
}
}
DetailDistanceUtil.updateSettings();
levels.values().forEach(DhClientLevel::clientTick);
}
public void asyncTick() {
public void clientTick() {
eventLoop.tick();
}
@@ -1,6 +1,5 @@
package com.seibel.lod.core.a7.world;
import com.seibel.lod.core.a7.WorldEnvironment;
import com.seibel.lod.core.a7.level.DhServerLevel;
import com.seibel.lod.core.a7.save.structure.LocalSaveStructure;
import com.seibel.lod.core.util.LodUtil;
@@ -43,8 +42,8 @@ public class DhServerWorld extends DhWorld implements IServerWorld {
}
}
public void tick() {
levels.values().forEach(DhServerLevel::tick);
public void serverTick() {
levels.values().forEach(DhServerLevel::serverTick);
}
public void doWorldGen() {
@@ -1,7 +1,5 @@
package com.seibel.lod.core.a7.world;
import com.seibel.lod.core.a7.WorldEnvironment;
import com.seibel.lod.core.a7.level.DhClientServerLevel;
import com.seibel.lod.core.a7.level.ILevel;
import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.lod.core.wrapperInterfaces.world.ILevelWrapper;
@@ -24,4 +22,7 @@ public abstract class DhWorld implements Closeable {
public abstract void unloadLevel(ILevelWrapper wrapper);
public abstract CompletableFuture<Void> saveAndFlush();
@Override
public abstract void close();
}
@@ -1,4 +1,7 @@
package com.seibel.lod.core.a7.world;
import com.seibel.lod.core.wrapperInterfaces.world.ILevelWrapper;
public interface IClientWorld {
void clientTick();
}
@@ -1,5 +1,6 @@
package com.seibel.lod.core.a7.world;
public interface IServerWorld {
void serverTick();
void doWorldGen();
}
@@ -1,4 +1,4 @@
package com.seibel.lod.core.a7;
package com.seibel.lod.core.a7.world;
public enum WorldEnvironment {
Client_Only,
@@ -19,6 +19,8 @@
package com.seibel.lod.core.api.internal.a7;
import com.seibel.lod.core.a7.level.IClientLevel;
import com.seibel.lod.core.a7.world.*;
import com.seibel.lod.core.config.Config;
import com.seibel.lod.core.ModInfo;
import com.seibel.lod.core.enums.rendering.EDebugMode;
@@ -28,7 +30,6 @@ import com.seibel.lod.core.logging.ConfigBasedLogger;
import com.seibel.lod.core.logging.ConfigBasedSpamLogger;
import com.seibel.lod.core.logging.SpamReducedLogger;
import com.seibel.lod.core.a7.level.DhClientServerLevel;
import com.seibel.lod.core.a7.world.DhWorld;
import com.seibel.lod.core.a7.Server;
import com.seibel.lod.core.objects.math.Mat4f;
import com.seibel.lod.core.render.GLProxy;
@@ -38,6 +39,7 @@ import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.ILevelWrapper;
import net.fabricmc.api.Environment;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -80,12 +82,6 @@ public class ClientApi
}
}
}
/**
* there is some setup that should only happen once,
* once this is true that setup has completed
*/
private boolean firstTimeSetupComplete = false;
private boolean configOverrideReminderPrinted = false;
public boolean rendererDisabledBecauseOfExceptions = false;
private ClientApi()
@@ -114,42 +110,46 @@ public class ClientApi
if (MC != null) MC.sendChatMessage(prefix + str);
}
public void clientServerConnected() {
SharedApi.currentServer = new Server(false);
SharedApi.currentWorld = new DhWorld(enviroment);
public void onClientOnlyConnected() {
SharedApi.currentWorld = new DhClientWorld();
}
public void clientServerDisconnected() {
public void onClientOnlyDisconnected() {
SharedApi.currentWorld.close();
SharedApi.currentWorld = null;
SharedApi.currentServer = null;
}
public void clientChunkLoadEvent(IChunkWrapper chunk, ILevelWrapper world)
public void clientChunkLoadEvent(IChunkWrapper chunk, ILevelWrapper level)
{
//TODO: Implement
}
public void clientChunkSaveEvent(IChunkWrapper chunk, ILevelWrapper world)
{
//TODO: Implement
}
public void clientLevelUnloadEvent(ILevelWrapper world)
{
if (SharedApi.currentWorld != null) {
SharedApi.currentWorld.unloadLevel(world);
if (SharedApi.getEnvironment() == WorldEnvironment.Client_Only) {
//TODO: Implement
}
}
public void clientLevelLoadEvent(ILevelWrapper world)
public void clientChunkSaveEvent(IChunkWrapper chunk, ILevelWrapper level)
{
//TODO: Maybe make DHLevel init no longer depend on needing player entity in single player
if (SharedApi.currentWorld != null) {
SharedApi.currentWorld.getOrLoadLevel(world);
if (SharedApi.getEnvironment() == WorldEnvironment.Client_Only) {
//TODO: Implement
}
}
public void clientLevelUnloadEvent(ILevelWrapper level)
{
if (SharedApi.currentWorld instanceof DhClientServerWorld) {
((DhClientServerWorld)SharedApi.currentWorld).disableRendering(level);
} else if (SharedApi.getEnvironment() == WorldEnvironment.Client_Only) {
SharedApi.currentWorld.unloadLevel(level);
}
}
public void clientLevelLoadEvent(ILevelWrapper level)
{
if (SharedApi.currentWorld instanceof DhClientServerWorld) {
((DhClientServerWorld)SharedApi.currentWorld).enableRendering(level);
} else if (SharedApi.getEnvironment() == WorldEnvironment.Client_Only) {
SharedApi.currentWorld.getOrLoadLevel(level); //TODO: This may need to be delayed to after player enters the level
}
}
private long lastFlush = 0;
public void rendererShutdownEvent() {
IProfilerWrapper profiler = MC.getProfiler();
profiler.push("DH-RendererShutdown");
@@ -176,15 +176,8 @@ public class ClientApi
ConfigBasedLogger.updateAll();
ConfigBasedSpamLogger.updateAll(doFlush);
if (SharedApi.currentWorld != null) {
if (ModInfo.IS_DEV_BUILD) {
// config overrides should only be used in the developer builds
applyDeveloperConfigOverrides();
}
if (SharedApi.currentServer == null) {
// In single player. Do client-side ticking system.
SharedApi.currentWorld.asyncTick();
}
if (SharedApi.currentWorld instanceof IClientWorld) {
((IClientWorld) SharedApi.currentWorld).clientTick();
}
profiler.pop();
}
@@ -199,18 +192,13 @@ public class ClientApi
if (world == null) return;
DhWorld DhWorld = SharedApi.currentWorld;
if (DhWorld == null) return;
DhClientServerLevel level = (SharedApi.currentServer == null) ? DhWorld.getOrLoadLevel(world) : DhWorld.getLevel(world);
if (level == null) return;
if (!(SharedApi.currentWorld instanceof IClientWorld)) return;
IClientLevel level = (IClientLevel)SharedApi.currentWorld;
if (prefLoggerEnabled) {
level.dumpRamUsage();
}
if (SharedApi.currentServer == null) {
// In multiplayer, without access to the server-side stuff. So we need to do some extra work.
level.asyncTick();
}
if (Config.Client.Advanced.Debugging.rendererType.get() == ERendererType.DEFAULT) {
if (MC_RENDER.playerHasBlindnessEffect()) {
// if the player is blind, don't render LODs,
@@ -19,6 +19,9 @@
package com.seibel.lod.core.api.internal.a7;
import com.seibel.lod.core.a7.world.DhClientServerWorld;
import com.seibel.lod.core.a7.world.DhServerWorld;
import com.seibel.lod.core.a7.world.IServerWorld;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.lod.core.a7.world.DhWorld;
@@ -44,8 +47,6 @@ public class ServerApi
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
private static final IVersionConstants VERSION_CONSTANTS = SingletonHandler.get(IVersionConstants.class);
private boolean isCurrentlyOnSinglePlayerServer = false;
private ServerApi()
{
}
@@ -57,51 +58,51 @@ public class ServerApi
private int lastWorldGenTickDelta = 0;
public void serverTickEvent()
{
lastWorldGenTickDelta--;
if (SharedApi.currentWorld != null && lastWorldGenTickDelta <= 0) {
lastWorldGenTickDelta = 20;
DhWorld DhWorld = SharedApi.currentWorld;
DhWorld.tick();
if (SharedApi.currentWorld instanceof IServerWorld) {
IServerWorld serverWorld = (IServerWorld) SharedApi.currentWorld;
serverWorld.serverTick();
lastWorldGenTickDelta--;
if (lastWorldGenTickDelta <= 0) {
serverWorld.doWorldGen();
lastWorldGenTickDelta = 20;
}
}
}
//TODO: rename to serverLoadEvent
public void serverWorldLoadEvent() {
SharedApi.currentServer = new Server(!SharedApi.inDedicatedEnvironment);
SharedApi.currentWorld = new DhWorld(enviroment);
//TODO: Setup the network handler
public void serverWorldLoadEvent(boolean isDedicatedEnvironment) {
if (isDedicatedEnvironment) {
SharedApi.currentWorld = new DhServerWorld();
} else {
SharedApi.currentWorld = new DhClientServerWorld();
}
}
//TODO: rename to serverUnloadEvent
public void serverWorldUnloadEvent() {
//TODO: Close the network handler
SharedApi.currentWorld.close();
SharedApi.currentWorld = null;
SharedApi.currentServer = null;
}
public void serverLevelLoadEvent(ILevelWrapper world) {
//TODO: Maybe make DHLevel init no longer depend on needing player entity in single player
if (SharedApi.currentServer.isSinglePlayer) return;
SharedApi.currentWorld.getOrLoadLevel(world);
if (SharedApi.currentWorld instanceof IServerWorld)
SharedApi.currentWorld.getOrLoadLevel(world);
}
public void serverLevelUnloadEvent(ILevelWrapper world) {
SharedApi.currentWorld.unloadLevel(world);
if (SharedApi.currentWorld instanceof IServerWorld)
SharedApi.currentWorld.unloadLevel(world);
}
@Deprecated
public void serverSaveEvent() {
SharedApi.currentWorld.save();
}
public void chunkSaveEvent(IChunkWrapper chunk, ILevelWrapper world) {
//TODO
if (SharedApi.currentWorld instanceof IServerWorld)
SharedApi.currentWorld.saveAndFlush();
}
public void serverChunkLoadEvent(IChunkWrapper chunk, ILevelWrapper world) {
//TODO
}
public void serverChunkSaveEvent(IChunkWrapper chunk, ILevelWrapper world) {
//TODO
}
}
@@ -1,15 +1,16 @@
package com.seibel.lod.core.api.internal.a7;
import com.seibel.lod.core.a7.world.WorldEnvironment;
import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.lod.core.a7.world.DhWorld;
import com.seibel.lod.core.a7.Server;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import org.apache.logging.log4j.Logger;
public class SharedApi {
public static DhWorld currentWorld;
public static Server currentServer;
public static IMinecraftSharedWrapper MC;
public static Logger LOGGER = DhLoggerBuilder.getLogger("DH Events");
public static boolean inDedicatedEnvironment;
public static WorldEnvironment getEnvironment() {
return currentWorld==null ? null : currentWorld.environment;
}
}
@@ -22,8 +22,8 @@ package com.seibel.lod.core.builders.lodBuilding.bufferBuilding;
import com.seibel.lod.core.enums.rendering.EDebugMode;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
import com.seibel.lod.core.objects.LodDataView;
import com.seibel.lod.core.a7.datatype.column.ColumnArrayView;
import com.seibel.lod.core.a7.datatype.column.ColumnBox;
import com.seibel.lod.core.a7.datatype.column.accessor.ColumnArrayView;
import com.seibel.lod.core.a7.datatype.column.render.ColumnBox;
import com.seibel.lod.core.objects.opengl.LodBox;
import com.seibel.lod.core.util.ColorUtil;
import com.seibel.lod.core.util.DataPointUtil;