Skip to content

Commit

Permalink
Add config file to mch-viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
Alvinn8 committed Apr 7, 2024
1 parent 7e9b32d commit e433292
Show file tree
Hide file tree
Showing 8 changed files with 259 additions and 14 deletions.
3 changes: 3 additions & 0 deletions mch-viewer/fabric/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ dependencies {
implementation(project(":mch"))!!
implementation(project(":mch-fs"))!!
compileOnly("org.jetbrains:annotations:24.0.1")

include(implementation("com.electronwill.night-config:core:${property("night_config_version")}")!!)
include(implementation("com.electronwill.night-config:toml:${property("night_config_version")}")!!)
}

tasks {
Expand Down
3 changes: 2 additions & 1 deletion mch-viewer/fabric/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ loader_version=0.15.6
fabric_version=0.96.1+1.20.4

fantasy_version=0.5.0+1.20.4
adventure_version=5.11.0
adventure_version=5.11.0
night_config_version=3.6.0
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ public boolean hasNext(@NotNull CommitInfo commit) throws IOException {
if (head == null) {
throw new IllegalArgumentException("Commit not in repository (empty repository).");
}
return head.getSha1().equals(commit.hash());
return !head.getSha1().equals(commit.hash());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.event.ClickEvent;
Expand All @@ -22,6 +25,7 @@
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector4d;
Expand All @@ -31,10 +35,15 @@
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;

public class HistoryCommand {
public static final SimpleCommandExceptionType NOT_VIEWING_HISTORY = new SimpleCommandExceptionType(net.minecraft.network.chat.Component.literal(
"You are not viewing the history right now."
"You are not viewing history right now."
));
public static final DynamicCommandExceptionType NOT_A_REPO = new DynamicCommandExceptionType(repoKey -> net.minecraft.network.chat.Component.literal(
"'" + repoKey + "' was not found in the mch-viewer configuration."
));

public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
Expand All @@ -50,6 +59,23 @@ public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
return 0;
}
})
.then(
Commands.literal("view")
.then(
Commands.argument("repo", StringArgumentType.greedyString())
.suggests(HistoryCommand::suggestRepos)
.executes(ctx -> {
String repoKey = StringArgumentType.getString(ctx, "repo");
try {
return view(ctx, repoKey);
} catch (IOException e) {
ctx.getSource().sendFailure(Component.text("Failed to create history view. See the server console for an error message."));
e.printStackTrace();
return 0;
}
})
)
)
.then(
Commands.literal("log")
.executes(ctx -> {
Expand Down Expand Up @@ -160,6 +186,53 @@ public static int test(CommandContext<CommandSourceStack> ctx) throws IOExceptio
return 1;
}

private static int view(CommandContext<CommandSourceStack> ctx, String repoKey) throws CommandSyntaxException, IOException {
MchViewerFabric mchViewer = MchViewerFabric.getInstance();
RepoViewerConfig repo = mchViewer.getRepo(repoKey);
if (repo == null) {
throw NOT_A_REPO.create(repoKey);
}
MchRepository repository = repo.getRepository();
TrackedWorld trackedWorld = repo.getTrackedWorld();
MinecraftServer server = ctx.getSource().getServer();

Reference20<Commit> headCommitRef = repository.getHeadCommit();
if (headCommitRef == null) {
throw new IllegalArgumentException("Repository is empty");
}

Commit commit = headCommitRef.resolve(repository);
CommitInfo commitInfo = new CommitInfo(commit, headCommitRef.getSha1());

HistoryView view = mchViewer.view(server, repository, trackedWorld, commitInfo);
DimensionView dimensionView = view.viewDimension(Level.OVERWORLD.location());

ServerPlayer player = ctx.getSource().getPlayer();
if (player != null) {
Vector4d spawnOverride = repo.getSpawnOverride();
Vector4d spawn = spawnOverride != null ? spawnOverride : view.getSpawn();
double x = spawn.x();
double y = spawn.y();
double z = spawn.z();
float angle = (float) spawn.w();
ServerLevel level = dimensionView.getLevel();

player.teleportTo(level, x, y, z, angle, 0);
}

return 1;
}

private static CompletableFuture<Suggestions> suggestRepos(CommandContext<CommandSourceStack> ctx, SuggestionsBuilder builder) {
MchViewerFabric mchViewer = MchViewerFabric.getInstance();
for (String repoKey : mchViewer.getRepoKeys()) {
if (repoKey.toLowerCase(Locale.ROOT).startsWith(builder.getRemainingLowerCase())) {
builder.suggest(repoKey);
}
}
return builder.buildFuture();
}

public static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
public static final DateFormat DETAILED_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss zzz");

Expand All @@ -183,7 +256,9 @@ private static void displayCommits(CommandContext<CommandSourceStack> ctx, @Null
Sha1 hash = commitInfo.hash();
boolean isCurrentCommit = commitInfo.equals(current);
TextComponent.Builder row = Component.text();
row.clickEvent(ClickEvent.runCommand("/history commit " + hash.asHex()));
if (!isCurrentCommit) {
row.clickEvent(ClickEvent.runCommand("/history commit " + hash.asHex()));
}
if (isCurrentCommit) {
row.append(
Component.text()
Expand Down Expand Up @@ -219,7 +294,7 @@ private static void displayCommits(CommandContext<CommandSourceStack> ctx, @Null
}

builder.append(Component.text("====", NamedTextColor.YELLOW));
CommitInfo prev = commits[0];
CommitInfo prev = commits[commits.length - 1];
if (prev != null && cachedCommits.hasPrevious(prev)) {
builder.append(
Component.text()
Expand All @@ -232,12 +307,12 @@ private static void displayCommits(CommandContext<CommandSourceStack> ctx, @Null
builder.append(Component.text("[ < ]", NamedTextColor.GRAY));
}
builder.append(Component.text("==", NamedTextColor.YELLOW));
CommitInfo next = commits[commits.length - 1];
CommitInfo next = commits[0];
if (next != null && cachedCommits.hasNext(next)) {
builder.append(Component.text()
.content("[ > ]")
.color(NamedTextColor.GOLD)
.hoverEvent(HoverEvent.showText(Component.text("Previous page")))
.hoverEvent(HoverEvent.showText(Component.text("Next page")))
.clickEvent(ClickEvent.runCommand("/history log after " + next.hash().asHex())));
} else {
builder.append(Component.text("[ > ]", NamedTextColor.GRAY));
Expand Down Expand Up @@ -290,7 +365,7 @@ private static int logBefore(CommandContext<CommandSourceStack> ctx, Sha1 commit
CommitInfo commitInfo = new CommitInfo(commit, commitHash);

CommitInfo[] commits = new CommitInfo[11];
for (int i = commits.length - 1; i >= 0; i--) {
for (int i = 0; i < commits.length; i++) {
commitInfo = cachedCommits.previousCommit(commitInfo);
if (commitInfo == null) {
break;
Expand All @@ -311,7 +386,7 @@ private static int logAfter(CommandContext<CommandSourceStack> ctx, Sha1 commitH
CommitInfo commitInfo = new CommitInfo(commit, commitHash);

CommitInfo[] commits = new CommitInfo[11];
for (int i = 0; i < commits.length; i++) {
for (int i = commits.length - 1; i >= 0; i--) {
commitInfo = cachedCommits.nextCommit(commitInfo);
if (commitInfo == null) {
break;
Expand All @@ -333,7 +408,13 @@ private static int commit(CommandContext<CommandSourceStack> ctx, Sha1 sha1) thr

historyView.setCommit(new CommitInfo(commit, ref.getSha1()));

log(ctx);
// We need to recreate the CommandSourceStack to be able to execute /mch log again.
// Calling log(ctx) will not work since this CommandContext still has the old level
// that no longer is tied to the HistoryView instance.
Entity entity = ctx.getSource().getEntity();
if (entity != null) {
ctx.getSource().getServer().getCommands().performPrefixedCommand(entity.createCommandSourceStack(), "history log");
}

return 1;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ public void setCommit(CommitInfo commit) throws IOException {
newLevel, player.getX(), player.getY(), player.getZ(),
player.getYRot(), player.getXRot()
);
if (!player.getAbilities().flying && player.getAbilities().mayfly) {
player.getAbilities().flying = true;
player.onUpdateAbilities();
}
}

// Delete old dimension view
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,42 @@

import ca.bkaw.mch.repository.MchRepository;
import ca.bkaw.mch.repository.TrackedWorld;
import com.electronwill.nightconfig.core.Config;
import com.electronwill.nightconfig.core.file.FileConfig;
import com.mojang.logging.LogUtils;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.kyori.adventure.platform.fabric.FabricServerAudiences;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class MchViewerFabric implements ModInitializer {
public static final Logger LOGGER = LogUtils.getLogger();
public static final String NAMESPACE = "mch";

private static MchViewerFabric instance;
private volatile FabricServerAudiences adventure;

private final Map<String, RepoViewerConfig> repoViewerConfigs = new HashMap<>();
private final Map<ResourceKey<Level>, DimensionView> dimensionViews = new HashMap<>();

public static MchViewerFabric getInstance() {
return instance;
}

@Override
public void onInitialize() {
instance = this;
Expand All @@ -33,10 +48,42 @@ public void onInitialize() {

ServerLifecycleEvents.SERVER_STARTING.register(server -> this.adventure = FabricServerAudiences.of(server));
ServerLifecycleEvents.SERVER_STOPPED.register(server -> this.adventure = null);

try {
this.loadConfig();
} catch (IOException e) {
throw new RuntimeException("Failed to load config.", e);
}
}

public static MchViewerFabric getInstance() {
return instance;
private static Path getConfigDir() {
return FabricLoader.getInstance().getConfigDir().resolve("mch-viewer");
}

private void loadConfig() throws IOException {
this.repoViewerConfigs.clear();

Path configDir = getConfigDir();
Files.createDirectories(configDir);
Path configPath = configDir.resolve("config.toml");
try (FileConfig config = FileConfig.of(configPath)) {
config.load();

Config reposConfig = config.get("repos");
if (reposConfig == null) {
LOGGER.info("mch-viewer is not configured. Add a repository to config/mch-viewer/config.toml to get started!");
return;
}
for (Config.Entry entry : reposConfig.entrySet()) {
String key = entry.getKey();
Config repoConfig = entry.getValue();

RepoViewerConfig repo = RepoViewerConfig.fromConfig(repoConfig);
this.repoViewerConfigs.put(key, repo);
}

config.save();
}
}

void registerDimensionView(ResourceKey<Level> levelKey, DimensionView dimensionView) {
Expand All @@ -52,13 +99,34 @@ public HistoryView view(MinecraftServer server, MchRepository repository, Tracke
}

/**
* Get a {@link DimensionView} associated with a level key.
* Get a {@link DimensionView} associated with a level key of the level is being
* used by mch to display the history of a dimension.
*
* @param levelKey The level key.
* @return The history view.
* @return The dimension view, or null.
*/
@Nullable
public DimensionView getDimensionView(ResourceKey<Level> levelKey) {
return this.dimensionViews.get(levelKey);
}

/**
* Get the {@link RepoViewerConfig} by key.
*
* @param repoKey The key.
* @return The repo, or null.
*/
@Nullable
public RepoViewerConfig getRepo(String repoKey) {
return this.repoViewerConfigs.get(repoKey);
}

/**
* Get an unmodifiable set of repo keys.
*
* @return The keys.
*/
public Set<String> getRepoKeys() {
return Collections.unmodifiableSet(this.repoViewerConfigs.keySet());
}
}
Loading

0 comments on commit e433292

Please sign in to comment.