Skip to content

Commit

Permalink
Merge pull request #172 from iron431/worldupdater
Browse files Browse the repository at this point in the history
Worldupdater
  • Loading branch information
lab3 authored Oct 1, 2023
2 parents 56c6e56 + 0c2f1a8 commit cd1e5b1
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import com.mojang.datafixers.DataFixer;
import com.mojang.datafixers.DataFixerBuilder;
import io.redspace.ironsspellbooks.IronsSpellbooks;
import io.redspace.ironsspellbooks.util.ByteHelper;
import io.redspace.ironsspellbooks.util.CodeTimer;
import it.unimi.dsi.fastutil.objects.Object2FloatMap;
import it.unimi.dsi.fastutil.objects.Object2FloatMaps;
import it.unimi.dsi.fastutil.objects.Object2FloatOpenCustomHashMap;
Expand All @@ -15,6 +17,7 @@
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.resources.ResourceKey;
import net.minecraft.util.Tuple;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.storage.ChunkStorage;
Expand All @@ -23,18 +26,18 @@
import net.minecraft.world.level.storage.DimensionDataStorage;
import net.minecraft.world.level.storage.LevelStorageSource;

import java.io.File;
import java.io.IOException;
import java.io.*;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class IronsWorldUpgrader {
public static int IRONS_WORLD_DATA_VERSION = 1;
final int REPORT_PROGRESS_MS = 20000;
final byte[] INHABITED_TIME_MARKER = new byte[]{0x49, 0x6E, 0x68, 0x61, 0x62, 0x69, 0x74, 0x65, 0x64, 0x54, 0x69, 0x6D, 0x65};
public static final String REGION_FOLDER = "region";
public static final String ENTITY_FOLDER = "entities";
private final LevelStorageSource.LevelStorageAccess levelStorage;
Expand Down Expand Up @@ -80,21 +83,26 @@ public void runUpgrade() {
IronsSpellbooks.LOGGER.info("IronsWorldUpgrader starting upgrade");

try {
IronsSpellbooks.LOGGER.info("IronsWorldUpgrader Attempting minecraft world backup (this can take long on large worlds)");
levelStorage.makeWorldBackup();
IronsSpellbooks.LOGGER.info("IronsWorldUpgrader Minecraft world backup complete.");
} catch (Exception exception) {
IronsSpellbooks.LOGGER.error("IronsWorldUpgrader Level Backup failed: {}", exception.getMessage());
}

IronsSpellbooks.LOGGER.info("IronsWorldUpgrader starting REGION_FOLDER");
long millis = Util.getMillis();
doWork(REGION_FOLDER, "block_entities");
doWork(REGION_FOLDER, "block_entities", true);
millis = Util.getMillis() - millis;
IronsSpellbooks.LOGGER.info("IronsWorldUpgrader finished REGION_FOLDER after {} ms. chunks updated:{} chunks skipped:{} tags fixed:{}", millis, this.converted, this.skipped, this.fixes);

IronsSpellbooks.LOGGER.info("IronsWorldUpgrader starting ENTITY_FOLDER");
millis = Util.getMillis();
doWork(ENTITY_FOLDER, null);
doWork(ENTITY_FOLDER, null, false);
millis = Util.getMillis() - millis;
IronsSpellbooks.LOGGER.info("IronsWorldUpgrader finished ENTITY_FOLDER after {} ms. chunks updated:{} chunks skipped:{} tags fixed:{}", millis, this.converted, this.skipped, this.fixes);

IronsSpellbooks.LOGGER.info("IronsWorldUpgrader starting fixDimensionStorage");
millis = Util.getMillis();
fixDimensionStorage();
millis = Util.getMillis() - millis;
Expand Down Expand Up @@ -126,23 +134,47 @@ private void fixDimensionStorage() {

if (ironsTraverser.changesMade()) {
NbtIo.writeCompressed(compoundTag, file);
IronsSpellbooks.LOGGER.debug("IronsWorldUpgrader: fixDimensionStorage updating file: {}, {}", file.getPath(), ironsTraverser.totalChanges());
}

fixes += ironsTraverser.totalChanges();
} catch (Exception exception) {
IronsSpellbooks.LOGGER.debug("IronsWorldUpgrader: fixDimensionStorage error: {}", exception.getMessage());
IronsSpellbooks.LOGGER.error("IronsWorldUpgrader FixDimensionStorage error: {}", exception.getMessage());
}
});
}
});
}

