Merge pull request #138 from PolyhedralDev/dev/profilerimpl

Fancy stack-based profiler
This commit is contained in:
dfsek 2021-04-26 21:32:42 -07:00 committed by GitHub
commit eee54f507e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 548 additions and 428 deletions

View File

@ -1,6 +1,6 @@
import com.dfsek.terra.getGitHash import com.dfsek.terra.getGitHash
val versionObj = Version("5", "1", "3", true) val versionObj = Version("5", "2", "0", true)
allprojects { allprojects {
version = versionObj version = versionObj

View File

@ -12,6 +12,7 @@ import com.dfsek.terra.api.util.logging.Logger;
import com.dfsek.terra.config.PluginConfig; import com.dfsek.terra.config.PluginConfig;
import com.dfsek.terra.config.lang.Language; import com.dfsek.terra.config.lang.Language;
import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.profiler.Profiler;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
import java.io.File; import java.io.File;
@ -64,4 +65,6 @@ public interface TerraPlugin extends LoaderRegistrar {
default void runPossiblyUnsafeTask(Runnable task) { default void runPossiblyUnsafeTask(Runnable task) {
task.run(); task.run();
} }
Profiler getProfiler();
} }

View File

@ -29,6 +29,7 @@ import com.dfsek.terra.api.structures.structure.Rotation;
import com.dfsek.terra.api.structures.structure.buffer.Buffer; import com.dfsek.terra.api.structures.structure.buffer.Buffer;
import com.dfsek.terra.api.structures.structure.buffer.DirectBuffer; import com.dfsek.terra.api.structures.structure.buffer.DirectBuffer;
import com.dfsek.terra.api.structures.structure.buffer.StructureBuffer; import com.dfsek.terra.api.structures.structure.buffer.StructureBuffer;
import com.dfsek.terra.profiler.ProfileFrame;
import com.dfsek.terra.registry.config.FunctionRegistry; import com.dfsek.terra.registry.config.FunctionRegistry;
import com.dfsek.terra.registry.config.LootRegistry; import com.dfsek.terra.registry.config.LootRegistry;
import com.dfsek.terra.registry.config.ScriptRegistry; import com.dfsek.terra.registry.config.ScriptRegistry;
@ -39,6 +40,7 @@ import org.apache.commons.io.IOUtils;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Random; import java.util.Random;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -52,7 +54,7 @@ public class StructureScript {
public StructureScript(InputStream inputStream, TerraPlugin main, ScriptRegistry registry, LootRegistry lootRegistry, FunctionRegistry functionRegistry) throws ParseException { public StructureScript(InputStream inputStream, TerraPlugin main, ScriptRegistry registry, LootRegistry lootRegistry, FunctionRegistry functionRegistry) throws ParseException {
Parser parser; Parser parser;
try { try {
parser = new Parser(IOUtils.toString(inputStream)); parser = new Parser(IOUtils.toString(inputStream, Charset.defaultCharset()));
} catch(IOException e) { } catch(IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -104,22 +106,31 @@ public class StructureScript {
* @param rotation Rotation of structure * @param rotation Rotation of structure
* @return Whether generation was successful * @return Whether generation was successful
*/ */
@SuppressWarnings("try")
public boolean execute(Location location, Random random, Rotation rotation) { public boolean execute(Location location, Random random, Rotation rotation) {
StructureBuffer buffer = new StructureBuffer(location); try(ProfileFrame ignore = main.getProfiler().profile("terrascript:" + id)) {
boolean level = applyBlock(new TerraImplementationArguments(buffer, rotation, random, 0)); StructureBuffer buffer = new StructureBuffer(location);
buffer.paste(); boolean level = applyBlock(new TerraImplementationArguments(buffer, rotation, random, 0));
return level; buffer.paste();
return level;
}
} }
@SuppressWarnings("try")
public boolean execute(Location location, Chunk chunk, Random random, Rotation rotation) { public boolean execute(Location location, Chunk chunk, Random random, Rotation rotation) {
StructureBuffer buffer = computeBuffer(location, random, rotation); try(ProfileFrame ignore = main.getProfiler().profile("terrascript_chunk:" + id)) {
buffer.paste(chunk); StructureBuffer buffer = computeBuffer(location, random, rotation);
return buffer.succeeded(); buffer.paste(chunk);
return buffer.succeeded();
}
} }
@SuppressWarnings("try")
public boolean test(Location location, Random random, Rotation rotation) { public boolean test(Location location, Random random, Rotation rotation) {
StructureBuffer buffer = computeBuffer(location, random, rotation); try(ProfileFrame ignore = main.getProfiler().profile("terrascript_test:" + id)) {
return buffer.succeeded(); StructureBuffer buffer = computeBuffer(location, random, rotation);
return buffer.succeeded();
}
} }
private StructureBuffer computeBuffer(Location location, Random random, Rotation rotation) { private StructureBuffer computeBuffer(Location location, Random random, Rotation rotation) {
@ -134,13 +145,19 @@ public class StructureScript {
} }
} }
@SuppressWarnings("try")
public boolean executeInBuffer(Buffer buffer, Random random, Rotation rotation, int recursions) { public boolean executeInBuffer(Buffer buffer, Random random, Rotation rotation, int recursions) {
return applyBlock(new TerraImplementationArguments(buffer, rotation, random, recursions)); try(ProfileFrame ignore = main.getProfiler().profile("terrascript_recursive:" + id)) {
return applyBlock(new TerraImplementationArguments(buffer, rotation, random, recursions));
}
} }
@SuppressWarnings("try")
public boolean executeDirect(Location location, Random random, Rotation rotation) { public boolean executeDirect(Location location, Random random, Rotation rotation) {
DirectBuffer buffer = new DirectBuffer(location); try(ProfileFrame ignore = main.getProfiler().profile("terrascript_direct:" + id)) {
return applyBlock(new TerraImplementationArguments(buffer, rotation, random, 0)); DirectBuffer buffer = new DirectBuffer(location);
return applyBlock(new TerraImplementationArguments(buffer, rotation, random, 0));
}
} }
public String getId() { public String getId() {

View File

@ -53,6 +53,7 @@ public class Transformer<F, T> {
private final LinkedHashMap<Transform<F, T>, List<Validator<T>>> transforms = new LinkedHashMap<>(); private final LinkedHashMap<Transform<F, T>, List<Validator<T>>> transforms = new LinkedHashMap<>();
@SafeVarargs @SafeVarargs
@SuppressWarnings("varargs")
public final Builder<F, T> addTransform(Transform<F, T> transform, Validator<T>... validators) { public final Builder<F, T> addTransform(Transform<F, T> transform, Validator<T>... validators) {
transforms.put(transform, Arrays.asList(validators)); transforms.put(transform, Arrays.asList(validators));
return this; return this;

View File

@ -4,16 +4,10 @@ import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.command.CommandTemplate; import com.dfsek.terra.api.command.CommandTemplate;
import com.dfsek.terra.api.command.annotation.Command; import com.dfsek.terra.api.command.annotation.Command;
import com.dfsek.terra.api.command.annotation.type.DebugCommand; import com.dfsek.terra.api.command.annotation.type.DebugCommand;
import com.dfsek.terra.api.command.annotation.type.PlayerCommand;
import com.dfsek.terra.api.command.annotation.type.WorldCommand;
import com.dfsek.terra.api.injection.annotations.Inject; import com.dfsek.terra.api.injection.annotations.Inject;
import com.dfsek.terra.api.platform.CommandSender; import com.dfsek.terra.api.platform.CommandSender;
import com.dfsek.terra.api.platform.entity.Player;
import com.dfsek.terra.world.TerraWorld;
@Command @Command
@WorldCommand
@PlayerCommand
@DebugCommand @DebugCommand
public class ProfileQueryCommand implements CommandTemplate { public class ProfileQueryCommand implements CommandTemplate {
@Inject @Inject
@ -21,8 +15,9 @@ public class ProfileQueryCommand implements CommandTemplate {
@Override @Override
public void execute(CommandSender sender) { public void execute(CommandSender sender) {
Player player = (Player) sender; StringBuilder data = new StringBuilder("Terra Profiler data dump: \n");
TerraWorld world = main.getWorld(player.getWorld()); main.getProfiler().getTimings().forEach((id, timings) -> data.append(id).append(": ").append(timings.toString()).append('\n'));
player.sendMessage(world.getProfiler().getResultsFormatted()); main.logger().info(data.toString());
sender.sendMessage("Profiler data dumped to console.");
} }
} }

View File

@ -4,16 +4,10 @@ import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.command.CommandTemplate; import com.dfsek.terra.api.command.CommandTemplate;
import com.dfsek.terra.api.command.annotation.Command; import com.dfsek.terra.api.command.annotation.Command;
import com.dfsek.terra.api.command.annotation.type.DebugCommand; import com.dfsek.terra.api.command.annotation.type.DebugCommand;
import com.dfsek.terra.api.command.annotation.type.PlayerCommand;
import com.dfsek.terra.api.command.annotation.type.WorldCommand;
import com.dfsek.terra.api.injection.annotations.Inject; import com.dfsek.terra.api.injection.annotations.Inject;
import com.dfsek.terra.api.platform.CommandSender; import com.dfsek.terra.api.platform.CommandSender;
import com.dfsek.terra.api.platform.entity.Player;
import com.dfsek.terra.world.TerraWorld;
@Command @Command
@WorldCommand
@PlayerCommand
@DebugCommand @DebugCommand
public class ProfileResetCommand implements CommandTemplate { public class ProfileResetCommand implements CommandTemplate {
@Inject @Inject
@ -21,9 +15,7 @@ public class ProfileResetCommand implements CommandTemplate {
@Override @Override
public void execute(CommandSender sender) { public void execute(CommandSender sender) {
Player player = (Player) sender; main.getProfiler().reset();
TerraWorld world = main.getWorld(player.getWorld()); sender.sendMessage("Profiler reset.");
world.getProfiler().reset();
player.sendMessage("Profiler reset.");
} }
} }

View File

@ -4,16 +4,10 @@ import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.command.CommandTemplate; import com.dfsek.terra.api.command.CommandTemplate;
import com.dfsek.terra.api.command.annotation.Command; import com.dfsek.terra.api.command.annotation.Command;
import com.dfsek.terra.api.command.annotation.type.DebugCommand; import com.dfsek.terra.api.command.annotation.type.DebugCommand;
import com.dfsek.terra.api.command.annotation.type.PlayerCommand;
import com.dfsek.terra.api.command.annotation.type.WorldCommand;
import com.dfsek.terra.api.injection.annotations.Inject; import com.dfsek.terra.api.injection.annotations.Inject;
import com.dfsek.terra.api.platform.CommandSender; import com.dfsek.terra.api.platform.CommandSender;
import com.dfsek.terra.api.platform.entity.Player;
import com.dfsek.terra.world.TerraWorld;
@Command @Command
@WorldCommand
@PlayerCommand
@DebugCommand @DebugCommand
public class ProfileStartCommand implements CommandTemplate { public class ProfileStartCommand implements CommandTemplate {
@Inject @Inject
@ -21,9 +15,7 @@ public class ProfileStartCommand implements CommandTemplate {
@Override @Override
public void execute(CommandSender sender) { public void execute(CommandSender sender) {
Player player = (Player) sender; main.getProfiler().start();
TerraWorld world = main.getWorld(player.getWorld()); sender.sendMessage("Profiling enabled.");
world.getProfiler().setProfiling(true);
player.sendMessage("Profiling enabled.");
} }
} }

View File

@ -4,16 +4,10 @@ import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.command.CommandTemplate; import com.dfsek.terra.api.command.CommandTemplate;
import com.dfsek.terra.api.command.annotation.Command; import com.dfsek.terra.api.command.annotation.Command;
import com.dfsek.terra.api.command.annotation.type.DebugCommand; import com.dfsek.terra.api.command.annotation.type.DebugCommand;
import com.dfsek.terra.api.command.annotation.type.PlayerCommand;
import com.dfsek.terra.api.command.annotation.type.WorldCommand;
import com.dfsek.terra.api.injection.annotations.Inject; import com.dfsek.terra.api.injection.annotations.Inject;
import com.dfsek.terra.api.platform.CommandSender; import com.dfsek.terra.api.platform.CommandSender;
import com.dfsek.terra.api.platform.entity.Player;
import com.dfsek.terra.world.TerraWorld;
@Command @Command
@WorldCommand
@PlayerCommand
@DebugCommand @DebugCommand
public class ProfileStopCommand implements CommandTemplate { public class ProfileStopCommand implements CommandTemplate {
@Inject @Inject
@ -21,9 +15,7 @@ public class ProfileStopCommand implements CommandTemplate {
@Override @Override
public void execute(CommandSender sender) { public void execute(CommandSender sender) {
Player player = (Player) sender; main.getProfiler().stop();
TerraWorld world = main.getWorld(player.getWorld()); sender.sendMessage("Profiling disabled.");
world.getProfiler().setProfiling(false);
player.sendMessage("Profiling disabled.");
} }
} }

View File

@ -18,7 +18,7 @@ public class OreHolderLoader implements TypeLoader<OreHolder> {
Map<String, Object> map = (Map<String, Object>) o; Map<String, Object> map = (Map<String, Object>) o;
for(Map.Entry<String, Object> entry : map.entrySet()) { for(Map.Entry<String, Object> entry : map.entrySet()) {
holder.add(configLoader.loadClass(Ore.class, entry.getKey()), (OreConfig) configLoader.loadType(OreConfig.class, entry.getValue())); holder.add(configLoader.loadClass(Ore.class, entry.getKey()), configLoader.loadClass(OreConfig.class, entry.getValue()), entry.getKey());
} }
return holder; return holder;

View File

@ -1,36 +0,0 @@
package com.dfsek.terra.profiler;
/**
* Class to hold a profiler data value. Contains formatting method to highlight value based on desired range.
*/
public class DataHolder {
private final long desired;
private final DataType type;
private final double desiredRangePercent;
/**
* Constructs a DataHolder with a DataType and a desired value, including a percentage around the desired value considered acceptable
*
* @param type The type of data held in this instance.
* @param desired The desired value. This should be the average value of whatever is being measured.
* @param desiredRangePercent The percentage around the desired value to be considered acceptable.
*/
public DataHolder(DataType type, long desired, double desiredRangePercent) {
this.desired = desired;
this.type = type;
this.desiredRangePercent = desiredRangePercent;
}
/**
* Returns a String, formatted with Bungee ChatColors.<br>
* GREEN if the value is better than desired and outside of acceptable range.<br>
* YELLOW if the value is better or worse than desired, and within acceptable range.<br>
* RED if the value is worse than desired and outside of acceptable range.<br>
*
* @param data The data to format.
* @return String - The formatted data.
*/
public String getFormattedData(long data) {
return type.getFormatted(data);
}
}

View File

@ -1,24 +0,0 @@
package com.dfsek.terra.profiler;
import net.jafama.FastMath;
public enum DataType {
PERIOD_MILLISECONDS(Desire.LOW, 1000000, "ms"), PERIOD_NANOSECONDS(Desire.LOW, 1, "ns");
private final Desire desire;
private final long divisor;
private final String unit;
DataType(Desire d, long divisor, String unit) {
this.desire = d;
this.divisor = divisor;
this.unit = unit;
}
public String getFormatted(long value) {
return (double) FastMath.round(((double) value / divisor) * 100D) / 100D + unit;
}
public Desire getDesire() {
return desire;
}
}

View File

@ -1,10 +0,0 @@
package com.dfsek.terra.profiler;
/**
* Enum to represent the "goal" of a value, whether it is desirable for the value to be high (e.g. Frequency), or low (e.g. Period)
*/
public enum Desire {
LOW, HIGH
}

View File

@ -0,0 +1,24 @@
package com.dfsek.terra.profiler;
public class Frame {
private final String id;
private final long start;
public Frame(String id) {
this.id = id;
this.start = System.nanoTime();
}
public String getId() {
return id;
}
public long getStart() {
return start;
}
@Override
public String toString() {
return id;
}
}

View File

@ -1,87 +0,0 @@
package com.dfsek.terra.profiler;
import com.dfsek.terra.api.math.MathUtil;
import com.dfsek.terra.api.util.GlueList;
import net.jafama.FastMath;
import java.math.BigInteger;
import java.util.LinkedList;
import java.util.List;
/**
* Class to record and hold all data for a single type of measurement performed by the profiler.
*/
public class Measurement {
private final List<Long> measurements = new LinkedList<>();
private final long desirable;
private final DataType type;
private long min = Long.MAX_VALUE;
private long max = Long.MIN_VALUE;
/**
* Constructs a new Measurement with a desired value and DataType.
*
* @param desirable The desired value of the measurement.
* @param type The type of data the measurement is holding.
*/
public Measurement(long desirable, DataType type) {
this.desirable = desirable;
this.type = type;
}
public void record(long value) {
max = FastMath.max(value, max);
min = FastMath.min(value, min);
measurements.add(value);
}
public int size() {
return measurements.size();
}
public ProfileFuture beginMeasurement() {
ProfileFuture future = new ProfileFuture();
long current = System.nanoTime();
future.thenRun(() -> record(System.nanoTime() - current));
return future;
}
public void reset() {
min = Long.MAX_VALUE;
max = Long.MIN_VALUE;
measurements.clear();
}
public DataHolder getDataHolder() {
return new DataHolder(type, desirable, 0.25);
}
public long getMin() {
if(min == Long.MAX_VALUE) return 0;
return min;
}
public long getMax() {
if(max == Long.MIN_VALUE) return 0;
return max;
}
public long average() {
BigInteger running = BigInteger.valueOf(0);
List<Long> mTemp = new GlueList<>(measurements);
for(Long l : mTemp) {
running = running.add(BigInteger.valueOf(l));
}
if(measurements.size() == 0) return 0;
return running.divide(BigInteger.valueOf(measurements.size())).longValue();
}
public double getStdDev() {
return MathUtil.standardDeviation(new GlueList<>(measurements));
}
public int entries() {
return measurements.size();
}
}

View File

@ -0,0 +1,14 @@
package com.dfsek.terra.profiler;
public class ProfileFrame implements AutoCloseable {
private final Runnable action;
public ProfileFrame(Runnable action) {
this.action = action;
}
@Override
public void close() {
action.run();
}
}

View File

@ -1,18 +0,0 @@
package com.dfsek.terra.profiler;
import java.util.concurrent.CompletableFuture;
public class ProfileFuture extends CompletableFuture<Boolean> implements AutoCloseable {
public ProfileFuture() {
super();
}
public boolean complete() {
return super.complete(true);
}
@Override
public void close() {
this.complete();
}
}

View File

@ -0,0 +1,56 @@
package com.dfsek.terra.profiler;
import java.util.Map;
public interface Profiler {
/**
* Push a frame to this profiler.
*
* @param frame ID of frame.
*/
void push(String frame);
/**
* Pop a frame from this profiler.
*
* @param frame ID of frame. Must match ID
* at the top of the profiler stack.
*/
void pop(String frame);
/**
* Start profiling.
*/
void start();
/**
* Stop profiling.
*/
void stop();
/**
* Get the profiler data.
*
* @return Profiler data.
*/
Map<String, Timings> getTimings();
/**
* Return a {@link AutoCloseable} implementation that
* may be used in a try-with-resources statement for
* more intuitive profiling, with auto-push/pop.
*
* @param frame ID of frame.
* @return {@link AutoCloseable} implementation for use
* in try-with-resources.
*/
default ProfileFrame profile(String frame) {
push(frame);
return new ProfileFrame(() -> pop(frame));
}
/**
* Clear the profiler data.
*/
void reset();
}

View File

@ -0,0 +1,91 @@
package com.dfsek.terra.profiler;
import com.dfsek.terra.api.util.mutable.MutableInteger;
import com.dfsek.terra.profiler.exception.MalformedStackException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
public class ProfilerImpl implements Profiler {
private static final ThreadLocal<Stack<Frame>> THREAD_STACK = ThreadLocal.withInitial(Stack::new);
private static final ThreadLocal<Map<String, List<Long>>> TIMINGS = ThreadLocal.withInitial(HashMap::new);
private final List<Map<String, List<Long>>> accessibleThreadMaps = new ArrayList<>();
private volatile boolean running = false;
private static boolean instantiated = false;
private static final ThreadLocal<Boolean> SAFE = ThreadLocal.withInitial(() -> false);
private static final ThreadLocal<MutableInteger> STACK_SIZE = ThreadLocal.withInitial(() -> new MutableInteger(0));
public ProfilerImpl() {
if(instantiated) throw new IllegalStateException("Only one instance of Profiler may exist!");
instantiated = true;
}
@Override
public void push(String frame) {
STACK_SIZE.get().increment();
if(running && SAFE.get()) {
Stack<Frame> stack = THREAD_STACK.get();
stack.push(new Frame(stack.isEmpty() ? frame : stack.peek().getId() + "." + frame));
} else SAFE.set(false);
}
@Override
public void pop(String frame) {
MutableInteger size = STACK_SIZE.get();
size.decrement();
if(running && SAFE.get()) {
long time = System.nanoTime();
Stack<Frame> stack = THREAD_STACK.get();
Map<String, List<Long>> timingsMap = TIMINGS.get();
if(timingsMap.size() == 0) {
synchronized(accessibleThreadMaps) {
accessibleThreadMaps.add(timingsMap);
}
}
Frame top = stack.pop();
if((stack.size() != 0 && !top.getId().endsWith("." + frame)) || (stack.size() == 0 && !top.getId().equals(frame)))
throw new MalformedStackException("Expected " + frame + ", found " + top);
List<Long> timings = timingsMap.computeIfAbsent(top.getId(), id -> new ArrayList<>());
timings.add(time - top.getStart());
}
if(size.get() == 0) SAFE.set(true);
}
@Override
public void start() {
running = true;
}
@Override
public void stop() {
running = false;
}
@Override
public Map<String, Timings> getTimings() {
Map<String, Timings> map = new HashMap<>();
accessibleThreadMaps.forEach(smap -> smap.forEach((key, list) -> {
String[] keys = key.split("\\.");
Timings timings = map.computeIfAbsent(keys[0], id -> new Timings());
for(int i = 1; i < keys.length; i++) {
timings = timings.getSubItem(keys[i]);
}
list.forEach(timings::addTime);
}));
return map;
}
@Override
public void reset() {
accessibleThreadMaps.forEach(Map::clear);
}
}

View File

@ -0,0 +1,73 @@
package com.dfsek.terra.profiler;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Timings {
private final Map<String, Timings> subItems = new HashMap<>();
private final List<Long> timings = new ArrayList<>();
public void addTime(long time) {
timings.add(time);
}
public List<Long> getTimings() {
return timings;
}
public double average() {
return (double) timings.stream().reduce(0L, Long::sum) / timings.size();
}
public long max() {
return timings.stream().mapToLong(Long::longValue).max().orElse(0L);
}
public long min() {
return timings.stream().mapToLong(Long::longValue).min().orElse(0L);
}
public double sum() {
return timings.stream().mapToDouble(Long::doubleValue).sum();
}
public Timings getSubItem(String id) {
return subItems.computeIfAbsent(id, s -> new Timings());
}
public String toString(int indent, Timings parent, Set<Integer> branches) {
StringBuilder builder = new StringBuilder();
builder.append((double) min() / 1000000).append("ms min / ").append(average() / 1000000).append("ms avg / ")
.append((double) max() / 1000000).append("ms max (").append(timings.size()).append(" samples, ")
.append((sum() / parent.sum()) * 100).append("% of parent)");
List<String> frames = new ArrayList<>();
Set<Integer> newBranches = new HashSet<>(branches);
newBranches.add(indent);
subItems.forEach((id, timings) -> frames.add(id + ": " + timings.toString(indent + 1, this, newBranches)));
for(int i = 0; i < frames.size(); i++) {
builder.append('\n');
for(int j = 0; j < indent; j++) {
if(branches.contains(j)) builder.append("");
else builder.append(" ");
}
if(i == frames.size() - 1 && !frames.get(i).contains("\n")) builder.append("└───");
else builder.append("├───");
builder.append(frames.get(i));
}
return builder.toString();
}
@Override
public String toString() {
return toString(1, this, Collections.emptySet());
}
}

View File

@ -1,85 +0,0 @@
package com.dfsek.terra.profiler;
import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.world.TerraWorld;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import net.jafama.FastMath;
import java.util.Map;
public class WorldProfiler {
private final BiMap<String, Measurement> measures = HashBiMap.create();
private final World world;
private boolean isProfiling;
public WorldProfiler(World w) {
if(!TerraWorld.isTerraWorld(w))
throw new IllegalArgumentException("Attempted to instantiate profiler on non-Terra managed world!");
this.addMeasurement(new Measurement(2500000, DataType.PERIOD_MILLISECONDS), "TotalChunkGenTime")
.addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "FloraTime")
.addMeasurement(new Measurement(10000000, DataType.PERIOD_MILLISECONDS), "TreeTime")
.addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "OreTime")
.addMeasurement(new Measurement(5000000, DataType.PERIOD_MILLISECONDS), "CaveTime")
.addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "StructureTime");
isProfiling = false;
this.world = w;
}
public String getResultsFormatted() {
if(! isProfiling) return "Profiler is not currently running.";
StringBuilder result = new StringBuilder("Gaea World Profiler Results (Min / Avg / Max / Std Dev): \n");
for(Map.Entry<String, Measurement> e : measures.entrySet()) {
result
.append(e.getKey())
.append(": ")
.append(e.getValue().getDataHolder().getFormattedData(e.getValue().getMin()))
.append(" / ")
.append(e.getValue().getDataHolder().getFormattedData(e.getValue().average()))
.append(" / ")
.append(e.getValue().getDataHolder().getFormattedData(e.getValue().getMax()))
.append(" / ")
.append((double) FastMath.round((e.getValue().getStdDev() / 1000000) * 100D) / 100D)
.append("ms")
.append(" (x").append(e.getValue().size()).append(")\n");
}
return result.toString();
}
public void reset() {
for(Map.Entry<String, Measurement> e : measures.entrySet()) {
e.getValue().reset();
}
}
public com.dfsek.terra.profiler.WorldProfiler addMeasurement(Measurement m, String name) {
measures.put(name, m);
return this;
}
public void setMeasurement(String id, long value) {
if(isProfiling) measures.get(id).record(value);
}
public ProfileFuture measure(String id) {
if(isProfiling) return measures.get(id).beginMeasurement();
else return null;
}
public String getID(Measurement m) {
return measures.inverse().get(m);
}
public boolean isProfiling() {
return isProfiling;
}
public void setProfiling(boolean enabled) {
this.isProfiling = enabled;
}
public World getWorld() {
return world;
}
}

View File

@ -0,0 +1,17 @@
package com.dfsek.terra.profiler.exception;
public class MalformedStackException extends ProfilerException {
private static final long serialVersionUID = -3009539681021691054L;
public MalformedStackException(String message) {
super(message);
}
public MalformedStackException(String message, Throwable cause) {
super(message, cause);
}
public MalformedStackException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,17 @@
package com.dfsek.terra.profiler.exception;
public class ProfilerException extends RuntimeException {
private static final long serialVersionUID = 8206737998791649002L;
public ProfilerException(String message) {
super(message);
}
public ProfilerException(String message, Throwable cause) {
super(message, cause);
}
public ProfilerException(Throwable cause) {
super(cause);
}
}

View File

@ -13,7 +13,6 @@ import com.dfsek.terra.api.world.generation.TerraChunkGenerator;
import com.dfsek.terra.api.world.palette.Palette; import com.dfsek.terra.api.world.palette.Palette;
import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.config.pack.WorldConfig; import com.dfsek.terra.config.pack.WorldConfig;
import com.dfsek.terra.profiler.WorldProfiler;
import com.dfsek.terra.world.generation.math.samplers.Sampler; import com.dfsek.terra.world.generation.math.samplers.Sampler;
import net.jafama.FastMath; import net.jafama.FastMath;
@ -21,7 +20,6 @@ public class TerraWorld {
private final BiomeProvider provider; private final BiomeProvider provider;
private final WorldConfig config; private final WorldConfig config;
private final boolean safe; private final boolean safe;
private final WorldProfiler profiler;
private final World world; private final World world;
private final BlockData air; private final BlockData air;
@ -31,7 +29,6 @@ public class TerraWorld {
this.world = w; this.world = w;
config = c.toWorldConfig(this); config = c.toWorldConfig(this);
this.provider = config.getProvider(); this.provider = config.getProvider();
profiler = new WorldProfiler(w);
air = main.getWorldHandle().createBlockData("minecraft:air"); air = main.getWorldHandle().createBlockData("minecraft:air");
main.getEventManager().callEvent(new TerraWorldLoadEvent(this, c)); main.getEventManager().callEvent(new TerraWorldLoadEvent(this, c));
safe = true; safe = true;
@ -61,9 +58,6 @@ public class TerraWorld {
return safe; return safe;
} }
public WorldProfiler getProfiler() {
return profiler;
}
/** /**
* Get a block at an ungenerated location * Get a block at an ungenerated location

View File

@ -15,7 +15,7 @@ import com.dfsek.terra.api.world.generation.TerraChunkGenerator;
import com.dfsek.terra.api.world.palette.Palette; import com.dfsek.terra.api.world.palette.Palette;
import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.config.templates.BiomeTemplate; import com.dfsek.terra.config.templates.BiomeTemplate;
import com.dfsek.terra.profiler.ProfileFuture; import com.dfsek.terra.profiler.ProfileFrame;
import com.dfsek.terra.world.Carver; import com.dfsek.terra.world.Carver;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
import com.dfsek.terra.world.carving.NoiseCarver; import com.dfsek.terra.world.carving.NoiseCarver;
@ -94,7 +94,7 @@ public class DefaultChunkGenerator2D implements TerraChunkGenerator {
public ChunkData generateChunkData(@NotNull World world, Random random, int chunkX, int chunkZ, ChunkData chunk) { public ChunkData generateChunkData(@NotNull World world, Random random, int chunkX, int chunkZ, ChunkData chunk) {
TerraWorld tw = main.getWorld(world); TerraWorld tw = main.getWorld(world);
BiomeProvider grid = tw.getBiomeProvider(); BiomeProvider grid = tw.getBiomeProvider();
try(ProfileFuture ignore = tw.getProfiler().measure("TotalChunkGenTime")) { try(ProfileFrame ignore = main.getProfiler().profile("chunk_base_2d")) {
if(!tw.isSafe()) return chunk; if(!tw.isSafe()) return chunk;
int xOrig = (chunkX << 4); int xOrig = (chunkX << 4);
int zOrig = (chunkZ << 4); int zOrig = (chunkZ << 4);

View File

@ -23,7 +23,7 @@ import com.dfsek.terra.api.world.palette.Palette;
import com.dfsek.terra.api.world.palette.SinglePalette; import com.dfsek.terra.api.world.palette.SinglePalette;
import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.config.templates.BiomeTemplate; import com.dfsek.terra.config.templates.BiomeTemplate;
import com.dfsek.terra.profiler.ProfileFuture; import com.dfsek.terra.profiler.ProfileFrame;
import com.dfsek.terra.world.Carver; import com.dfsek.terra.world.Carver;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
import com.dfsek.terra.world.carving.NoiseCarver; import com.dfsek.terra.world.carving.NoiseCarver;
@ -50,7 +50,6 @@ public class DefaultChunkGenerator3D implements TerraChunkGenerator {
private final Carver carver; private final Carver carver;
public DefaultChunkGenerator3D(ConfigPack c, TerraPlugin main) { public DefaultChunkGenerator3D(ConfigPack c, TerraPlugin main) {
this.configPack = c; this.configPack = c;
this.main = main; this.main = main;
@ -106,7 +105,7 @@ public class DefaultChunkGenerator3D implements TerraChunkGenerator {
public ChunkData generateChunkData(@NotNull World world, Random random, int chunkX, int chunkZ, ChunkData chunk) { public ChunkData generateChunkData(@NotNull World world, Random random, int chunkX, int chunkZ, ChunkData chunk) {
TerraWorld tw = main.getWorld(world); TerraWorld tw = main.getWorld(world);
BiomeProvider grid = tw.getBiomeProvider(); BiomeProvider grid = tw.getBiomeProvider();
try(ProfileFuture ignore = tw.getProfiler().measure("TotalChunkGenTime")) { try(ProfileFrame ignore = main.getProfiler().profile("chunk_base_3d")) {
if(!tw.isSafe()) return chunk; if(!tw.isSafe()) return chunk;
int xOrig = (chunkX << 4); int xOrig = (chunkX << 4);
int zOrig = (chunkZ << 4); int zOrig = (chunkZ << 4);
@ -227,17 +226,20 @@ public class DefaultChunkGenerator3D implements TerraChunkGenerator {
return false; return false;
} }
@SuppressWarnings({"try"})
static void biomes(@NotNull World world, int chunkX, int chunkZ, @NotNull BiomeGrid biome, TerraPlugin main) { static void biomes(@NotNull World world, int chunkX, int chunkZ, @NotNull BiomeGrid biome, TerraPlugin main) {
int xOrig = (chunkX << 4); try(ProfileFrame ignore = main.getProfiler().profile("biomes")) {
int zOrig = (chunkZ << 4); int xOrig = (chunkX << 4);
BiomeProvider grid = main.getWorld(world).getBiomeProvider(); int zOrig = (chunkZ << 4);
for(int x = 0; x < 4; x++) { BiomeProvider grid = main.getWorld(world).getBiomeProvider();
for(int z = 0; z < 4; z++) { for(int x = 0; x < 4; x++) {
int cx = xOrig + (x << 2); for(int z = 0; z < 4; z++) {
int cz = zOrig + (z << 2); int cx = xOrig + (x << 2);
TerraBiome b = grid.getBiome(cx, cz); int cz = zOrig + (z << 2);
TerraBiome b = grid.getBiome(cx, cz);
biome.setBiome(cx, cz, b.getVanillaBiomes().get(b.getGenerator(world).getBiomeNoise(), cx, 0, cz)); biome.setBiome(cx, cz, b.getVanillaBiomes().get(b.getGenerator(world).getBiomeNoise(), cx, 0, cz));
}
} }
} }
} }

View File

@ -14,7 +14,7 @@ import com.dfsek.terra.api.world.generation.TerraBlockPopulator;
import com.dfsek.terra.carving.UserDefinedCarver; import com.dfsek.terra.carving.UserDefinedCarver;
import com.dfsek.terra.config.pack.WorldConfig; import com.dfsek.terra.config.pack.WorldConfig;
import com.dfsek.terra.config.templates.CarverTemplate; import com.dfsek.terra.config.templates.CarverTemplate;
import com.dfsek.terra.profiler.ProfileFuture; import com.dfsek.terra.profiler.ProfileFrame;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -38,7 +38,7 @@ public class CavePopulator implements TerraBlockPopulator, Chunkified {
TerraWorld tw = main.getWorld(world); TerraWorld tw = main.getWorld(world);
WorldHandle handle = main.getWorldHandle(); WorldHandle handle = main.getWorldHandle();
BlockData AIR = handle.createBlockData("minecraft:air"); BlockData AIR = handle.createBlockData("minecraft:air");
try(ProfileFuture ignored = tw.getProfiler().measure("CaveTime")) { try(ProfileFrame ignore = main.getProfiler().profile("carving")) {
Random random = PopulationUtil.getRandom(chunk); Random random = PopulationUtil.getRandom(chunk);
if(!tw.isSafe()) return; if(!tw.isSafe()) return;
WorldConfig config = tw.getConfig(); WorldConfig config = tw.getConfig();
@ -49,38 +49,40 @@ public class CavePopulator implements TerraBlockPopulator, Chunkified {
Map<Location, BlockData> shiftCandidate = new HashMap<>(); Map<Location, BlockData> shiftCandidate = new HashMap<>();
Set<Block> updateNeeded = new HashSet<>(); Set<Block> updateNeeded = new HashSet<>();
c.carve(chunk.getX(), chunk.getZ(), world, (v, type) -> { c.carve(chunk.getX(), chunk.getZ(), world, (v, type) -> {
Block b = chunk.getBlock(v.getBlockX(), v.getBlockY(), v.getBlockZ()); try(ProfileFrame ignored = main.getProfiler().profile("carving:" + c.getConfig().getID())) {
BlockData m = b.getBlockData(); Block b = chunk.getBlock(v.getBlockX(), v.getBlockY(), v.getBlockZ());
BlockType re = m.getBlockType(); BlockData m = b.getBlockData();
switch(type) { BlockType re = m.getBlockType();
case CENTER: switch(type) {
if(template.getInner().canReplace(re)) { case CENTER:
b.setBlockData(template.getInner().get(v.getBlockY()).get(random), false); if(template.getInner().canReplace(re)) {
if(template.getUpdate().contains(re)) updateNeeded.add(b); b.setBlockData(template.getInner().get(v.getBlockY()).get(random), false);
if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m); if(template.getUpdate().contains(re)) updateNeeded.add(b);
} if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m);
break; }
case WALL: break;
if(template.getOuter().canReplace(re)) { case WALL:
b.setBlockData(template.getOuter().get(v.getBlockY()).get(random), false); if(template.getOuter().canReplace(re)) {
if(template.getUpdate().contains(re)) updateNeeded.add(b); b.setBlockData(template.getOuter().get(v.getBlockY()).get(random), false);
if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m); if(template.getUpdate().contains(re)) updateNeeded.add(b);
} if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m);
break; }
case TOP: break;
if(template.getTop().canReplace(re)) { case TOP:
b.setBlockData(template.getTop().get(v.getBlockY()).get(random), false); if(template.getTop().canReplace(re)) {
if(template.getUpdate().contains(re)) updateNeeded.add(b); b.setBlockData(template.getTop().get(v.getBlockY()).get(random), false);
if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m); if(template.getUpdate().contains(re)) updateNeeded.add(b);
} if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m);
break; }
case BOTTOM: break;
if(template.getBottom().canReplace(re)) { case BOTTOM:
b.setBlockData(template.getBottom().get(v.getBlockY()).get(random), false); if(template.getBottom().canReplace(re)) {
if(template.getUpdate().contains(re)) updateNeeded.add(b); b.setBlockData(template.getBottom().get(v.getBlockY()).get(random), false);
if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m); if(template.getUpdate().contains(re)) updateNeeded.add(b);
} if(template.getShift().containsKey(re)) shiftCandidate.put(b.getLocation(), m);
break; }
break;
}
} }
}); });
for(Map.Entry<Location, BlockData> entry : shiftCandidate.entrySet()) { for(Map.Entry<Location, BlockData> entry : shiftCandidate.entrySet()) {
@ -93,7 +95,7 @@ public class CavePopulator implements TerraBlockPopulator, Chunkified {
if(template.getShift().get(entry.getValue().getBlockType()).contains(mut.getBlock().getBlockData().getBlockType())) { if(template.getShift().get(entry.getValue().getBlockType()).contains(mut.getBlock().getBlockData().getBlockType())) {
mut.getBlock().setBlockData(shiftStorage.computeIfAbsent(entry.getValue().getBlockType(), BlockType::getDefaultData), false); mut.getBlock().setBlockData(shiftStorage.computeIfAbsent(entry.getValue().getBlockType(), BlockType::getDefaultData), false);
} }
} catch(NullPointerException ignore) { } catch(NullPointerException ignored) {
} }
} }
for(Block b : updateNeeded) { for(Block b : updateNeeded) {

View File

@ -8,7 +8,7 @@ import com.dfsek.terra.api.util.world.PopulationUtil;
import com.dfsek.terra.api.world.biome.UserDefinedBiome; import com.dfsek.terra.api.world.biome.UserDefinedBiome;
import com.dfsek.terra.api.world.biome.provider.BiomeProvider; import com.dfsek.terra.api.world.biome.provider.BiomeProvider;
import com.dfsek.terra.api.world.generation.TerraBlockPopulator; import com.dfsek.terra.api.world.generation.TerraBlockPopulator;
import com.dfsek.terra.profiler.ProfileFuture; import com.dfsek.terra.profiler.ProfileFrame;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
import com.dfsek.terra.world.population.items.flora.FloraLayer; import com.dfsek.terra.world.population.items.flora.FloraLayer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -32,7 +32,7 @@ public class FloraPopulator implements TerraBlockPopulator {
@Override @Override
public void populate(@NotNull World world, @NotNull Chunk chunk) { public void populate(@NotNull World world, @NotNull Chunk chunk) {
TerraWorld tw = main.getWorld(world); TerraWorld tw = main.getWorld(world);
try(ProfileFuture ignored = tw.getProfiler().measure("FloraTime")) { try(ProfileFrame ignore = main.getProfiler().profile("flora")) {
if(tw.getConfig().getTemplate().disableFlora()) return; if(tw.getConfig().getTemplate().disableFlora()) return;
if(!tw.isSafe()) return; if(!tw.isSafe()) return;

View File

@ -10,7 +10,7 @@ import com.dfsek.terra.api.world.biome.TerraBiome;
import com.dfsek.terra.api.world.biome.UserDefinedBiome; import com.dfsek.terra.api.world.biome.UserDefinedBiome;
import com.dfsek.terra.api.world.generation.TerraBlockPopulator; import com.dfsek.terra.api.world.generation.TerraBlockPopulator;
import com.dfsek.terra.config.templates.BiomeTemplate; import com.dfsek.terra.config.templates.BiomeTemplate;
import com.dfsek.terra.profiler.ProfileFuture; import com.dfsek.terra.profiler.ProfileFrame;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -27,7 +27,7 @@ public class OrePopulator implements TerraBlockPopulator {
@Override @Override
public void populate(@NotNull World world, @NotNull Chunk chunk) { public void populate(@NotNull World world, @NotNull Chunk chunk) {
TerraWorld tw = main.getWorld(world); TerraWorld tw = main.getWorld(world);
try(ProfileFuture ignored = tw.getProfiler().measure("OreTime")) { try(ProfileFrame ignore = main.getProfiler().profile("ore")) {
if(tw.getConfig().getTemplate().disableOres()) return; if(tw.getConfig().getTemplate().disableOres()) return;
if(!tw.isSafe()) return; if(!tw.isSafe()) return;
@ -40,11 +40,13 @@ public class OrePopulator implements TerraBlockPopulator {
BiomeTemplate config = ((UserDefinedBiome) b).getConfig(); BiomeTemplate config = ((UserDefinedBiome) b).getConfig();
int finalCx = cx; int finalCx = cx;
int finalCz = cz; int finalCz = cz;
config.getOreHolder().forEach((ore, oreConfig) -> { config.getOreHolder().forEach((id, orePair) -> {
int amount = oreConfig.getAmount().get(random); try(ProfileFrame ignored = main.getProfiler().profile("ore:" + id)) {
for(int i = 0; i < amount; i++) { int amount = orePair.getRight().getAmount().get(random);
Vector3 location = new Vector3(random.nextInt(16) + 16 * finalCx, oreConfig.getHeight().get(random), random.nextInt(16) + 16 * finalCz); for(int i = 0; i < amount; i++) {
ore.generate(location, chunk, random); Vector3 location = new Vector3(random.nextInt(16) + 16 * finalCx, orePair.getRight().getHeight().get(random), random.nextInt(16) + 16 * finalCz);
orePair.getLeft().generate(location, chunk, random);
}
} }
}); });
} }

View File

@ -11,9 +11,8 @@ import com.dfsek.terra.api.world.biome.UserDefinedBiome;
import com.dfsek.terra.api.world.biome.provider.BiomeProvider; import com.dfsek.terra.api.world.biome.provider.BiomeProvider;
import com.dfsek.terra.api.world.generation.Chunkified; import com.dfsek.terra.api.world.generation.Chunkified;
import com.dfsek.terra.api.world.generation.TerraBlockPopulator; import com.dfsek.terra.api.world.generation.TerraBlockPopulator;
import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.config.pack.WorldConfig; import com.dfsek.terra.config.pack.WorldConfig;
import com.dfsek.terra.profiler.ProfileFuture; import com.dfsek.terra.profiler.ProfileFrame;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
import com.dfsek.terra.world.population.items.TerraStructure; import com.dfsek.terra.world.population.items.TerraStructure;
import net.jafama.FastMath; import net.jafama.FastMath;
@ -32,7 +31,7 @@ public class StructurePopulator implements TerraBlockPopulator, Chunkified {
@Override @Override
public void populate(@NotNull World world, @NotNull Chunk chunk) { public void populate(@NotNull World world, @NotNull Chunk chunk) {
TerraWorld tw = main.getWorld(world); TerraWorld tw = main.getWorld(world);
try(ProfileFuture ignored = tw.getProfiler().measure("StructureTime")) { try(ProfileFrame ignore = main.getProfiler().profile("structure")) {
if(tw.getConfig().getTemplate().disableStructures()) return; if(tw.getConfig().getTemplate().disableStructures()) return;
int cx = (chunk.getX() << 4); int cx = (chunk.getX() << 4);

View File

@ -8,7 +8,7 @@ import com.dfsek.terra.api.util.world.PopulationUtil;
import com.dfsek.terra.api.world.biome.UserDefinedBiome; import com.dfsek.terra.api.world.biome.UserDefinedBiome;
import com.dfsek.terra.api.world.biome.provider.BiomeProvider; import com.dfsek.terra.api.world.biome.provider.BiomeProvider;
import com.dfsek.terra.api.world.generation.TerraBlockPopulator; import com.dfsek.terra.api.world.generation.TerraBlockPopulator;
import com.dfsek.terra.profiler.ProfileFuture; import com.dfsek.terra.profiler.ProfileFrame;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
import com.dfsek.terra.world.population.items.tree.TreeLayer; import com.dfsek.terra.world.population.items.tree.TreeLayer;
import net.jafama.FastMath; import net.jafama.FastMath;
@ -32,7 +32,7 @@ public class TreePopulator implements TerraBlockPopulator {
@SuppressWarnings("try") @SuppressWarnings("try")
public void populate(@NotNull World world, @NotNull Chunk chunk) { public void populate(@NotNull World world, @NotNull Chunk chunk) {
TerraWorld tw = main.getWorld(world); TerraWorld tw = main.getWorld(world);
try(ProfileFuture ignored = tw.getProfiler().measure("TreeTime")) { try(ProfileFrame ignore = main.getProfiler().profile("tree")) {
if(tw.getConfig().getTemplate().disableTrees()) return; if(tw.getConfig().getTemplate().disableTrees()) return;
if(!tw.isSafe()) return; if(!tw.isSafe()) return;
@ -42,8 +42,9 @@ public class TreePopulator implements TerraBlockPopulator {
for(int z = 0; z < 16; z += 2) { for(int z = 0; z < 16; z += 2) {
UserDefinedBiome biome = (UserDefinedBiome) provider.getBiome((chunk.getX() << 4) + x, (chunk.getZ() << 4) + z); UserDefinedBiome biome = (UserDefinedBiome) provider.getBiome((chunk.getX() << 4) + x, (chunk.getZ() << 4) + z);
for(TreeLayer layer : biome.getConfig().getTrees()) { for(TreeLayer layer : biome.getConfig().getTrees()) {
if(layer.getDensity() >= random.nextDouble() * 100) if(layer.getDensity() >= random.nextDouble() * 100) {
layer.place(chunk, new Vector2(offset(random, x), offset(random, z))); layer.place(chunk, new Vector2(offset(random, x), offset(random, z)));
}
} }
} }
} }

View File

@ -16,7 +16,6 @@ public abstract class Ore {
protected TerraPlugin main; protected TerraPlugin main;
public Ore(BlockData material, MaterialSet replaceable, boolean applyGravity, TerraPlugin main) { public Ore(BlockData material, MaterialSet replaceable, boolean applyGravity, TerraPlugin main) {
this.material = material; this.material = material;
this.replaceable = replaceable; this.replaceable = replaceable;
this.applyGravity = applyGravity; this.applyGravity = applyGravity;

View File

@ -1,6 +1,7 @@
package com.dfsek.terra.world.population.items.ores; package com.dfsek.terra.world.population.items.ores;
import com.dfsek.terra.api.util.GlueList; import com.dfsek.terra.api.util.GlueList;
import com.dfsek.terra.api.util.generic.pair.ImmutablePair;
import java.util.List; import java.util.List;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
@ -11,22 +12,24 @@ import java.util.function.BiConsumer;
public class OreHolder { public class OreHolder {
private final List<Entry> entries = new GlueList<>(); private final List<Entry> entries = new GlueList<>();
public void forEach(BiConsumer<Ore, OreConfig> consumer) { public void forEach(BiConsumer<String, ImmutablePair<Ore, OreConfig>> consumer) {
entries.forEach(entry -> consumer.accept(entry.getOre(), entry.getConfig())); entries.forEach(entry -> consumer.accept(entry.getId(), ImmutablePair.of(entry.getOre(), entry.getConfig())));
} }
public OreHolder add(Ore ore, OreConfig config) { public OreHolder add(Ore ore, OreConfig config, String id) {
entries.add(new Entry(ore, config)); entries.add(new Entry(ore, config, id));
return this; return this;
} }
private static final class Entry { private static final class Entry {
private final Ore ore; private final Ore ore;
private final OreConfig config; private final OreConfig config;
private final String id;
private Entry(Ore ore, OreConfig config) { private Entry(Ore ore, OreConfig config, String id) {
this.ore = ore; this.ore = ore;
this.config = config; this.config = config;
this.id = id;
} }
public OreConfig getConfig() { public OreConfig getConfig() {
@ -36,5 +39,9 @@ public class OreHolder {
public Ore getOre() { public Ore getOre() {
return ore; return ore;
} }
public String getId() {
return id;
}
} }
} }

View File

@ -40,6 +40,7 @@ import com.dfsek.terra.config.loaders.config.sampler.NoiseSamplerBuilderLoader;
import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.config.templates.AbstractableTemplate; import com.dfsek.terra.config.templates.AbstractableTemplate;
import com.dfsek.terra.config.templates.BiomeTemplate; import com.dfsek.terra.config.templates.BiomeTemplate;
import com.dfsek.terra.profiler.Profiler;
import com.dfsek.terra.registry.config.BiomeRegistry; import com.dfsek.terra.registry.config.BiomeRegistry;
import com.dfsek.terra.registry.config.NoiseRegistry; import com.dfsek.terra.registry.config.NoiseRegistry;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
@ -137,6 +138,11 @@ public class DistributionTest {
return null; return null;
} }
@Override
public Profiler getProfiler() {
return null;
}
@Override @Override
public void register(TypeRegistry registry) { public void register(TypeRegistry registry) {

View File

@ -0,0 +1,63 @@
package profiler;
import com.dfsek.terra.profiler.Profiler;
import com.dfsek.terra.profiler.ProfilerImpl;
public class ProfilerTest {
private static final Profiler PROFILER = new ProfilerImpl();
//@Test
public static void main(String... a) throws InterruptedException {
//PROFILER.start();
for(int i = 0; i < 1000; i++) {
doThing();
}
for(int i = 0; i < 100; i++) {
doThirdOtherThing();
}
for(int i = 0; i < 100; i++) {
doOtherThing();
}
PROFILER.stop();
PROFILER.push("thing");
PROFILER.push("thing2");
PROFILER.start();
PROFILER.pop("thing2");
PROFILER.pop("thing");
PROFILER.push("thing4");
PROFILER.pop("thing4");
PROFILER.getTimings().forEach((id, timings) -> {
System.out.println(id + ": " + timings.toString());
});
}
private static void doThing() throws InterruptedException {
PROFILER.push("thing");
Thread.sleep(1);
doOtherThing();
thing4();
PROFILER.pop("thing");
}
private static void doOtherThing() throws InterruptedException {
PROFILER.push("thing2");
Thread.sleep(2);
doThirdOtherThing();
thing4();
PROFILER.pop("thing2");
}
private static void doThirdOtherThing() throws InterruptedException {
PROFILER.push("thing3");
Thread.sleep(2);
PROFILER.pop("thing3");
}
private static void thing4() throws InterruptedException {
PROFILER.push("thing4");
Thread.sleep(2);
PROFILER.pop("thing4");
}
}

View File

@ -13,6 +13,7 @@ import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -20,7 +21,7 @@ import java.util.Map;
public class ParserTest { public class ParserTest {
@Test @Test
public void parse() throws IOException, ParseException { public void parse() throws IOException, ParseException {
Parser parser = new Parser(IOUtils.toString(getClass().getResourceAsStream("/test.tesf"))); Parser parser = new Parser(IOUtils.toString(getClass().getResourceAsStream("/test.tesf"), Charset.defaultCharset()));
parser.registerFunction("test", new FunctionBuilder<Test1>() { parser.registerFunction("test", new FunctionBuilder<Test1>() {
@Override @Override

View File

@ -70,8 +70,8 @@ fun installServer(dir: String) {
// Cloning test setup. // Cloning test setup.
gitClone("https://github.com/PolyhedralDev/WorldGenTestServer") gitClone("https://github.com/PolyhedralDev/WorldGenTestServer")
// Copying plugins // Copying plugins
Files.move(Paths.get("WorldGenTestServer/plugins"), Files.move(file("WorldGenTestServer/plugins").toPath(),
Paths.get("$testDir/$dir/plugins"), file("$testDir/$dir/plugins").toPath(),
StandardCopyOption.REPLACE_EXISTING) StandardCopyOption.REPLACE_EXISTING)
// Copying config // Copying config
val serverText = URL("https://raw.githubusercontent.com/PolyhedralDev/WorldGenTestServer/master/server.properties").readText() val serverText = URL("https://raw.githubusercontent.com/PolyhedralDev/WorldGenTestServer/master/server.properties").readText()

View File

@ -40,6 +40,8 @@ import com.dfsek.terra.config.PluginConfig;
import com.dfsek.terra.config.lang.LangUtil; import com.dfsek.terra.config.lang.LangUtil;
import com.dfsek.terra.config.lang.Language; import com.dfsek.terra.config.lang.Language;
import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.profiler.Profiler;
import com.dfsek.terra.profiler.ProfilerImpl;
import com.dfsek.terra.registry.master.AddonRegistry; import com.dfsek.terra.registry.master.AddonRegistry;
import com.dfsek.terra.registry.master.ConfigRegistry; import com.dfsek.terra.registry.master.ConfigRegistry;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
@ -64,6 +66,8 @@ public class TerraBukkitPlugin extends JavaPlugin implements TerraPlugin {
private final Map<World, TerraWorld> worldMap = new HashMap<>(); private final Map<World, TerraWorld> worldMap = new HashMap<>();
private final Map<String, ConfigPack> worlds = new HashMap<>(); private final Map<String, ConfigPack> worlds = new HashMap<>();
private final Profiler profiler = new ProfilerImpl();
private final ConfigRegistry registry = new ConfigRegistry(); private final ConfigRegistry registry = new ConfigRegistry();
private final CheckedRegistry<ConfigPack> checkedRegistry = new CheckedRegistry<>(registry); private final CheckedRegistry<ConfigPack> checkedRegistry = new CheckedRegistry<>(registry);
@ -141,6 +145,11 @@ public class TerraBukkitPlugin extends JavaPlugin implements TerraPlugin {
Bukkit.getScheduler().runTask(this, task); Bukkit.getScheduler().runTask(this, task);
} }
@Override
public Profiler getProfiler() {
return profiler;
}
@Override @Override
public void onDisable() { public void onDisable() {
BukkitChunkGeneratorWrapper.saveAll(); BukkitChunkGeneratorWrapper.saveAll();

View File

@ -3,19 +3,11 @@ package com.dfsek.terra.bukkit.generator;
import com.dfsek.terra.api.TerraPlugin; import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.platform.world.Chunk; import com.dfsek.terra.api.platform.world.Chunk;
import com.dfsek.terra.api.platform.world.generator.GeneratorWrapper; import com.dfsek.terra.api.platform.world.generator.GeneratorWrapper;
import com.dfsek.terra.api.world.generation.TerraBlockPopulator;
import com.dfsek.terra.api.world.generation.TerraChunkGenerator; import com.dfsek.terra.api.world.generation.TerraChunkGenerator;
import com.dfsek.terra.bukkit.population.PopulationManager; import com.dfsek.terra.bukkit.population.PopulationManager;
import com.dfsek.terra.bukkit.world.BukkitAdapter; import com.dfsek.terra.bukkit.world.BukkitAdapter;
import com.dfsek.terra.bukkit.world.BukkitBiomeGrid; import com.dfsek.terra.bukkit.world.BukkitBiomeGrid;
import com.dfsek.terra.profiler.DataType;
import com.dfsek.terra.profiler.Measurement;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
import com.dfsek.terra.world.population.CavePopulator;
import com.dfsek.terra.world.population.FloraPopulator;
import com.dfsek.terra.world.population.OrePopulator;
import com.dfsek.terra.world.population.StructurePopulator;
import com.dfsek.terra.world.population.TreePopulator;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.BlockPopulator;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
@ -24,13 +16,10 @@ import org.jetbrains.annotations.NotNull;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.stream.Collectors;
public class BukkitChunkGeneratorWrapper extends ChunkGenerator implements GeneratorWrapper { public class BukkitChunkGeneratorWrapper extends ChunkGenerator implements GeneratorWrapper {
@ -76,8 +65,6 @@ public class BukkitChunkGeneratorWrapper extends ChunkGenerator implements Gener
e.printStackTrace(); e.printStackTrace();
} }
popMap.put(w, popMan); popMap.put(w, popMan);
main.getWorld(w).getProfiler().addMeasurement(new Measurement(15000000, DataType.PERIOD_MILLISECONDS), "PopulationManagerTime");
popMan.attachProfiler(main.getWorld(w).getProfiler());
needsLoad = false; needsLoad = false;
} }

View File

@ -5,12 +5,10 @@ import com.dfsek.terra.api.platform.world.Chunk;
import com.dfsek.terra.api.platform.world.World; import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.api.util.FastRandom; import com.dfsek.terra.api.util.FastRandom;
import com.dfsek.terra.api.world.generation.Chunkified; import com.dfsek.terra.api.world.generation.Chunkified;
import com.dfsek.terra.api.world.generation.TerraBlockPopulator;
import com.dfsek.terra.api.world.generation.TerraChunkGenerator; import com.dfsek.terra.api.world.generation.TerraChunkGenerator;
import com.dfsek.terra.bukkit.TerraBukkitPlugin; import com.dfsek.terra.bukkit.TerraBukkitPlugin;
import com.dfsek.terra.bukkit.world.BukkitAdapter; import com.dfsek.terra.bukkit.world.BukkitAdapter;
import com.dfsek.terra.profiler.ProfileFuture; import com.dfsek.terra.profiler.ProfileFrame;
import com.dfsek.terra.profiler.WorldProfiler;
import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.BlockPopulator;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -26,22 +24,12 @@ public class PopulationManager extends BlockPopulator {
private final TerraChunkGenerator generator; private final TerraChunkGenerator generator;
private final HashSet<ChunkCoordinate> needsPop = new HashSet<>(); private final HashSet<ChunkCoordinate> needsPop = new HashSet<>();
private final TerraPlugin main; private final TerraPlugin main;
private WorldProfiler profiler;
public PopulationManager(TerraChunkGenerator generator, TerraPlugin main) { public PopulationManager(TerraChunkGenerator generator, TerraPlugin main) {
this.generator = generator; this.generator = generator;
this.main = main; this.main = main;
} }
private ProfileFuture measure() {
if(profiler != null) return profiler.measure("PopulationManagerTime");
return null;
}
public void attachProfiler(WorldProfiler p) {
this.profiler = p;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public synchronized void saveBlocks(World w) throws IOException { public synchronized void saveBlocks(World w) throws IOException {
File f = new File(Gaea.getGaeaFolder(w), "chunks.bin"); File f = new File(Gaea.getGaeaFolder(w), "chunks.bin");
@ -78,8 +66,9 @@ public class PopulationManager extends BlockPopulator {
} }
@Override @Override
@SuppressWarnings("try")
public void populate(org.bukkit.@NotNull World world, @NotNull Random random, org.bukkit.@NotNull Chunk source) { public void populate(org.bukkit.@NotNull World world, @NotNull Random random, org.bukkit.@NotNull Chunk source) {
try(ProfileFuture ignored = measure()) { try(ProfileFrame ignore = main.getProfiler().profile("popman")) {
Chunk chunk = BukkitAdapter.adapt(source); Chunk chunk = BukkitAdapter.adapt(source);
needsPop.add(new ChunkCoordinate(chunk)); needsPop.add(new ChunkCoordinate(chunk));
int x = chunk.getX(); int x = chunk.getX();

View File

@ -5,8 +5,10 @@ import com.dfsek.terra.api.math.vector.Location;
import com.dfsek.terra.api.platform.handle.WorldHandle; import com.dfsek.terra.api.platform.handle.WorldHandle;
import com.dfsek.terra.api.platform.world.Tree; import com.dfsek.terra.api.platform.world.Tree;
import com.dfsek.terra.api.util.collections.MaterialSet; import com.dfsek.terra.api.util.collections.MaterialSet;
import com.dfsek.terra.profiler.ProfileFrame;
import org.bukkit.TreeType; import org.bukkit.TreeType;
import java.util.Locale;
import java.util.Random; import java.util.Random;
public class BukkitTree implements Tree { public class BukkitTree implements Tree {
@ -40,8 +42,11 @@ public class BukkitTree implements Tree {
} }
@Override @Override
@SuppressWarnings("try")
public boolean plant(Location l, Random r) { public boolean plant(Location l, Random r) {
return ((BukkitWorld) l.getWorld()).getHandle().generateTree(BukkitAdapter.adapt(l), delegate); try(ProfileFrame ignore = main.getProfiler().profile("bukkit_tree:" + delegate.toString().toLowerCase(Locale.ROOT))) {
return ((BukkitWorld) l.getWorld()).getHandle().generateTree(BukkitAdapter.adapt(l), delegate);
}
} }
@Override @Override

View File

@ -4,6 +4,7 @@ import com.dfsek.terra.api.platform.block.state.SerialState;
import com.dfsek.terra.api.platform.block.state.Sign; import com.dfsek.terra.api.platform.block.state.Sign;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@SuppressWarnings("deprecation")
public class BukkitSign extends BukkitBlockState implements Sign { public class BukkitSign extends BukkitBlockState implements Sign {
protected BukkitSign(org.bukkit.block.Sign block) { protected BukkitSign(org.bukkit.block.Sign block) {
super(block); super(block);

View File

@ -46,6 +46,8 @@ import com.dfsek.terra.fabric.world.TerraBiomeSource;
import com.dfsek.terra.fabric.world.features.PopulatorFeature; import com.dfsek.terra.fabric.world.features.PopulatorFeature;
import com.dfsek.terra.fabric.world.generator.FabricChunkGenerator; import com.dfsek.terra.fabric.world.generator.FabricChunkGenerator;
import com.dfsek.terra.fabric.world.generator.FabricChunkGeneratorWrapper; import com.dfsek.terra.fabric.world.generator.FabricChunkGeneratorWrapper;
import com.dfsek.terra.profiler.Profiler;
import com.dfsek.terra.profiler.ProfilerImpl;
import com.dfsek.terra.registry.exception.DuplicateEntryException; import com.dfsek.terra.registry.exception.DuplicateEntryException;
import com.dfsek.terra.registry.master.AddonRegistry; import com.dfsek.terra.registry.master.AddonRegistry;
import com.dfsek.terra.registry.master.ConfigRegistry; import com.dfsek.terra.registry.master.ConfigRegistry;
@ -67,7 +69,6 @@ import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.biome.Biome; import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeEffects; import net.minecraft.world.biome.BiomeEffects;
import net.minecraft.world.biome.GenerationSettings; import net.minecraft.world.biome.GenerationSettings;
import net.minecraft.world.biome.SpawnSettings;
import net.minecraft.world.gen.GenerationStep; import net.minecraft.world.gen.GenerationStep;
import net.minecraft.world.gen.chunk.ChunkGenerator; import net.minecraft.world.gen.chunk.ChunkGenerator;
import net.minecraft.world.gen.chunk.ChunkGeneratorSettings; import net.minecraft.world.gen.chunk.ChunkGeneratorSettings;
@ -75,7 +76,6 @@ import net.minecraft.world.gen.decorator.Decorator;
import net.minecraft.world.gen.decorator.NopeDecoratorConfig; import net.minecraft.world.gen.decorator.NopeDecoratorConfig;
import net.minecraft.world.gen.feature.ConfiguredFeature; import net.minecraft.world.gen.feature.ConfiguredFeature;
import net.minecraft.world.gen.feature.ConfiguredFeatures; import net.minecraft.world.gen.feature.ConfiguredFeatures;
import net.minecraft.world.gen.feature.DefaultBiomeFeatures;
import net.minecraft.world.gen.feature.DefaultFeatureConfig; import net.minecraft.world.gen.feature.DefaultFeatureConfig;
import net.minecraft.world.gen.feature.FeatureConfig; import net.minecraft.world.gen.feature.FeatureConfig;
import net.minecraft.world.gen.surfacebuilder.SurfaceBuilder; import net.minecraft.world.gen.surfacebuilder.SurfaceBuilder;
@ -105,6 +105,9 @@ public class TerraFabricPlugin implements TerraPlugin, ModInitializer {
private final Map<Long, TerraWorld> worldMap = new HashMap<>(); private final Map<Long, TerraWorld> worldMap = new HashMap<>();
private final EventManager eventManager = new TerraEventManager(this); private final EventManager eventManager = new TerraEventManager(this);
private final GenericLoaders genericLoaders = new GenericLoaders(this); private final GenericLoaders genericLoaders = new GenericLoaders(this);
private final Profiler profiler = new ProfilerImpl();
private final Logger logger = new Logger() { private final Logger logger = new Logger() {
private final org.apache.logging.log4j.Logger logger = LogManager.getLogger(); private final org.apache.logging.log4j.Logger logger = LogManager.getLogger();
@ -391,6 +394,11 @@ public class TerraFabricPlugin implements TerraPlugin, ModInitializer {
return eventManager; return eventManager;
} }
@Override
public Profiler getProfiler() {
return profiler;
}
@Addon("Terra-Fabric") @Addon("Terra-Fabric")
@Author("Terra") @Author("Terra")
@Version("1.0.0") @Version("1.0.0")
@ -435,7 +443,7 @@ public class TerraFabricPlugin implements TerraPlugin, ModInitializer {
private void injectTree(CheckedRegistry<Tree> registry, String id, ConfiguredFeature<?, ?> tree) { private void injectTree(CheckedRegistry<Tree> registry, String id, ConfiguredFeature<?, ?> tree) {
try { try {
registry.add(id, new FabricTree(tree)); registry.add(id, new FabricTree(tree, id));
} catch(DuplicateEntryException ignore) { } catch(DuplicateEntryException ignore) {
} }
} }

View File

@ -6,25 +6,31 @@ import com.dfsek.terra.api.util.collections.MaterialSet;
import com.dfsek.terra.fabric.TerraFabricPlugin; import com.dfsek.terra.fabric.TerraFabricPlugin;
import com.dfsek.terra.fabric.world.generator.FabricChunkGenerator; import com.dfsek.terra.fabric.world.generator.FabricChunkGenerator;
import com.dfsek.terra.fabric.world.handles.world.FabricWorldAccess; import com.dfsek.terra.fabric.world.handles.world.FabricWorldAccess;
import com.dfsek.terra.profiler.ProfileFrame;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.StructureWorldAccess; import net.minecraft.world.StructureWorldAccess;
import net.minecraft.world.gen.chunk.ChunkGenerator; import net.minecraft.world.gen.chunk.ChunkGenerator;
import net.minecraft.world.gen.feature.ConfiguredFeature; import net.minecraft.world.gen.feature.ConfiguredFeature;
import java.util.Locale;
import java.util.Random; import java.util.Random;
public class FabricTree implements Tree { public class FabricTree implements Tree {
private final ConfiguredFeature<?, ?> delegate; private final ConfiguredFeature<?, ?> delegate;
private final String id;
public FabricTree(ConfiguredFeature<?, ?> delegate) { public FabricTree(ConfiguredFeature<?, ?> delegate, String id) {
this.delegate = delegate; this.delegate = delegate;
this.id = id;
} }
@Override @Override
public boolean plant(Location l, Random r) { public boolean plant(Location l, Random r) {
FabricWorldAccess fabricWorldAccess = ((FabricWorldAccess) l.getWorld()); try(ProfileFrame ignore = TerraFabricPlugin.getInstance().getProfiler().profile("fabric_tree:" + id.toLowerCase(Locale.ROOT))) {
ChunkGenerator generatorWrapper = ((FabricChunkGenerator) fabricWorldAccess.getGenerator()).getHandle(); FabricWorldAccess fabricWorldAccess = ((FabricWorldAccess) l.getWorld());
return delegate.generate((StructureWorldAccess) fabricWorldAccess.getHandle(), generatorWrapper, r, new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ())); ChunkGenerator generatorWrapper = ((FabricChunkGenerator) fabricWorldAccess.getGenerator()).getHandle();
return delegate.generate((StructureWorldAccess) fabricWorldAccess.getHandle(), generatorWrapper, r, new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ()));
}
} }
@Override @Override

View File

@ -21,6 +21,8 @@ import com.dfsek.terra.config.lang.Language;
import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.platform.RawBiome; import com.dfsek.terra.platform.RawBiome;
import com.dfsek.terra.platform.RawWorldHandle; import com.dfsek.terra.platform.RawWorldHandle;
import com.dfsek.terra.profiler.Profiler;
import com.dfsek.terra.profiler.ProfilerImpl;
import com.dfsek.terra.registry.master.AddonRegistry; import com.dfsek.terra.registry.master.AddonRegistry;
import com.dfsek.terra.registry.master.ConfigRegistry; import com.dfsek.terra.registry.master.ConfigRegistry;
import com.dfsek.terra.world.TerraWorld; import com.dfsek.terra.world.TerraWorld;
@ -39,6 +41,8 @@ public class StandalonePlugin implements TerraPlugin {
private final RawWorldHandle worldHandle = new RawWorldHandle(); private final RawWorldHandle worldHandle = new RawWorldHandle();
private final EventManager eventManager = new TerraEventManager(this); private final EventManager eventManager = new TerraEventManager(this);
private final Profiler profiler = new ProfilerImpl();
@Override @Override
public WorldHandle getWorldHandle() { public WorldHandle getWorldHandle() {
return worldHandle; return worldHandle;
@ -147,4 +151,9 @@ public class StandalonePlugin implements TerraPlugin {
public EventManager getEventManager() { public EventManager getEventManager() {
return eventManager; return eventManager;
} }
@Override
public Profiler getProfiler() {
return profiler;
}
} }

View File

@ -14,6 +14,7 @@ import com.dfsek.terra.api.util.logging.DebugLogger;
import com.dfsek.terra.config.PluginConfig; import com.dfsek.terra.config.PluginConfig;
import com.dfsek.terra.config.lang.Language; import com.dfsek.terra.config.lang.Language;
import com.dfsek.terra.config.pack.ConfigPack; import com.dfsek.terra.config.pack.ConfigPack;
import com.dfsek.terra.profiler.Profiler;
import com.dfsek.terra.registry.master.AddonRegistry; import com.dfsek.terra.registry.master.AddonRegistry;
import com.dfsek.terra.registry.master.ConfigRegistry; import com.dfsek.terra.registry.master.ConfigRegistry;
import com.dfsek.terra.sponge.world.SpongeWorldHandle; import com.dfsek.terra.sponge.world.SpongeWorldHandle;
@ -138,4 +139,9 @@ public class TerraSpongePlugin implements TerraPlugin {
public EventManager getEventManager() { public EventManager getEventManager() {
return eventManager; return eventManager;
} }
@Override
public Profiler getProfiler() {
return null;
}
} }