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

Add variant support for slugs & server version limitation #1356

Merged
merged 5 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions core/src/main/java/tc/oc/pgm/api/map/MapInfo.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package tc.oc.pgm.api.map;

import com.google.common.collect.Range;
import java.time.LocalDate;
import java.util.Collection;
import java.util.Map;
Expand Down Expand Up @@ -34,6 +35,14 @@ public interface MapInfo extends Comparable<MapInfo>, Cloneable {
*/
Map<String, VariantInfo> getVariants();

/**
* Get what servers versions should load this map, servers outside the range should ignore the
* map.
*
* @return range of the server versions that can load this map
*/
Range<Version> getServerVersion();

/** @return the subfolder in which the world is in, or null for the parent folder */
@Nullable
String getWorldFolder();
Expand Down Expand Up @@ -203,5 +212,7 @@ interface VariantInfo {
String getMapName();

String getWorld();

Range<Version> getServerVersions();
}
}
11 changes: 10 additions & 1 deletion core/src/main/java/tc/oc/pgm/map/MapFactoryImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand Down Expand Up @@ -35,6 +36,7 @@
import tc.oc.pgm.regions.RegionParser;
import tc.oc.pgm.util.ClassLogger;
import tc.oc.pgm.util.Version;
import tc.oc.pgm.util.platform.Platform;
import tc.oc.pgm.util.xml.DocumentWrapper;
import tc.oc.pgm.util.xml.InvalidXMLException;
import tc.oc.pgm.util.xml.Node;
Expand All @@ -55,7 +57,8 @@ public class MapFactoryImpl extends ModuleGraph<MapModule<?>, MapModuleFactory<?

