first commit
This commit is contained in:
commit
07047c6ee4
53 files changed
+6267
No files matched your search
@@ -0,0 +1,206 @@
|
||||
package de.lucas.litematica.placement;
|
||||
|
||||
import de.lucas.litematica.schematic.SchematicFiles;
|
||||
import de.lucas.litematica.schematic.SchematicManager;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* All placements of the current world/server. Main thread only. Placements are persisted per world and restored
|
||||
* on the next login, see {@link PlacementStorage}.
|
||||
*/
|
||||
public final class PlacementManager {
|
||||
private static final PlacementManager INSTANCE = new PlacementManager();
|
||||
private static final int SAVE_DELAY_TICKS = 40;
|
||||
|
||||
private final List<SchematicPlacement> placements = new ArrayList<>();
|
||||
@Nullable
|
||||
private UUID selected;
|
||||
@Nullable
|
||||
private String worldKey;
|
||||
private int saveCountdown = -1;
|
||||
|
||||
public static PlacementManager get() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public List<SchematicPlacement> all() {
|
||||
return Collections.unmodifiableList(this.placements);
|
||||
}
|
||||
|
||||
/** Placements in the player's current dimension. */
|
||||
public List<SchematicPlacement> inCurrentDimension() {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc.level == null) {
|
||||
return List.of();
|
||||
}
|
||||
ResourceLocation dim = mc.level.dimension().location();
|
||||
return this.placements.stream().filter(p -> p.dimension().equals(dim)).toList();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public SchematicPlacement selected() {
|
||||
for (SchematicPlacement p : this.placements) {
|
||||
if (p.id().equals(this.selected)) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void select(@Nullable SchematicPlacement placement) {
|
||||
this.selected = placement == null ? null : placement.id();
|
||||
this.markDirty();
|
||||
}
|
||||
|
||||
/** Selects the next placement in the current dimension, wrapping around. */
|
||||
public void selectNext() {
|
||||
List<SchematicPlacement> list = this.inCurrentDimension();
|
||||
if (list.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
int index = list.indexOf(this.selected());
|
||||
this.select(list.get((index + 1) % list.size()));
|
||||
}
|
||||
|
||||
/** Places a schematic file with its minimum corner at {@code origin}. The file is loaded in the background. */
|
||||
public SchematicPlacement place(Path file, BlockPos origin) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
ResourceLocation dim = mc.level != null ? mc.level.dimension().location() : ResourceLocation.withDefaultNamespace("overworld");
|
||||
SchematicPlacement placement = new SchematicPlacement(UUID.randomUUID(), SchematicFiles.relativeName(file), dim,
|
||||
SchematicFiles.baseName(file), origin.immutable(), origin.immutable(), PlacementTransform.IDENTITY, true);
|
||||
this.placements.add(placement);
|
||||
this.selected = placement.id();
|
||||
this.loadSchematic(placement, true);
|
||||
this.markDirty();
|
||||
return placement;
|
||||
}
|
||||
|
||||
public void remove(SchematicPlacement placement) {
|
||||
this.placements.remove(placement);
|
||||
if (placement.id().equals(this.selected)) {
|
||||
this.selected = this.placements.isEmpty() ? null : this.placements.getLast().id();
|
||||
}
|
||||
this.markDirty();
|
||||
}
|
||||
|
||||
public void move(SchematicPlacement placement, int dx, int dy, int dz) {
|
||||
this.setOrigin(placement, placement.origin().offset(dx, dy, dz));
|
||||
}
|
||||
|
||||
public void setOrigin(SchematicPlacement placement, BlockPos origin) {
|
||||
placement.setOrigin(origin);
|
||||
this.markDirty();
|
||||
}
|
||||
|
||||
public void resetOrigin(SchematicPlacement placement) {
|
||||
this.setOrigin(placement, placement.originalOrigin());
|
||||
}
|
||||
|
||||
/** Makes the current position the one {@link #resetOrigin} returns to. */
|
||||
public void storeOriginAsOriginal(SchematicPlacement placement) {
|
||||
placement.setOriginalOrigin(placement.origin());
|
||||
this.markDirty();
|
||||
}
|
||||
|
||||
public void setTransform(SchematicPlacement placement, PlacementTransform transform) {
|
||||
placement.setTransform(transform);
|
||||
this.markDirty();
|
||||
}
|
||||
|
||||
public void setEnabled(SchematicPlacement placement, boolean enabled) {
|
||||
placement.setEnabled(enabled);
|
||||
this.markDirty();
|
||||
}
|
||||
|
||||
public void rename(SchematicPlacement placement, String name) {
|
||||
if (!name.isBlank()) {
|
||||
placement.setName(name.trim());
|
||||
this.markDirty();
|
||||
}
|
||||
}
|
||||
|
||||
/** Reloads all placements that use the given file, e.g. after it was replaced on disk. */
|
||||
public void reloadFile(Path file) {
|
||||
String relative = SchematicFiles.relativeName(file);
|
||||
SchematicManager.get().invalidate(file);
|
||||
for (SchematicPlacement p : this.placements) {
|
||||
if (p.schematicFile().equals(relative)) {
|
||||
this.loadSchematic(p, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
if (this.saveCountdown >= 0 && --this.saveCountdown < 0) {
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
|
||||
public void onLogin() {
|
||||
this.placements.clear();
|
||||
this.worldKey = PlacementStorage.currentWorldKey();
|
||||
PlacementStorage.Loaded loaded = PlacementStorage.load(this.worldKey);
|
||||
this.placements.addAll(loaded.placements());
|
||||
this.selected = loaded.selected();
|
||||
for (SchematicPlacement p : this.placements) {
|
||||
this.loadSchematic(p, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void onLogout() {
|
||||
this.save();
|
||||
this.placements.clear();
|
||||
this.selected = null;
|
||||
this.worldKey = null;
|
||||
}
|
||||
|
||||
private void markDirty() {
|
||||
this.saveCountdown = SAVE_DELAY_TICKS;
|
||||
}
|
||||
|
||||
private void save() {
|
||||
this.saveCountdown = -1;
|
||||
if (this.worldKey != null) {
|
||||
PlacementStorage.save(this.worldKey, this.placements, this.selected);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadSchematic(SchematicPlacement placement, boolean announce) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
Path file = SchematicFiles.resolve(placement.schematicFile());
|
||||
SchematicManager.get().load(file).whenComplete((schematic, error) -> mc.execute(() -> {
|
||||
if (!this.placements.contains(placement)) {
|
||||
return;
|
||||
}
|
||||
if (error != null) {
|
||||
String message = SchematicManager.errorMessage(error);
|
||||
placement.setSchematic(null, message);
|
||||
notify(Component.translatable("neolitematica.message.load_failed", placement.schematicFile(), message).withStyle(ChatFormatting.RED));
|
||||
} else {
|
||||
placement.setSchematic(schematic, null);
|
||||
if (announce) {
|
||||
notify(Component.translatable("neolitematica.message.placed", placement.name(),
|
||||
placement.origin().getX(), placement.origin().getY(), placement.origin().getZ()));
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private static void notify(Component message) {
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
if (mc.player != null) {
|
||||
mc.player.displayClientMessage(message, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user