Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] New storage system #36

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.spongepowered.configurate.NodePath;
import org.spongepowered.configurate.transformation.ConfigurationTransformation;
import xyz.jpenilla.squaremap.common.Logging;

@SuppressWarnings("unused")
public final class Config extends AbstractConfig {
Expand Down Expand Up @@ -114,6 +116,21 @@ private static void progressLogging() {
PROGRESS_LOGGING_INTERVAL = config.getInt("settings.render-progress-logging.interval-seconds", 1);
}

public static DataStorageType STORAGE_TYPE;

private static void storage() {
try {
STORAGE_TYPE = DataStorageType.valueOf(
config.getString("settings.storage.type", "flatfile").toUpperCase(Locale.ROOT)
);
} catch (IllegalArgumentException e) {
Logging.logger().error("Invalid storage type '"
+ config.getString("settings.storage.type", "flatfile")
+ "', falling back to flatfile.");
STORAGE_TYPE = DataStorageType.FLATFILE;
}
}

public static Config config() {
return config;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package xyz.jpenilla.squaremap.common.config;

public enum DataStorageType {
FLATFILE
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,8 @@
package xyz.jpenilla.squaremap.common.data;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import java.io.BufferedReader;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
Expand All @@ -32,33 +21,25 @@
import xyz.jpenilla.squaremap.api.Registry;
import xyz.jpenilla.squaremap.api.WorldIdentifier;
import xyz.jpenilla.squaremap.common.LayerRegistry;
import xyz.jpenilla.squaremap.common.Logging;
import xyz.jpenilla.squaremap.common.SquaremapCommon;
import xyz.jpenilla.squaremap.common.config.WorldAdvanced;
import xyz.jpenilla.squaremap.common.config.WorldConfig;
import xyz.jpenilla.squaremap.common.data.storage.AdditionalParameters;
import xyz.jpenilla.squaremap.common.data.storage.DataStorageHolder;
import xyz.jpenilla.squaremap.common.layer.SpawnIconProvider;
import xyz.jpenilla.squaremap.common.layer.WorldBorderProvider;
import xyz.jpenilla.squaremap.common.task.render.AbstractRender;
import xyz.jpenilla.squaremap.common.task.render.BackgroundRender;
import xyz.jpenilla.squaremap.common.task.render.FullRender;
import xyz.jpenilla.squaremap.common.util.Colors;
import xyz.jpenilla.squaremap.common.util.FileUtil;
import xyz.jpenilla.squaremap.common.util.RecordTypeAdapterFactory;
import xyz.jpenilla.squaremap.common.util.Util;
import xyz.jpenilla.squaremap.common.visibilitylimit.VisibilityLimitImpl;

@DefaultQualifier(NonNull.class)
public abstract class MapWorldInternal implements MapWorld {
private static final String DIRTY_CHUNKS_FILE_NAME = "dirty_chunks.json";
private static final String RENDER_PROGRESS_FILE_NAME = "resume_render.json";
private static final Gson GSON = new GsonBuilder()
.registerTypeAdapterFactory(new RecordTypeAdapterFactory())
.enableComplexMapKeySerialization()
.create();
private static final Map<WorldIdentifier, LayerRegistry> LAYER_REGISTRIES = new HashMap<>();

private final ServerLevel level;
private final Path dataPath;
private final Path tilesPath;
private final ExecutorService imageIOexecutor;
private final ScheduledExecutorService executor;
Expand Down Expand Up @@ -90,17 +71,6 @@ protected MapWorldInternal(final ServerLevel level) {
this.blockColors = new BlockColors(this);
this.levelBiomeColorData = LevelBiomeColorData.create(this);

this.dataPath = SquaremapCommon.instance().platform().dataDirectory().resolve("data").resolve(
Util.levelWebName(this.level)
);
try {
if (!Files.exists(this.dataPath)) {
Files.createDirectories(this.dataPath);
}
} catch (final IOException e) {
throw this.failedToCreateDataDirectory(e);
}

this.tilesPath = FileUtil.getAndCreateTilesDirectory(this.serverLevel());

this.startBackgroundRender();
Expand All @@ -124,53 +94,22 @@ protected MapWorldInternal(final ServerLevel level) {
}

public @Nullable Map<RegionCoordinate, Boolean> getRenderProgress() {
try {
final Path file = this.dataPath.resolve(RENDER_PROGRESS_FILE_NAME);
if (Files.isRegularFile(file)) {
final Type type = new TypeToken<LinkedHashMap<RegionCoordinate, Boolean>>() {
}.getType();
try (final BufferedReader reader = Files.newBufferedReader(file)) {
return GSON.fromJson(reader, type);
}
}
} catch (JsonIOException | JsonSyntaxException | IOException e) {
Logging.logger().warn("Failed to deserialize render progress for world '{}'", this.identifier().asString(), e);
}
return null;
return DataStorageHolder.getDataStorage().getRenderProgress(
this.identifier(),
new AdditionalParameters().put("levelWebName", Util.levelWebName(this.level))
).join();
}

public void saveRenderProgress(Map<RegionCoordinate, Boolean> regions) {
try {
Files.writeString(this.dataPath.resolve(RENDER_PROGRESS_FILE_NAME), GSON.toJson(regions));
} catch (IOException e) {
Logging.logger().warn("Failed to serialize render progress for world '{}'", this.identifier().asString(), e);
}
DataStorageHolder.getDataStorage().storeRenderProgress(this.identifier(), regions, new AdditionalParameters().put("levelWebName", Util.levelWebName(this.level)));
}

private void serializeDirtyChunks() {
try {
Files.writeString(this.dataPath.resolve(DIRTY_CHUNKS_FILE_NAME), GSON.toJson(this.modifiedChunks));
} catch (IOException e) {
Logging.logger().warn("Failed to serialize dirty chunks for world '{}'", this.identifier().asString(), e);
}
DataStorageHolder.getDataStorage().storeDirtyChunks(this.identifier(), this.modifiedChunks, new AdditionalParameters().put("levelWebName", Util.levelWebName(this.level)));
}

private void deserializeDirtyChunks() {
try {
final Path file = this.dataPath.resolve(DIRTY_CHUNKS_FILE_NAME);
if (Files.isRegularFile(file)) {
try (final BufferedReader reader = Files.newBufferedReader(file)) {
this.modifiedChunks.addAll(
GSON.fromJson(
reader,
TypeToken.getParameterized(List.class, ChunkCoordinate.class).getType()
)
);
}
}
} catch (JsonIOException | JsonSyntaxException | IOException e) {
Logging.logger().warn("Failed to deserialize dirty chunks for world '{}'", this.identifier().asString(), e);
}
this.modifiedChunks.addAll(DataStorageHolder.getDataStorage().getDirtyChunks(this.identifier(), new AdditionalParameters().put("levelWebName", Util.levelWebName(this.level))).join());
}

private void startBackgroundRender() {
Expand Down Expand Up @@ -255,11 +194,7 @@ public void pauseRenders(boolean pauseRenders) {
}

public void finishedRender() {
try {
Files.deleteIfExists(this.dataPath.resolve(RENDER_PROGRESS_FILE_NAME));
} catch (IOException e) {
Logging.logger().warn("Failed to delete render progress data for world '{}'", this.identifier().asString(), e);
}
DataStorageHolder.getDataStorage().deleteRenderProgress(this.identifier(), new AdditionalParameters().put("levelWebName", Util.levelWebName(this.level)));
}

public void stopRender() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package xyz.jpenilla.squaremap.common.data.storage;

import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;

@DefaultQualifier(NonNull.class)
public final class AdditionalParameters {

private final Map<String, Object> parameters = new ConcurrentHashMap<>();

public AdditionalParameters put(String key, Object value) {
this.parameters.put(key, value);
return this;
}

public Optional<Object> get(String key) {
return Optional.ofNullable(parameters.get(key));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package xyz.jpenilla.squaremap.common.data.storage;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;
import xyz.jpenilla.squaremap.api.WorldIdentifier;
import xyz.jpenilla.squaremap.common.data.ChunkCoordinate;
import xyz.jpenilla.squaremap.common.data.RegionCoordinate;

@DefaultQualifier(NonNull.class)
public interface DataStorage {

void storeDirtyChunks(WorldIdentifier world, Set<ChunkCoordinate> dirtyChunkCoordinates, AdditionalParameters parameters);

CompletableFuture<Set<ChunkCoordinate>> getDirtyChunks(WorldIdentifier world, AdditionalParameters parameters);

void storeRenderProgress(WorldIdentifier world, Map<RegionCoordinate, Boolean> renderProgress, AdditionalParameters parameters);

CompletableFuture<Map<RegionCoordinate, Boolean>> getRenderProgress(WorldIdentifier world, AdditionalParameters parameters);

void deleteRenderProgress(WorldIdentifier world, AdditionalParameters parameters);

void updateMarkers(WorldIdentifier world, List<Map<String, Object>> layers, AdditionalParameters parameters);

void updateWorldSettings(WorldIdentifier world, Map<String, Object> settings, AdditionalParameters parameters);

void updateGlobalSettings(WorldIdentifier world, Map<String, Object> settings, AdditionalParameters parameters);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package xyz.jpenilla.squaremap.common.data.storage;

import xyz.jpenilla.squaremap.common.config.Config;

public class DataStorageHolder {

private static DataStorage dataStorage;

public static DataStorage getDataStorage() {
if (dataStorage == null) {
switch (Config.STORAGE_TYPE) {
case FLATFILE -> dataStorage = new FlatfileDataStorage();
}
}
return dataStorage;
}

}