public MapFactoryImpl(Logger logger, MapSource source, MapIncludeProcessor includes) {
super(Modules.MAP, Modules.MAP_DEPENDENCY_ONLY); // Don't copy, avoid N factory copies
this.logger = ClassLogger.get(assertNotNull(logger), getClass(), assertNotNull(source).getId());
this.logger =
ClassLogger.get(assertNotNull(logger), getClass(), assertNotNull(source).getId());
this.source = source;
this.includes = includes;
}
Expand All @@ -75,6 +78,12 @@ public MapContext load() throws MapException {
document = MapFilePreprocessor.getDocument(source, includes);

info = new MapInfoImpl(source, document.getRootElement());

// We're not loading this map, return a dummy map context to allow variants to load, if needed
if (!info.getServerVersion().contains(Platform.MINECRAFT_VERSION)) {
return new MapContextImpl(info, List.of());
}

try {
loadAll();
} catch (ModuleLoadException e) {
Expand Down
86 changes: 64 additions & 22 deletions core/src/main/java/tc/oc/pgm/map/MapInfoImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
import static net.kyori.adventure.text.Component.translatable;
import static tc.oc.pgm.api.map.MapSource.DEFAULT_VARIANT;
import static tc.oc.pgm.util.Assert.assertNotNull;
import static tc.oc.pgm.util.bukkit.MiscUtils.MISC_UTILS;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Range;
import java.lang.ref.SoftReference;
import java.time.LocalDate;
import java.util.*;
Expand Down Expand Up @@ -56,6 +58,7 @@ public class MapInfoImpl implements MapInfo {
private final Map<String, VariantInfo> variants;

private final String worldFolder;
private final Range<Version> serverVersion;
private final Version proto;
private final Version version;
private final Phase phase;
Expand Down Expand Up @@ -88,6 +91,7 @@ public MapInfoImpl(MapSource source, Element root) throws InvalidXMLException {
if (variant == null) throw new InvalidXMLException("Could not find variant definition", root);

this.worldFolder = variant.getWorld();
this.serverVersion = variant.getServerVersions();

this.name = variant.getMapName();
this.normalizedName = StringUtils.normalize(name);
Expand All @@ -97,30 +101,26 @@ public MapInfoImpl(MapSource source, Element root) throws InvalidXMLException {
this.proto = assertNotNull(XMLUtils.parseSemanticVersion(Node.fromRequiredAttr(root, "proto")));
this.version =
assertNotNull(XMLUtils.parseSemanticVersion(Node.fromRequiredChildOrAttr(root, "version")));
this.description =
assertNotNull(
Node.fromRequiredChildOrAttr(root, "objective", "description").getValueNormalize());
this.description = assertNotNull(
Node.fromRequiredChildOrAttr(root, "objective", "description").getValueNormalize());
this.created = XMLUtils.parseDate(Node.fromChildOrAttr(root, "created"));
this.authors = parseContributors(root, "author");
this.contributors = parseContributors(root, "contributor");
this.rules = parseRules(root);
this.difficulty =
XMLUtils.parseEnum(
Node.fromLastChildOrAttr(root, "difficulty"), Difficulty.class, Difficulty.NORMAL)
.ordinal();
this.difficulty = XMLUtils.parseEnum(
Node.fromLastChildOrAttr(root, "difficulty"), Difficulty.class, Difficulty.NORMAL)
.ordinal();
this.world = parseWorld(root);
this.gamemode = XMLUtils.parseFormattedText(Node.fromLastChildOrAttr(root, "game"));
this.gamemodes = parseGamemodes(root);
this.phase =
XMLUtils.parseEnum(Node.fromLastChildOrAttr(root, "phase"), Phase.class, Phase.PRODUCTION);
this.friendlyFire =
XMLUtils.parseBoolean(
Node.fromLastChildOrAttr(root, "friendlyfire", "friendly-fire"), false);
this.friendlyFire = XMLUtils.parseBoolean(
Node.fromLastChildOrAttr(root, "friendlyfire", "friendly-fire"), false);
}

@NotNull
private static Map<String, VariantInfo> createVariantMap(Element root)
throws InvalidXMLException {
private Map<String, VariantInfo> createVariantMap(Element root) throws InvalidXMLException {
ImmutableMap.Builder<String, VariantInfo> variants = ImmutableMap.builder();
variants.put(DEFAULT_VARIANT, new VariantData(root, null));
for (Element el : root.getChildren("variant")) {
Expand All @@ -145,6 +145,11 @@ public Map<String, VariantInfo> getVariants() {
return variants;
}

@Override
public Range<Version> getServerVersion() {
return serverVersion;
}

@Override
public String getWorldFolder() {
return worldFolder;
Expand Down Expand Up @@ -349,28 +354,32 @@ protected void setContext(MapContextImpl context) {
// If the map defines no game-modes manually, derive them from map tags, sorted by auxiliary
// last.
if (this.gamemodes.isEmpty()) {
this.gamemodes =
this.tags.stream()
.filter(MapTag::isGamemode)
.sorted(
Comparator.comparing(MapTag::isAuxiliary)
.thenComparing(Comparator.naturalOrder()))
.map(MapTag::getGamemode)
.collect(StreamUtils.toImmutableList());
this.gamemodes = this.tags.stream()
.filter(MapTag::isGamemode)
.sorted(
Comparator.comparing(MapTag::isAuxiliary).thenComparing(Comparator.naturalOrder()))
.map(MapTag::getGamemode)
.collect(StreamUtils.toImmutableList());
}
}
this.context = new SoftReference<>(context);
}

private static class VariantData implements VariantInfo {
private class VariantData implements VariantInfo {
// taken from https://minecraft.wiki/w/Data_version#Java_Edition
private static final int MAP_DATA_VERSION_1_13 = 1519;
private static final Version VERSION_1_13 = new Version(1, 13, 0);
private final String variantId;
private final String mapName;
private final String mapId;
private final String world;
private final Range<Version> serverVersions;

public VariantData(Element root, @Nullable Element variantEl) throws InvalidXMLException {
String name = assertNotNull(Node.fromRequiredChildOrAttr(root, "name").getValueNormalize());
String slug = assertNotNull(root).getChildTextNormalize("slug");
Node minVer = Node.fromAttr(root, "min-server-version");
Node maxVer = Node.fromAttr(root, "max-server-version");

if (variantEl == null) {
this.variantId = DEFAULT_VARIANT;
Expand All @@ -384,9 +393,23 @@ public VariantData(Element root, @Nullable Element variantEl) throws InvalidXMLE
boolean override = XMLUtils.parseBoolean(Node.fromAttr(variantEl, "override"), false);
this.mapName = (override ? "" : name + ": ") + variantEl.getTextNormalize();
this.world = variantEl.getAttributeValue("world");
if (slug != null) slug += "_" + variantId;

String variantSlug = variantEl.getAttributeValue("slug");
if (variantSlug != null) slug = variantSlug;
else if (slug != null) slug += "_" + variantId;

Node minVerVariant = Node.fromAttr(variantEl, "min-server-version");
if (minVerVariant != null) minVer = minVerVariant;

Node maxVerVariant = Node.fromAttr(variantEl, "max-server-version");
if (maxVerVariant != null) minVer = minVerVariant;
}
this.mapId = assertNotNull(slug != null ? slug : StringUtils.slugify(mapName));

this.serverVersions = XMLUtils.parseClosedRange(
minVer,
parseOrInferMinimumVersion(source, minVer),
XMLUtils.parseSemanticVersion(maxVer));
}

@Override
Expand All @@ -408,5 +431,24 @@ public String getMapName() {
public String getWorld() {
return world;
}

@Override
public Range<Version> getServerVersions() {
return serverVersions;
}

@Nullable
private Version parseOrInferMinimumVersion(MapSource source, @Nullable Node minVer)
throws InvalidXMLException {
if (minVer != null) return XMLUtils.parseSemanticVersion(minVer);
/* Infer the map version from the DataVersion field in level.dat. If 1.13+, set that as
the min version to avoid legacy servers crashing when loading the chunks. */
var sourceDir = source.getAbsoluteDir();
var levelDat = (world != null ? sourceDir.resolve(world) : sourceDir).resolve("level.dat");

var mapDataVersion = MISC_UTILS.getWorldDataVersion(levelDat);
if (mapDataVersion >= MAP_DATA_VERSION_1_13) return VERSION_1_13;
return null;
}
}
}
Loading