diff --git a/Spigot-Server-Patches/Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch b/Spigot-Server-Patches/Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch new file mode 100644 index 0000000000..dceaca58c0 --- /dev/null +++ b/Spigot-Server-Patches/Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch @@ -0,0 +1,93 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar <aikar@aikar.co> +Date: Sat, 21 Jul 2018 08:25:40 -0400 +Subject: [PATCH] Add Debug Entities option to debug dupe uuid issues + +Add -Ddebug.entities=true to your JVM flags to gain more information + +diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java +index f6b755863..108654d63 100644 +--- a/src/main/java/net/minecraft/server/Entity.java ++++ b/src/main/java/net/minecraft/server/Entity.java +@@ -0,0 +0,0 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper + } + return bukkitEntity; + } ++ Throwable addedToWorldStack; // Paper - entity debug + // CraftBukikt end + + private static final Logger a = LogManager.getLogger(); +diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java +index b16324e1a..994d4bbb8 100644 +--- a/src/main/java/net/minecraft/server/WorldServer.java ++++ b/src/main/java/net/minecraft/server/WorldServer.java +@@ -0,0 +0,0 @@ import com.google.common.util.concurrent.ListenableFuture; + import java.io.File; + import java.util.ArrayList; + import java.util.Collection; ++import java.util.Date; + import java.util.Iterator; + import java.util.List; + import java.util.Map; +@@ -0,0 +0,0 @@ public class WorldServer extends World implements IAsyncTaskHandler { + private final List<NextTickListEntry> W = Lists.newArrayList(); + + // CraftBukkit start ++ private static final boolean DEBUG_ENTITIES = Boolean.getBoolean("debug.entities"); // Paper ++ private static Throwable getAddToWorldStackTrace(Entity entity) { ++ return new Throwable(entity + " Added to world at " + new Date()); ++ } + public final int dimension; + + // Add env and gen to constructor +@@ -0,0 +0,0 @@ public class WorldServer extends World implements IAsyncTaskHandler { + private boolean j(Entity entity) { + if (entity.dead) { + WorldServer.a.warn("Tried to add entity {} but it was marked as removed already", EntityTypes.a(entity)); // CraftBukkit // Paper ++ if (DEBUG_ENTITIES) getAddToWorldStackTrace(entity).printStackTrace(); + return false; + } else { + UUID uuid = entity.getUniqueID(); +@@ -0,0 +0,0 @@ public class WorldServer extends World implements IAsyncTaskHandler { + this.f.remove(entity1); + } else { + if (!(entity instanceof EntityHuman)) { +- WorldServer.a.error("Keeping entity {} that already exists with UUID {} - " + entity1, EntityTypes.a(entity1), uuid.toString()); // CraftBukkit // Paper ++ WorldServer.a.error("Keeping entity {} that already exists with UUID {}", entity1, uuid.toString()); // CraftBukkit // Paper + WorldServer.a.error("Deleting duplicate entity {}", entity); // Paper ++ if (DEBUG_ENTITIES) { ++ if (entity1.addedToWorldStack != null) { ++ entity1.addedToWorldStack.printStackTrace(); ++ } ++ getAddToWorldStackTrace(entity).printStackTrace(); ++ } + return false; + } + +@@ -0,0 +0,0 @@ public class WorldServer extends World implements IAsyncTaskHandler { + protected void b(Entity entity) { + super.b(entity); + this.entitiesById.a(entity.getId(), entity); +- this.entitiesByUUID.put(entity.getUniqueID(), entity); ++ // Paper start ++ if (DEBUG_ENTITIES) { ++ entity.addedToWorldStack = getAddToWorldStackTrace(entity); ++ } ++ Entity old = this.entitiesByUUID.put(entity.getUniqueID(), entity); ++ if (old != null && old.getId() != entity.getId() && old.valid) { ++ Logger logger = LogManager.getLogger(); ++ logger.error("Overwrote an existing entity " + old + " with " + entity); ++ if (DEBUG_ENTITIES) { ++ if (old.addedToWorldStack != null) { ++ old.addedToWorldStack.printStackTrace(); ++ } else { ++ logger.error("Oddly, the old entity was not added to the world in the normal way. Plugins?"); ++ } ++ entity.addedToWorldStack.printStackTrace(); ++ } ++ } ++ // Paper end + Entity[] aentity = entity.bb(); + + if (aentity != null) { +-- \ No newline at end of file diff --git a/Spigot-Server-Patches/Additional-Paper-Config-options.patch b/Spigot-Server-Patches/Additional-Paper-Config-options.patch new file mode 100644 index 0000000000..11679c2eb1 --- /dev/null +++ b/Spigot-Server-Patches/Additional-Paper-Config-options.patch @@ -0,0 +1,35 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar <aikar@aikar.co> +Date: Sat, 21 Jul 2018 14:23:31 -0400 +Subject: [PATCH] Additional Paper Config options + +Have to keep as sep patch for now until 1.13, otherwise we can't merge :/ + +diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java +index 62bce1806..5a17ce3d2 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java ++++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java +@@ -0,0 +0,0 @@ public class PaperConfig { + readConfig(PaperConfig.class, null); + } + ++ protected static void logError(String s) { ++ Bukkit.getLogger().severe(s); ++ } ++ + protected static void log(String s) { + if (verbose) { + Bukkit.getLogger().info(s); +diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +index 50416f40a..14c8edeff 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java ++++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +@@ -0,0 +0,0 @@ import org.bukkit.configuration.file.YamlConfiguration; + import org.spigotmc.SpigotWorldConfig; + + import static com.destroystokyo.paper.PaperConfig.log; ++import static com.destroystokyo.paper.PaperConfig.logError; + + public class PaperWorldConfig { + +-- \ No newline at end of file diff --git a/Spigot-Server-Patches/Duplicate-UUID-Resolve-Option.patch b/Spigot-Server-Patches/Duplicate-UUID-Resolve-Option.patch new file mode 100644 index 0000000000..56262f7bc9 --- /dev/null +++ b/Spigot-Server-Patches/Duplicate-UUID-Resolve-Option.patch @@ -0,0 +1,207 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar <aikar@aikar.co> +Date: Sat, 21 Jul 2018 14:27:34 -0400 +Subject: [PATCH] Duplicate UUID Resolve Option + +Due to a bug in https://github.com/PaperMC/Paper/commit/2e29af3df05ec0a383f48be549d1c03200756d24 +which was added all the way back in March of 2016, it was unknown (potentially not at the time) +that an entity might actually change the seed of the random object. + +At some point, EntitySquid did start setting the seed. Due to this shared random, this caused +every entity to use a Random object with a predictable seed. + +This has caused entities to potentially generate with the same UUID.... + +Over the years, servers have had entities disappear, but no sign of trouble +because CraftBukkit removed the log lines indicating that something was wrong. + +We have fixed the root issue causing duplicate UUID's, however we now have chunk +files full of entities that have the same UUID as another entity! + +When these chunks load, the 2nd entity will not be added to the world correctly. + +If that chunk loads in a different order in the future, then it will reverse and the +missing one is now the one added to the world and not the other. This results in very +inconsistent entity behavior. + +This change allows you to recover any duplicate entity by generating a new UUID for it. +This also lets you delete them instead if you don't want to risk having new entities added to +the world that you previously did not see. + +But for those who are ok with leaving this inconsistent behavior, you may use WARN or NOTHING options. + +It is recommended you regenerate the entities, as these were legit entities, and deserve your love. + +diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +index 14c8edeff..e3f6557e1 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java ++++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +@@ -0,0 +0,0 @@ public class PaperWorldConfig { + log("Bed Search Radius: " + bedSearchRadius); + } + } ++ ++ public enum DuplicateUUIDMode { ++ REGEN, DELETE, NOTHING, WARN ++ } ++ public DuplicateUUIDMode duplicateUUIDMode = DuplicateUUIDMode.REGEN; ++ public void repairDuplicateUUID() { ++ String desiredMode = getString("duplicate-uuid-resolver", "regenerate").toLowerCase().trim(); ++ switch (desiredMode.toLowerCase()) { ++ case "regen": ++ case "regenerate": ++ duplicateUUIDMode = DuplicateUUIDMode.REGEN; ++ log("Duplicate UUID Resolve: Regenerate New UUID"); ++ break; ++ case "remove": ++ case "delete": ++ duplicateUUIDMode = DuplicateUUIDMode.DELETE; ++ log("Duplicate UUID Resolve: Delete Entity"); ++ break; ++ case "silent": ++ case "nothing": ++ duplicateUUIDMode = DuplicateUUIDMode.NOTHING; ++ logError("Duplicate UUID Resolve: Do Nothing (no logs) - Warning, may lose indication of bad things happening"); ++ logError("PaperMC Strongly discourages use of this setting! Triggering these messages means SOMETHING IS WRONG!"); ++ break; ++ case "log": ++ case "warn": ++ duplicateUUIDMode = DuplicateUUIDMode.WARN; ++ log("Duplicate UUID Resolve: Warn (do nothing but log it happened, may be spammy)"); ++ break; ++ default: ++ duplicateUUIDMode = DuplicateUUIDMode.WARN; ++ logError("Warning: Invalidate duplicate-uuid-resolver config " + desiredMode + " - must be one of: regen, delete, nothing, warn"); ++ log("Duplicate UUID Resolve: Warn (do nothing but log it happened, may be spammy)"); ++ break; ++ } ++ } + } +diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java +index 04adf4e3c..ea9559583 100644 +--- a/src/main/java/net/minecraft/server/Chunk.java ++++ b/src/main/java/net/minecraft/server/Chunk.java +@@ -0,0 +0,0 @@ + package net.minecraft.server; + ++// Paper start ++import com.destroystokyo.paper.PaperWorldConfig.DuplicateUUIDMode; ++import java.util.HashMap; ++import java.util.UUID; ++// Paper end + import com.destroystokyo.paper.exception.ServerInternalException; + import com.google.common.base.Predicate; + import com.google.common.collect.Maps; +@@ -0,0 +0,0 @@ public class Chunk { + public final World world; + public final int[] heightMap; + public Long scheduledForUnload; // Paper - delay chunk unloads ++ private static final Logger logger = LogManager.getLogger(); // Paper + public final int locX; + public final int locZ; + private boolean m; +@@ -0,0 +0,0 @@ public class Chunk { + if (i != this.locX || j != this.locZ) { + Chunk.e.warn("Wrong location! ({}, {}) should be ({}, {}), {}", Integer.valueOf(i), Integer.valueOf(j), Integer.valueOf(this.locX), Integer.valueOf(this.locZ), entity); + entity.die(); ++ return; // Paper + } + + int k = MathHelper.floor(entity.locY / 16.0D); +@@ -0,0 +0,0 @@ public class Chunk { + + for (int j = 0; j < i; ++j) { + List entityslice = aentityslice[j]; // Spigot ++ // Paper start ++ DuplicateUUIDMode mode = world.paperConfig.duplicateUUIDMode; ++ if (mode == DuplicateUUIDMode.DELETE || mode == DuplicateUUIDMode.REGEN) { ++ Map<UUID, Entity> thisChunk = new HashMap<>(); ++ for (Iterator<Entity> iterator = ((List<Entity>) entityslice).iterator(); iterator.hasNext(); ) { ++ Entity entity = iterator.next(); ++ Entity other = ((WorldServer) world).entitiesByUUID.get(entity.uniqueID); ++ if (other == null) { ++ other = thisChunk.get(entity.uniqueID); ++ } ++ if (other != null) { ++ switch (mode) { ++ case REGEN: { ++ entity.setUUID(UUID.randomUUID()); ++ logger.error("Duplicate UUID found used by " + other); ++ logger.error("Regenerated a new UUID for " + entity); ++ break; ++ } ++ case DELETE: { ++ logger.error("Duplicate UUID found used by " + other); ++ logger.error("Deleting duplicate entity " + entity); ++ entity.die(); ++ iterator.remove(); ++ break; ++ } ++ } ++ } ++ thisChunk.put(entity.uniqueID, entity); ++ } ++ } ++ // Paper end + + this.world.a((Collection) entityslice); + } +diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java +index 0d3af8cb7..7188d0c99 100644 +--- a/src/main/java/net/minecraft/server/Entity.java ++++ b/src/main/java/net/minecraft/server/Entity.java +@@ -0,0 +0,0 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper + }); + } + ++ public void setUUID(UUID uuid) { a(uuid); } // Paper - OBFHELPER + public void a(UUID uuid) { + this.uniqueID = uuid; + this.ar = this.uniqueID.toString(); +diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java +index 994d4bbb8..1244baf45 100644 +--- a/src/main/java/net/minecraft/server/WorldServer.java ++++ b/src/main/java/net/minecraft/server/WorldServer.java +@@ -0,0 +0,0 @@ public class WorldServer extends World implements IAsyncTaskHandler { + private final PlayerChunkMap manager; + // private final Set<NextTickListEntry> nextTickListHash = Sets.newHashSet(); + private final HashTreeSet<NextTickListEntry> nextTickList = new HashTreeSet<NextTickListEntry>(); // CraftBukkit - HashTreeSet +- private final Map<UUID, Entity> entitiesByUUID = Maps.newHashMap(); ++ public final Map<UUID, Entity> entitiesByUUID = Maps.newHashMap(); // Paper + public boolean savingDisabled; + private boolean Q; + private int emptyTime; +@@ -0,0 +0,0 @@ public class WorldServer extends World implements IAsyncTaskHandler { + this.f.remove(entity1); + } else { + if (!(entity instanceof EntityHuman)) { +- WorldServer.a.error("Keeping entity {} that already exists with UUID {}", entity1, uuid.toString()); // CraftBukkit // Paper +- WorldServer.a.error("Deleting duplicate entity {}", entity); // Paper +- if (DEBUG_ENTITIES) { +- if (entity1.addedToWorldStack != null) { +- entity1.addedToWorldStack.printStackTrace(); ++ if (entity.world.paperConfig.duplicateUUIDMode != com.destroystokyo.paper.PaperWorldConfig.DuplicateUUIDMode.NOTHING) { ++ WorldServer.a.error("Keeping entity {} that already exists with UUID {}", entity1, uuid.toString()); // CraftBukkit // Paper ++ WorldServer.a.error("Duplicate entity {} will not be added to the world. See paper.yml duplicate-uuid-resolver and set this to either regen, delete or nothing to get rid of this message", entity); // Paper ++ if (DEBUG_ENTITIES) { ++ if (entity1.addedToWorldStack != null) { ++ entity1.addedToWorldStack.printStackTrace(); ++ } ++ getAddToWorldStackTrace(entity).printStackTrace(); + } +- getAddToWorldStackTrace(entity).printStackTrace(); + } ++ + return false; + } + +@@ -0,0 +0,0 @@ public class WorldServer extends World implements IAsyncTaskHandler { + entity.addedToWorldStack = getAddToWorldStackTrace(entity); + } + Entity old = this.entitiesByUUID.put(entity.getUniqueID(), entity); +- if (old != null && old.getId() != entity.getId() && old.valid) { ++ if (old != null && old.getId() != entity.getId() && old.valid && entity.world.paperConfig.duplicateUUIDMode != com.destroystokyo.paper.PaperWorldConfig.DuplicateUUIDMode.NOTHING) { + Logger logger = LogManager.getLogger(); + logger.error("Overwrote an existing entity " + old + " with " + entity); + if (DEBUG_ENTITIES) { +-- \ No newline at end of file diff --git a/Spigot-Server-Patches/Properly-remove-entities-on-dimension-teleport.patch b/Spigot-Server-Patches/Properly-remove-entities-on-dimension-teleport.patch index 897cba5442..0038947481 100644 --- a/Spigot-Server-Patches/Properly-remove-entities-on-dimension-teleport.patch +++ b/Spigot-Server-Patches/Properly-remove-entities-on-dimension-teleport.patch @@ -42,7 +42,7 @@ index 4ac2d39c5..d6d3ffa6f 100644 } protected void c(Entity entity) { -+ if (!entity.valid) return; // Paper - Already removed, dont fire twice - this looks like it can happen even without our changes ++ if (!this.entitiesByUUID.containsKey(entity.getUniqueID()) && !entity.valid) return; // Paper - Already removed, dont fire twice - this looks like it can happen even without our changes super.c(entity); this.entitiesById.d(entity.getId()); this.entitiesByUUID.remove(entity.getUniqueID()); diff --git a/Spigot-Server-Patches/Use-a-Shared-Random-for-Entities.patch b/Spigot-Server-Patches/Use-a-Shared-Random-for-Entities.patch index d7eb6f3516..3789709708 100644 --- a/Spigot-Server-Patches/Use-a-Shared-Random-for-Entities.patch +++ b/Spigot-Server-Patches/Use-a-Shared-Random-for-Entities.patch @@ -13,7 +13,20 @@ index e16579116..c0367df20 100644 // CraftBukkit start private static final int CURRENT_LEVEL = 2; -+ public static Random SHARED_RANDOM = new Random(); // Paper ++ // Paper start ++ public static Random SHARED_RANDOM = new Random() { ++ private boolean locked = false; ++ @Override ++ public synchronized void setSeed(long seed) { ++ if (locked) { ++ LogManager.getLogger().error("Ignoring setSeed on Entity.SHARED_RANDOM", new Throwable()); ++ } else { ++ super.setSeed(seed); ++ locked = true; ++ } ++ } ++ }; ++ // Paper end static boolean isLevelAtLeast(NBTTagCompound tag, int level) { return tag.hasKey("Bukkit.updateLevel") && tag.getInt("Bukkit.updateLevel") >= level; } diff --git a/Spigot-Server-Patches/add-more-information-to-Entity.toString.patch b/Spigot-Server-Patches/add-more-information-to-Entity.toString.patch new file mode 100644 index 0000000000..188e517b86 --- /dev/null +++ b/Spigot-Server-Patches/add-more-information-to-Entity.toString.patch @@ -0,0 +1,21 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar <aikar@aikar.co> +Date: Thu, 19 Jul 2018 01:13:28 -0400 +Subject: [PATCH] add more information to Entity.toString() + +UUID, ticks lived, valid, dead + +diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java +index 80ecdb282..99dac412f 100644 +--- a/src/main/java/net/minecraft/server/Entity.java ++++ b/src/main/java/net/minecraft/server/Entity.java +@@ -0,0 +0,0 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper + } + + public String toString() { +- return String.format("%s[\'%s\'/%d, l=\'%s\', x=%.2f, y=%.2f, z=%.2f]", new Object[] { this.getClass().getSimpleName(), this.getName(), Integer.valueOf(this.id), this.world == null ? "~NULL~" : this.world.getWorldData().getName(), Double.valueOf(this.locX), Double.valueOf(this.locY), Double.valueOf(this.locZ)}); ++ return String.format("%s[\'%s\'/%d, uuid=\'%s\', l=\'%s\', x=%.2f, y=%.2f, z=%.2f, cx=%d, cd=%d, tl=%d, v=%b, d=%b]", new Object[] { this.getClass().getSimpleName(), this.getName(), Integer.valueOf(this.id), this.uniqueID.toString(), this.world == null ? "~NULL~" : this.world.getWorldData().getName(), Double.valueOf(this.locX), Double.valueOf(this.locY), Double.valueOf(this.locZ), getChunkX(), getChunkZ(), this.ticksLived, this.valid, this.dead}); // Paper - add more information + } + + public boolean isInvulnerable(DamageSource damagesource) { +-- \ No newline at end of file diff --git a/Spigot-Server-Patches/add-uuid-to-Entity.toString.patch b/Spigot-Server-Patches/add-uuid-to-Entity.toString.patch deleted file mode 100644 index 67c0f76b66..0000000000 --- a/Spigot-Server-Patches/add-uuid-to-Entity.toString.patch +++ /dev/null @@ -1,20 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar <aikar@aikar.co> -Date: Thu, 19 Jul 2018 01:13:28 -0400 -Subject: [PATCH] add uuid to Entity.toString() - - -diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index ea42800ae..93c34a260 100644 ---- a/src/main/java/net/minecraft/server/Entity.java -+++ b/src/main/java/net/minecraft/server/Entity.java -@@ -0,0 +0,0 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke - } - - public String toString() { -- return String.format(Locale.ROOT, "%s[\'%s\'/%d, l=\'%s\', x=%.2f, y=%.2f, z=%.2f]", new Object[] { this.getClass().getSimpleName(), this.getDisplayName().getText(), Integer.valueOf(this.id), this.world == null ? "~NULL~" : this.world.getWorldData().getName(), Double.valueOf(this.locX), Double.valueOf(this.locY), Double.valueOf(this.locZ)}); -+ return String.format(Locale.ROOT, "%s[\'%s\'/%d, uuid=\'%s\', l=\'%s\', x=%.2f, y=%.2f, z=%.2f]", new Object[] { this.getClass().getSimpleName(), this.getDisplayName().getText(), Integer.valueOf(this.id), this.uniqueID.toString(), this.world == null ? "~NULL~" : this.world.getWorldData().getName(), Double.valueOf(this.locX), Double.valueOf(this.locY), Double.valueOf(this.locZ)}); // Paper - add uuid - } - - public boolean isInvulnerable(DamageSource damagesource) { --- \ No newline at end of file diff --git a/paper b/paper index 24bc958b55..f80040c8f6 100755 --- a/paper +++ b/paper @@ -99,7 +99,7 @@ case "$1" in "e" | "edit") case "$2" in "s" | "server") - export LAST_EDIT="$basedir/Paper-Server" + export PAPER_LAST_EDIT="$basedir/Paper-Server" cd "$basedir/Paper-Server" ( set -e @@ -110,7 +110,7 @@ case "$1" in ) ;; "a" | "api") - export LAST_EDIT="$basedir/Paper-API" + export PAPER_LAST_EDIT="$basedir/Paper-API" cd "$basedir/Paper-API" ( set -e @@ -121,8 +121,8 @@ case "$1" in ) ;; "c" | "continue") - cd "$LAST_EDIT" - unset LAST_EDIT + cd "$PAPER_LAST_EDIT" + unset PAPER_LAST_EDIT ( set -e diff --git a/scripts/decompile.sh b/scripts/decompile.sh index f2c1754fdd..2018259c37 100755 --- a/scripts/decompile.sh +++ b/scripts/decompile.sh @@ -6,9 +6,9 @@ PS1="$" basedir="$(cd "$1" && pwd -P)" workdir="$basedir/work" minecraftversion=$(cat "$workdir/BuildData/info.json" | grep minecraftVersion | cut -d '"' -f 4) +windows="$([[ "$OSTYPE" == "cygwin" || "$OSTYPE" == "msys" ]] && echo "true" || echo "false")" decompiledir="$workdir/Minecraft/$minecraftversion" classdir="$decompiledir/classes" - echo "Extracting NMS classes..." if [ ! -d "$classdir" ]; then mkdir -p "$classdir" @@ -30,4 +30,13 @@ if [ ! -d "$decompiledir/net/minecraft/server" ]; then exit 1 fi fi + +# set a symlink to current +currentlink="$workdir/Minecraft/current" +if ([ ! -e "$currentlink" ] || [ -L "$currentlink" ]) && [ "$windows" == "false" ]; then + echo "Pointing $currentlink to $minecraftversion" + rm -rf "$currentlink" + ln -sfn "$minecraftversion" "$currentlink" +fi + )