Skip to content

Commit

Permalink
Rework storages to make them extensible with addons
Browse files Browse the repository at this point in the history
  • Loading branch information
TBlueF committed Apr 5, 2024
1 parent 7e7b1e4 commit fdf242a
Show file tree
Hide file tree
Showing 73 changed files with 3,176 additions and 3,306 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ private synchronized void loadMap(String id, MapConfig mapConfig) throws Configu
id,
name,
world,
storage,
storage.map(id),
getOrLoadResourcePack(),
mapConfig
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
package de.bluecolored.bluemap.common.api;

import de.bluecolored.bluemap.api.AssetStorage;
import de.bluecolored.bluemap.core.storage.Storage;
import de.bluecolored.bluemap.core.storage.MapStorage;
import de.bluecolored.bluemap.core.storage.compression.CompressedInputStream;

import java.io.IOException;
import java.io.InputStream;
Expand All @@ -34,39 +35,39 @@

public class AssetStorageImpl implements AssetStorage {

private static final String ASSET_PATH = "assets/";

private final Storage storage;
private final MapStorage storage;
private final String mapId;

public AssetStorageImpl(Storage storage, String mapId) {
public AssetStorageImpl(MapStorage storage, String mapId) {
this.storage = storage;
this.mapId = mapId;
}

@Override
public OutputStream writeAsset(String name) throws IOException {
return storage.writeMeta(mapId, ASSET_PATH + name);
return storage.asset(name).write();
}

@Override
public Optional<InputStream> readAsset(String name) throws IOException {
return storage.readMeta(mapId, ASSET_PATH + name);
CompressedInputStream in = storage.asset(name).read();
if (in == null) return Optional.empty();
return Optional.of(in.decompress());
}

@Override
public boolean assetExists(String name) throws IOException {
return storage.readMetaInfo(mapId, ASSET_PATH + name).isPresent();
return storage.asset(name).exists();
}

@Override
public String getAssetUrl(String name) {
return "maps/" + mapId + "/" + Storage.escapeMetaName(ASSET_PATH + name);
return "maps/" + mapId + "/assets/" + MapStorage.escapeAssetName(name);
}

@Override
public void deleteAsset(String name) throws IOException {
storage.deleteMeta(mapId, ASSET_PATH + name);
storage.asset(name).delete();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public void registerStyle(String url) {

@Override
@Deprecated(forRemoval = true)
@SuppressWarnings("removal")
public String createImage(BufferedImage image, String path) throws IOException {
path = path.replaceAll("[^a-zA-Z0-9_.\\-/]", "_");

Expand All @@ -102,6 +103,7 @@ public String createImage(BufferedImage image, String path) throws IOException {

@Override
@Deprecated(forRemoval = true)
@SuppressWarnings("removal")
public Map<String, String> availableImages() throws IOException {
Path webRoot = getWebRoot().toAbsolutePath();
String separator = webRoot.getFileSystem().getSeparator();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,18 +269,11 @@ private Map<String, MapConfig> loadMapConfigs(Collection<ServerWorld> autoConfig
String name = worldFolder.getFileName() + " (" + dimensionName + ")";
if (i > 1) name = name + " (" + i + ")";

ConfigTemplate template;
switch (world.getDimension().getFormatted()) {
case "minecraft:the_nether":
template = createNetherMapTemplate(name, worldFolder, dimension, i - 1);
break;
case "minecraft:the_end":
template = createEndMapTemplate(name, worldFolder, dimension, i - 1);
break;
default:
template = createOverworldMapTemplate(name, worldFolder, dimension, i - 1);
break;
}
ConfigTemplate template = switch (world.getDimension().getFormatted()) {
case "minecraft:the_nether" -> createNetherMapTemplate(name, worldFolder, dimension, i - 1);
case "minecraft:the_end" -> createEndMapTemplate(name, worldFolder, dimension, i - 1);
default -> createOverworldMapTemplate(name, worldFolder, dimension, i - 1);
};

Files.writeString(
configFile,
Expand Down Expand Up @@ -358,7 +351,7 @@ private Map<String, StorageConfig> loadStorageConfigs(Path defaultWebroot) throw
Path rawConfig = configManager.getRaw(configFile);
String id = rawConfig.getFileName().toString();

StorageConfig storageConfig = configManager.loadConfig(rawConfig, StorageConfig.class); // load superclass
StorageConfig storageConfig = configManager.loadConfig(rawConfig, StorageConfig.Base.class); // load superclass
storageConfig = configManager.loadConfig(rawConfig, storageConfig.getStorageType().getConfigType()); // load actual config type

storageConfigs.put(id, storageConfig);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package de.bluecolored.bluemap.common.config.storage;

import de.bluecolored.bluemap.core.storage.sql.Database;
import de.bluecolored.bluemap.core.storage.sql.commandset.CommandSet;
import de.bluecolored.bluemap.core.storage.sql.commandset.MySQLCommandSet;
import de.bluecolored.bluemap.core.storage.sql.commandset.PostgreSQLCommandSet;
import de.bluecolored.bluemap.core.storage.sql.commandset.SqliteCommandSet;
import de.bluecolored.bluemap.core.util.Key;
import de.bluecolored.bluemap.core.util.Keyed;
import de.bluecolored.bluemap.core.util.Registry;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.util.function.Function;

public interface Dialect extends Keyed {

Dialect MYSQL = new Impl(Key.bluemap("mysql"), MySQLCommandSet::new);
Dialect MARIADB = new Impl(Key.bluemap("mariadb"), MySQLCommandSet::new);
Dialect POSTGRESQL = new Impl(Key.bluemap("postgresql"), PostgreSQLCommandSet::new);
Dialect SQLITE = new Impl(Key.bluemap("sqlite"), SqliteCommandSet::new);

Registry<Dialect> REGISTRY = new Registry<>(
MYSQL,
MARIADB,
POSTGRESQL,
SQLITE
);

CommandSet createCommandSet(Database database);

@RequiredArgsConstructor
class Impl implements Dialect {

@Getter
private final Key key;

private final Function<Database, CommandSet> commandSetProvider;

@Override
public CommandSet createCommandSet(Database database) {
return commandSetProvider.apply(database);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,30 @@
package de.bluecolored.bluemap.common.config.storage;

import de.bluecolored.bluemap.api.debug.DebugDump;
import de.bluecolored.bluemap.core.storage.Compression;
import de.bluecolored.bluemap.core.storage.file.FileStorageSettings;
import de.bluecolored.bluemap.common.config.ConfigurationException;
import de.bluecolored.bluemap.core.storage.compression.Compression;
import de.bluecolored.bluemap.core.storage.file.FileStorage;
import lombok.Getter;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;

import java.nio.file.Path;

@SuppressWarnings("FieldMayBeFinal")
@DebugDump
@ConfigSerializable
public class FileConfig extends StorageConfig implements FileStorageSettings {
@Getter
public class FileConfig extends StorageConfig {

private Path root = Path.of("bluemap", "web", "maps");
private String compression = Compression.GZIP.getKey().getFormatted();

private Compression compression = Compression.GZIP;

@Override
public Path getRoot() {
return root;
public Compression getCompression() throws ConfigurationException {
return parseKey(Compression.REGISTRY, compression, "compression");
}

@Override
public Compression getCompression() {
return compression;
public FileStorage createStorage() throws ConfigurationException {
return new FileStorage(root, getCompression());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,67 +25,146 @@
package de.bluecolored.bluemap.common.config.storage;

import de.bluecolored.bluemap.api.debug.DebugDump;
import de.bluecolored.bluemap.core.storage.Compression;
import de.bluecolored.bluemap.core.storage.sql.SQLStorageSettings;
import de.bluecolored.bluemap.common.config.ConfigurationException;
import de.bluecolored.bluemap.core.storage.compression.Compression;
import de.bluecolored.bluemap.core.storage.sql.Database;
import de.bluecolored.bluemap.core.storage.sql.SQLStorage;
import de.bluecolored.bluemap.core.storage.sql.commandset.CommandSet;
import de.bluecolored.bluemap.core.util.Key;
import lombok.AccessLevel;
import lombok.Getter;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;

import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Driver;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@SuppressWarnings({"FieldMayBeFinal", "FieldCanBeLocal"})
@ConfigSerializable
public class SQLConfig extends StorageConfig implements SQLStorageSettings {
@Getter
public class SQLConfig extends StorageConfig {

@DebugDump private String driverJar = null;
@DebugDump private String driverClass = null;
private String connectionUrl = "jdbc:mysql://localhost/bluemap?permitMysqlScheme";
private static final Pattern URL_DIALECT_PATTERN = Pattern.compile("jdbc:([^:]*)://.*");

private String connectionUrl = "jdbc:mysql://localhost/bluemap?permitMysqlScheme";
private Map<String, String> connectionProperties = new HashMap<>();
@DebugDump private String dialect = null;

@DebugDump private Compression compression = Compression.GZIP;
@DebugDump private String driverJar = null;
@DebugDump private String driverClass = null;
@DebugDump private int maxConnections = -1;

@DebugDump private transient URL driverJarURL = null;
@DebugDump private String compression = Compression.GZIP.getKey().getFormatted();

@DebugDump private int maxConnections = -1;
@DebugDump
@Getter(AccessLevel.NONE)
private transient URL driverJarURL = null;

@Override
public Optional<URL> getDriverJar() throws MalformedURLException {
if (driverJar == null) return Optional.empty();
public Optional<URL> getDriverJar() throws ConfigurationException {
try {
if (driverJar == null) return Optional.empty();

if (driverJarURL == null) {
driverJarURL = Paths.get(driverJar).toUri().toURL();
}
if (driverJarURL == null) {
driverJarURL = Paths.get(driverJar).toUri().toURL();
}

return Optional.of(driverJarURL);
return Optional.of(driverJarURL);
} catch (MalformedURLException ex) {
throw new ConfigurationException("""
The configured driver-jar path is not formatted correctly!
Please check your 'driver-jar' setting in your configuration and make sure you have the correct path configured.
""".strip(), ex);
}
}

@Override
@SuppressWarnings("unused")
public Optional<String> getDriverClass() {
return Optional.ofNullable(driverClass);
}

@Override
public String getConnectionUrl() {
return connectionUrl;
public Compression getCompression() throws ConfigurationException {
return parseKey(Compression.REGISTRY, compression, "compression");
}

@Override
public Map<String, String> getConnectionProperties() {
return connectionProperties;
public Dialect getDialect() throws ConfigurationException {
String key = dialect;

// default from connection-url
if (key == null) {
Matcher matcher = URL_DIALECT_PATTERN.matcher(connectionUrl);
if (!matcher.find()) return Dialect.MYSQL;
key = Key.bluemap(matcher.group(1)).getFormatted();
}

return parseKey(Dialect.REGISTRY, key, "dialect");
}

@Override
public int getMaxConnections() {
return maxConnections;
public SQLStorage createStorage() throws ConfigurationException {
Driver driver = createDriver();
Database database;
if (driver != null) {
database = new Database(getConnectionUrl(), getConnectionProperties(), getMaxConnections(), driver);
} else {
database = new Database(getConnectionUrl(), getConnectionProperties(), getMaxConnections());
}
CommandSet commandSet = getDialect().createCommandSet(database);
return new SQLStorage(commandSet, getCompression());
}

@Override
public Compression getCompression() {
return compression;
private @Nullable Driver createDriver() throws ConfigurationException {
if (driverClass == null) return null;

try {
// load driver class
Class<?> driverClazz;
URL driverJarUrl = getDriverJar().orElse(null);
if (driverJarUrl != null) {

// sanity-check if file exists
if (!Files.exists(Path.of(driverJarUrl.toURI()))) {
throw new ConfigurationException("""
The configured driver-jar was not found!
Please check your 'driver-jar' setting in your configuration and make sure you have the correct path configured.
""".strip());
}

ClassLoader classLoader = new URLClassLoader(new URL[]{driverJarUrl});
driverClazz = Class.forName(driverClass, true, classLoader);
} else {
driverClazz = Class.forName(driverClass);
}

// create driver
return (Driver) driverClazz.getDeclaredConstructor().newInstance();
} catch (ClassCastException ex) {
throw new ConfigurationException("""
The configured driver-class was found but is not of the correct class-type!
Please check your 'driver-class' setting in your configuration and make sure you have the correct class configured.
""".strip(), ex);
} catch (ClassNotFoundException ex) {
throw new ConfigurationException("""
The configured driver-class was not found!
Please check your 'driver-class' setting in your configuration and make sure you have the correct class configured.
""".strip(), ex);
} catch (ConfigurationException ex) {
throw ex;
} catch (Exception ex) {
throw new ConfigurationException("""
BlueMap failed to load the configured SQL-Driver!
Please check your 'driver-jar' and 'driver-class' settings in your configuration.
""".strip(), ex);
}
}

}
Loading

0 comments on commit fdf242a

Please sign in to comment.