Skip to content

Commit

Permalink
Match overworld dimension instead of generating it
Browse files Browse the repository at this point in the history
This commit changes the BetaMinecraftInterface to instantiate the real
DimensionOverworld class, rather than generating an inheriting class
with ByteBuddy. To accomplish this, I expanded the ClassTranslator to
allow access to previously matched classes. This now lets us specify
"empty class that inherits from DimensionBase", thus allowing us to
match DimensionOverworld.

I had to change ClassTranslator.translations to a List, so that
iteration order would be guaranteed. I don't see why this field was a
map before, other than the need to store tuples.
  • Loading branch information
kahomayo authored and moulins committed Feb 11, 2021
1 parent d25de41 commit 64d6095
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 37 deletions.
14 changes: 8 additions & 6 deletions src/main/java/amidst/clazz/real/RealClassDetector.java
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
package amidst.clazz.real;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import amidst.documentation.Immutable;

@Immutable
public class RealClassDetector {
private final Predicate<RealClass> predicate;
private final BiPredicate<RealClass, Map<String, String>> predicate;

public RealClassDetector(Predicate<RealClass> predicate) {
public RealClassDetector(BiPredicate<RealClass, Map<String, String>> predicate) {
this.predicate = predicate;
}

public Optional<RealClass> firstMatching(List<RealClass> realClasses) {
return realClasses.stream().filter(predicate).findFirst();
public Optional<RealClass> firstMatching(List<RealClass> realClasses, Map<String, String> mappedNames) {
return realClasses.stream().filter(c -> predicate.test(c, mappedNames)).findFirst();
}

public List<RealClass> allMatching(List<RealClass> realClasses) {
return realClasses.stream().filter(predicate).collect(Collectors.toList());
public List<RealClass> allMatching(List<RealClass> realClasses, Map<String, String> mappedNames) {
return realClasses.stream().filter(c -> predicate.test(c, mappedNames)).collect(Collectors.toList());
}
}
18 changes: 12 additions & 6 deletions src/main/java/amidst/clazz/translator/CTBuilder.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package amidst.clazz.translator;

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiPredicate;
import java.util.function.Predicate;

import amidst.clazz.real.RealClass;
Expand Down Expand Up @@ -118,7 +120,11 @@ private CTBuilder(CTBuilder previous) {
}

public CTBuilder ifDetect(Predicate<RealClass> predicate) {
this.detector = new RealClassDetector(predicate);
return ifDetect((c, m) -> predicate.test(c));
}

public CTBuilder ifDetect(BiPredicate<RealClass, Map<String, String>> biPredicate) {
this.detector = new RealClassDetector(biPredicate);
return this;
}

Expand All @@ -142,17 +148,17 @@ public ClassTranslator construct() {
return new ClassTranslator(constructResult());
}

private Map<RealClassDetector, SymbolicClassDeclaration> constructResult() {
Map<RealClassDetector, SymbolicClassDeclaration> result = constructPreviousResult();
result.put(detector, declarationBuilder.constructThis());
private List<Map.Entry<RealClassDetector, SymbolicClassDeclaration>> constructResult() {
List<Map.Entry<RealClassDetector, SymbolicClassDeclaration>> result = constructPreviousResult();
result.add(new AbstractMap.SimpleImmutableEntry<>(detector, declarationBuilder.constructThis()));
return result;
}

private Map<RealClassDetector, SymbolicClassDeclaration> constructPreviousResult() {
private List<Map.Entry<RealClassDetector, SymbolicClassDeclaration>> constructPreviousResult() {
if (previous != null) {
return previous.constructResult();
} else {
return new HashMap<>();
return new ArrayList<>();
}
}
}
25 changes: 18 additions & 7 deletions src/main/java/amidst/clazz/translator/ClassTranslator.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package amidst.clazz.translator;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -17,33 +18,43 @@ public static CTBuilder builder() {
return CTBuilder.newInstance();
}

private final Map<RealClassDetector, SymbolicClassDeclaration> translations;
private final List<Map.Entry<RealClassDetector, SymbolicClassDeclaration>> translations;

public ClassTranslator(Map<RealClassDetector, SymbolicClassDeclaration> translations) {
public ClassTranslator(List<Map.Entry<RealClassDetector, SymbolicClassDeclaration>> translations) {
this.translations = translations;
}

public Map<SymbolicClassDeclaration, List<RealClass>> translateToAllMatching(List<RealClass> realClasses) {
Map<SymbolicClassDeclaration, List<RealClass>> result = new HashMap<>();
for (Entry<RealClassDetector, SymbolicClassDeclaration> entry : translations.entrySet()) {
Map<String, String> foundNames = new HashMap<>();
Map<String, String> foundNamesView = Collections.unmodifiableMap(foundNames);
for (Entry<RealClassDetector, SymbolicClassDeclaration> entry : translations) {
SymbolicClassDeclaration declaration = entry.getValue();
List<RealClass> allMatching = entry.getKey().allMatching(realClasses);
List<RealClass> allMatching = entry.getKey().allMatching(realClasses, foundNamesView);
if (result.containsKey(declaration)) {
result.get(declaration).addAll(allMatching);
} else {
result.put(declaration, allMatching);
allMatching.stream()
.findFirst()
.ifPresent(c -> foundNames.put(declaration.getSymbolicClassName(), c.getRealClassName()));
}
}
return result;
}

public Map<SymbolicClassDeclaration, String> translate(List<RealClass> realClasses) throws ClassNotFoundException {
Map<SymbolicClassDeclaration, String> result = new HashMap<>();
for (Entry<RealClassDetector, SymbolicClassDeclaration> entry : translations.entrySet()) {
Map<String, String> foundNames = new HashMap<>();
Map<String, String> foundNamesView = Collections.unmodifiableMap(foundNames);
for (Entry<RealClassDetector, SymbolicClassDeclaration> entry : translations) {
Optional<String> realClassName = entry.getKey().firstMatching(realClasses, foundNamesView).map(RealClass::getRealClassName);
SymbolicClassDeclaration declaration = entry.getValue();
realClassName.ifPresent(name -> foundNames.put(declaration.getSymbolicClassName(), name));
addResult(
result,
entry.getValue(),
entry.getKey().firstMatching(realClasses).map(RealClass::getRealClassName));
declaration,
realClassName);
}
return result;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package amidst.mojangapi.minecraftinterface.legacy;

import amidst.clazz.real.RealClass;
import amidst.clazz.translator.ClassTranslator;
import amidst.mojangapi.minecraftinterface.RecognisedVersion;

import java.util.Objects;

public enum BetaClassTranslator {
INSTANCE;

Expand All @@ -19,14 +22,21 @@ public static ClassTranslator get(RecognisedVersion version) {
.requiredMethod(BetaSymbolicNames.METHOD_BIOMEGENERATOR_GET_BIOME, "a").symbolicArray(BetaSymbolicNames.CLASS_BIOME, 1).real("int").real("int").real("int").real("int").end()
.requiredField(BetaSymbolicNames.FIELD_BIOMEGENERATOR_TEMPERATURE, "a")
.next()
.ifDetect(c -> c.searchForFloat(0.7529412f) && c.searchForFloat(0.84705883f))
.ifDetect(BetaClassTranslator::isDimensionBase)
.thenDeclareRequired(BetaSymbolicNames.CLASS_DIMENSION_BASE)
.requiredField(BetaSymbolicNames.FIELD_DIMENSION_WORLD, "a")
.requiredField(BetaSymbolicNames.FIELD_DIMENSION_BIOMEGENERATOR, "b")
.next()
.ifDetect((c, mappedNames) -> RecognisedVersion.isOlder(version, RecognisedVersion._b1_6_6)
? isDimensionBase(c)
: c.getNumberOfFields() == 0
&& c.getNumberOfMethods() == 0
&& mappedNames.get(BetaSymbolicNames.CLASS_DIMENSION_BASE).equals(c.getRealSuperClassName()))
.thenDeclareRequired(BetaSymbolicNames.CLASS_DIMENSION_OVERWORLD)
.next()
.ifDetect(c -> {
int nFields = RecognisedVersion.isOlder(version, RecognisedVersion._b1_6_6) ? 5 : 6;
return c.getNumberOfFields() == 0 && c.getNumberOfConstructors() == 0 && c.getNumberOfMethods() == nFields && c.isInterface() && c.hasMethodWithRealArgsReturning("void");
int nMethods = RecognisedVersion.isOlder(version, RecognisedVersion._b1_6_6) ? 5 : 6;
return c.getNumberOfFields() == 0 && c.getNumberOfConstructors() == 0 && c.getNumberOfMethods() == nMethods && c.isInterface() && c.hasMethodWithRealArgsReturning("void");
})
.thenDeclareRequired(BetaSymbolicNames.INTERFACE_SOMETHING)
.next()
Expand Down Expand Up @@ -66,5 +76,9 @@ public static ClassTranslator get(RecognisedVersion version) {
.requiredField(BetaSymbolicNames.FIELD_PERLIN_NOISE_Z_OFFSET, "c")
.construct();
}

private static boolean isDimensionBase(RealClass c) {
return c.searchForFloat(0.7529412f) && c.searchForFloat(0.84705883f);
}
// @formatter:on
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,14 @@ public class BetaMinecraftInterface implements MinecraftInterface {
private final SymbolicClass worldClass;
private final SymbolicClass overworldLevelSourceClass;
private final BiomeMapping biomeMapping;
private final Class<?> dimensionImplClass;
private final SymbolicClass dimensionOverworldClass;
private final Class<?> hackedNoiseClass;
private final SymbolicClass perlinNoiseClass;

private BetaMinecraftInterface(
SymbolicClass biomeClass,
SymbolicClass dimensionBaseClass,
SymbolicClass dimensionOverworldClass,
SymbolicClass worldClass,
SymbolicClass overworldLevelSourceClass,
SymbolicClass perlinOctaveNoiseClass,
Expand All @@ -53,7 +54,7 @@ private BetaMinecraftInterface(
this.worldClass = worldClass;
this.overworldLevelSourceClass = overworldLevelSourceClass;
this.perlinNoiseClass = perlinNoiseClass;
this.dimensionImplClass = makeInstantiableDimension(dimensionBaseClass);
this.dimensionOverworldClass = dimensionOverworldClass;
this.hackedNoiseClass = makeInterpolationNoiseClass(perlinOctaveNoiseClass);
try {
this.biomeMapping = new BiomeMapping(biomeClass);
Expand All @@ -67,6 +68,7 @@ public BetaMinecraftInterface(Map<String, SymbolicClass> stringSymbolicClassMap,
this(
stringSymbolicClassMap.get(BetaSymbolicNames.CLASS_BIOME),
stringSymbolicClassMap.get(BetaSymbolicNames.CLASS_DIMENSION_BASE),
stringSymbolicClassMap.get(BetaSymbolicNames.CLASS_DIMENSION_OVERWORLD),
stringSymbolicClassMap.get(BetaSymbolicNames.CLASS_WORLD),
stringSymbolicClassMap.get(BetaSymbolicNames.CLASS_OVERWORLD_LEVEL_SOURCE),
stringSymbolicClassMap.get(BetaSymbolicNames.CLASS_PERLIN_OCTAVE_NOISE),
Expand All @@ -92,20 +94,8 @@ public RecognisedVersion getRecognisedVersion() {
return recognisedVersion;
}

private Class<?> makeInstantiableDimension(SymbolicClass dimensionBaseClass) {
// Since b1.6, the Dimension class is abstract, with OverworldDimension
// being an empty class inheriting from it. Our ClassTranslator cannot
// match "empty class that inherits from Overworld", so we just generate
// an equivalent class at runtime.
return new ByteBuddy()
.subclass(dimensionBaseClass.getClazz())
.make()
.load(dimensionBaseClass.getClazz().getClassLoader())
.getLoaded();
}

private SymbolicObject constructDimension() throws IllegalAccessException, InstantiationException {
return new SymbolicObject(dimensionBaseClass, dimensionImplClass.newInstance());
return new SymbolicObject(dimensionBaseClass, dimensionOverworldClass.getClazz().newInstance());
}

private SymbolicObject constructWorld(SymbolicObject overworldDimension, WorldOptions worldOptions) throws InvocationTargetException, IllegalAccessException, InstantiationException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public enum BetaSymbolicNames {
public static final String FIELD_DIMENSION_WORLD = "world";
public static final String INTERFACE_SOMETHING = "ISomething";

public static final String CLASS_DIMENSION_OVERWORLD = "DimensionOverworld";

public static final String CLASS_PERLIN_OCTAVE_NOISE = "PerlinOctaveNoise";
public static final String METHOD_PERLIN_OCTAVE_NOISE_SAMPLE_3D = "sample";
public static final String FIELD_PERLIN_OCTAVE_NOISE_OCTAVES = "octaves";
Expand Down

0 comments on commit 64d6095

Please sign in to comment.