private void doWork(String regionFolder, String filterTag) {
private boolean preScanChunkUpdateNeeded(ChunkStorage chunkStorage, ChunkPos chunkPos) throws Exception {
var regionFile = chunkStorage.worker.storage.getRegionFile(chunkPos);
var dataInputStream = regionFile.getChunkDataInputStream(chunkPos);

try (dataInputStream) {
if (dataInputStream == null) {
return false;
}

int markerPos = ByteHelper.indexOf(dataInputStream, INHABITED_TIME_MARKER);

if (markerPos == -1) {
return true;
}

var inhabitedTime = dataInputStream.readLong();
return inhabitedTime != 0;

} catch (Exception ignored) {
}

return true;
}

private void doWork(String regionFolder, String filterTag, boolean preScan) {
running = true;
converted = 0;
skipped = 0;
fixes = 0;
long nextProgressReportMS = System.currentTimeMillis() + REPORT_PROGRESS_MS;
int totalChunks = 0;

ImmutableMap.Builder<ResourceKey<Level>, ListIterator<ChunkPos>> builder = ImmutableMap.builder();
Expand Down Expand Up @@ -174,23 +206,27 @@ private void doWork(String regionFolder, String filterTag) {
boolean updated = false;

try {
CompoundTag chunkDataTag = chunkstorage.read(chunkpos).join().orElse(null);
if (chunkDataTag != null) {
ListTag blockEntitiesTag;

if (filterTag != null) {
blockEntitiesTag = (ListTag) chunkDataTag.get(filterTag);
} else {
blockEntitiesTag = new ListTag();
blockEntitiesTag.add(chunkDataTag);
}
if (!preScan || preScanChunkUpdateNeeded(chunkstorage, chunkpos)) {
CompoundTag chunkDataTag = chunkstorage.read(chunkpos).join().orElse(null);

if (chunkDataTag != null && chunkDataTag.getInt("InhabitedTime") != 0) {
ListTag blockEntitiesTag;

if (filterTag != null) {
blockEntitiesTag = (ListTag) chunkDataTag.get(filterTag);
} else {
blockEntitiesTag = new ListTag();
blockEntitiesTag.add(chunkDataTag);
}

var ironsTagTraverser = new IronsTagTraverser();
ironsTagTraverser.visit(blockEntitiesTag);
if (ironsTagTraverser.changesMade()) {
chunkstorage.write(chunkpos, chunkDataTag);
this.fixes = ironsTagTraverser.totalChanges();
updated = true;
var ironsTagTraverser = new IronsTagTraverser();
ironsTagTraverser.visit(blockEntitiesTag);

if (ironsTagTraverser.changesMade()) {
chunkstorage.write(chunkpos, chunkDataTag);
this.fixes = ironsTagTraverser.totalChanges();
updated = true;
}
}
}
} catch (Exception exception) {
Expand All @@ -203,6 +239,12 @@ private void doWork(String regionFolder, String filterTag) {
++this.skipped;
}

if (System.currentTimeMillis() > nextProgressReportMS) {
nextProgressReportMS = System.currentTimeMillis() + REPORT_PROGRESS_MS;
int chunksProcessed = this.converted + this.skipped;
IronsSpellbooks.LOGGER.info("IronsWorldUpgrader {} PROGRESS: {} of {} chunks complete ({}%)", regionFolder, chunksProcessed, totalChunks, String.format("%.2f", (chunksProcessed / (float) totalChunks) * 100));
}

processedItem = true;
}
}
Expand Down
65 changes: 65 additions & 0 deletions src/main/java/io/redspace/ironsspellbooks/util/ByteHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package io.redspace.ironsspellbooks.util;

import java.io.DataInputStream;
import java.io.IOException;

public class ByteHelper {
/**
* Returns the start position of the first occurrence of the specified {@code target} within
* {@code array}, or {@code -1} if there is no such occurrence.
*
* <p>More formally, returns the lowest index {@code i} such that {@code Arrays.copyOfRange(array,
* i, i + target.length)} contains exactly the same elements as {@code target}.
*
* @param array the array to search for the sequence {@code target}
* @param target the array to search for as a sub-sequence of {@code array}
*/
public static int indexOf(byte[] array, int length, byte[] target) {
if (array == null || target == null || target.length == 0 || length == 0) {
return -1;
}

outer:
for (int i = 0; i < length - target.length + 1; i++) {
for (int j = 0; j < target.length; j++) {
if (array[i + j] != target[j]) {
continue outer;
}
}
return i;
}
return -1;
}

public static int indexOf(DataInputStream dataInputStream, byte[] target) throws IOException {
if (dataInputStream == null || target == null || target.length == 0) {
return -1;
}

int index = -1;
int data;

outer:
do {
data = dataInputStream.read();
index++;

int addlReadCount = 0;

for (int j = 0; j < target.length; j++) {
if (data != target[j]) {
index += addlReadCount;
continue outer;
} else if (j < target.length - 1) {
data = dataInputStream.read();
addlReadCount++;
if (data == -1) {
return -1;
}
}
}
return index;
} while (data != -1);
return -1;
}
}
43 changes: 43 additions & 0 deletions src/main/java/io/redspace/ironsspellbooks/util/CodeTimer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.redspace.ironsspellbooks.util;

import net.minecraft.util.Tuple;

import java.util.ArrayList;
import java.util.List;

public class CodeTimer {
private final List<Tuple<String, Long>> timing = new ArrayList<>();

public CodeTimer() {
add("START");
}

public void add(String name) {
timing.add(new Tuple<>(name, System.nanoTime()));
}

public String getOutput(String delimiter) {
StringBuilder sb = new StringBuilder();

long itemDelta = 0;
long totalDelta = 0;

for (int i = 0; i < timing.size(); i++) {
var item = timing.get(i);

if (i > 0) {
var lastItem = timing.get(i - 1);
itemDelta = item.getB() - lastItem.getB();
totalDelta += itemDelta;
sb.append(String.format("%s%s%s%s%f%s%f\n", lastItem.getA(), delimiter, item.getA(), delimiter, (itemDelta / 1000000d), delimiter, totalDelta / 1000000d));
}
}

return sb.toString();
}

@Override
public String toString() {
return getOutput("\t");
}
}
17 changes: 16 additions & 1 deletion src/main/resources/META-INF/accesstransformer.cfg
Original file line number Diff line number Diff line change
@@ -1,20 +1,35 @@
#Making Minecraft's UpgradeRecipe fields public for use in JEI integration
public net.minecraft.world.item.crafting.UpgradeRecipe f_44518_ # base
public net.minecraft.world.item.crafting.UpgradeRecipe f_44519_ # addition

#Making Minecraft's structure gen pools public for adding houses to village generation
public net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool f_210560_ # templates
public-f net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool f_210559_ # rawTemplates

#Making the dispenser behavior map public so that we can save the old entry before overriding it
public net.minecraft.world.level.block.DispenserBlock f_52661_ #DISPENSER_REGISTRY

#Making PotionBrewing's addMix public so we can register our own recipes
public net.minecraft.world.item.alchemy.PotionBrewing m_43513_(Lnet/minecraft/world/item/alchemy/Potion;Lnet/minecraft/world/item/Item;Lnet/minecraft/world/item/alchemy/Potion;)V #addMix(Potion, Item, Potion)
public net.minecraft.world.item.alchemy.PotionBrewing f_43495_

#Making the loot table of a ranomized container public for debugging
public net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity f_59605_
public net.minecraft.server.MinecraftServer f_129744_

#Making the falling block parmeters public for more control over custom falling blocks
public net.minecraft.world.entity.item.FallingBlockEntity <init>(Lnet/minecraft/world/level/Level;DDDLnet/minecraft/world/level/block/state/BlockState;)V #constructor
public net.minecraft.world.entity.item.FallingBlockEntity f_31947_ #cancelDrop
public net.minecraft.world.entity.item.FallingBlockEntity f_31946_ #blockstate

#Making item particles (eating, tool breaking, etc) public so that the scroll can use the same particle code
public net.minecraft.world.entity.LivingEntity m_21060_(Lnet/minecraft/world/item/ItemStack;I)V
public net.minecraft.world.entity.LivingEntity m_21060_(Lnet/minecraft/world/item/ItemStack;I)V

#Irons World Upgrader
public net.minecraft.world.level.chunk.storage.ChunkStorage f_63495_ #worker
public net.minecraft.world.level.chunk.storage.IOWorker f_63518_ #storage
public net.minecraft.world.level.chunk.storage.RegionFileStorage m_63711_(Lnet/minecraft/world/level/ChunkPos;)Lnet/minecraft/world/level/chunk/storage/RegionFile; #getRegionFile

# Makes public the 'makeExecutor' method in Util,
# accepting a String and returns an ExecutorService
public net.minecraft.Util m_137477_(Ljava/lang/String;)Ljava/util/concurrent/ExecutorService; #makeExecutor

0 comments on commit cd1e5b1

Please sign in to comment.