diff --git a/build.gradle b/build.gradle index 66332a9..d1def41 100644 --- a/build.gradle +++ b/build.gradle @@ -13,6 +13,8 @@ targetCompatibility = '1.8' compileJava.options.encoding = 'UTF-8' +if(compile_incremental == "true") + compileJava.options.incremental = true ext.commit = null ext.previousCommit = null diff --git a/buildSrc/src/main/java/org/ultramine/gradle/task/SpeicialClassTransformTask.java b/buildSrc/src/main/java/org/ultramine/gradle/task/SpeicialClassTransformTask.java index f6ab49b..4ce68df 100644 --- a/buildSrc/src/main/java/org/ultramine/gradle/task/SpeicialClassTransformTask.java +++ b/buildSrc/src/main/java/org/ultramine/gradle/task/SpeicialClassTransformTask.java @@ -4,6 +4,7 @@ import org.apache.commons.io.FileUtils; import org.gradle.api.DefaultTask; import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputDirectory; import org.gradle.api.tasks.OutputDirectory; import org.gradle.api.tasks.ParallelizableTask; import org.gradle.api.tasks.TaskAction; @@ -13,6 +14,7 @@ import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.LdcInsnNode; import org.objectweb.asm.tree.MethodNode; @@ -32,6 +34,7 @@ @ParallelizableTask public class SpeicialClassTransformTask extends DefaultTask { + @InputDirectory private File inputDir; @OutputDirectory private File outputDir = new File(getProject().getBuildDir(), getName()); @@ -116,7 +119,7 @@ if(file.isDirectory()) return; String path = getRelPath(file); - processClass(path, transformerMap.get(file)); + processClass(path, transformerMap.get(path)); } private void processClass(String path, List transfs) @@ -176,6 +179,8 @@ @Override public void transform(ClassNode node) { + replaceAnnotations(node.visibleAnnotations); + replaceAnnotations(node.invisibleAnnotations); for(Object o : node.methods) { MethodNode m = (MethodNode) o; @@ -193,6 +198,38 @@ } } + private void replaceAnnotations(List anns) + { + if(anns == null) + return; + for(AnnotationNode ann : anns) + { + if(ann.values != null) + { + for(int x = 0; x < ann.values.size() - 1; x += 2) + { + Object value = ann.values.get(x+1); + if(value instanceof String) + { + String replacement = replaceMap.get(value); + if(replacement != null) + ann.values.set(x+1, replacement); + } + else if(value instanceof String[]) + { + String[] arr = (String[]) value; + for(int j = 0; j < arr.length; j++) + { + String replacement = replaceMap.get(arr[j]); + if(replacement != null) + arr[j] = replacement; + } + } + } + } + } + } + @Override public int getWriteFlags() { @@ -211,5 +248,25 @@ { replaceMap.put(search, replacement); } + + @Override + public boolean equals(Object o) + { + if(this == o) return true; + if(o == null || getClass() != o.getClass()) return false; + + ReplaceStringTransformer that = (ReplaceStringTransformer) o; + + if(path != null ? !path.equals(that.path) : that.path != null) return false; + return replaceMap != null ? replaceMap.equals(that.replaceMap) : that.replaceMap == null; + } + + @Override + public int hashCode() + { + int result = path != null ? path.hashCode() : 0; + result = 31 * result + (replaceMap != null ? replaceMap.hashCode() : 0); + return result; + } } } diff --git a/gradle.properties b/gradle.properties index 66b5bc0..ef41885 100644 --- a/gradle.properties +++ b/gradle.properties @@ -33,3 +33,7 @@ # Publish (injecting) publish_jars= publish_url= + +# +# Build options +compile_incremental=false diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 3c7abdf..51288f9 100644 --- a/gradle/wrapper/gradle-wrapper.jar +++ b/gradle/wrapper/gradle-wrapper.jar Binary files differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index eb67457..c181797 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Apr 24 12:13:23 VLAT 2014 +#Mon Dec 26 01:51:59 MSK 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=http\://services.gradle.org/distributions/gradle-2.10-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.2.1-all.zip diff --git a/gradlew b/gradlew index 91a7e26..4453cce 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,12 +6,30 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -30,6 +48,7 @@ cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,31 +59,11 @@ MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -90,7 +89,7 @@ fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -114,6 +113,7 @@ if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` @@ -154,11 +154,19 @@ esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 8a0b282..f955316 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line diff --git a/src/main/java/net/minecraft/entity/Entity.java b/src/main/java/net/minecraft/entity/Entity.java index 6d6f3d3..be7b9f4 100644 --- a/src/main/java/net/minecraft/entity/Entity.java +++ b/src/main/java/net/minecraft/entity/Entity.java @@ -2489,7 +2489,7 @@ /* ===================================== ULTRAMINE START =====================================*/ - private final EntityType cachedEntityType = computeEntityType(); + private EntityType cachedEntityType; private GameProfile owner; public boolean removeThisTick; @@ -2525,12 +2525,18 @@ isCreatureType(EnumCreatureType.monster, false) ? EntityType.MONSTER : isCreatureType(EnumCreatureType.creature, false) ? EntityType.ANIMAL : isCreatureType(EnumCreatureType.ambient, false) ? EntityType.AMBIENT : - isCreatureType(EnumCreatureType.waterCreature, false) ? EntityType.WATER : + isCreatureType(EnumCreatureType.waterCreature, false) ? EntityType.WATER : + isCreatureType(EnumCreatureType.monster, true) ? EntityType.MONSTER : + isCreatureType(EnumCreatureType.creature, true) ? EntityType.ANIMAL : + isCreatureType(EnumCreatureType.ambient, true) ? EntityType.AMBIENT : + isCreatureType(EnumCreatureType.waterCreature, true) ? EntityType.WATER : EntityType.OTHER; } public final EntityType getEntityType() { + if(cachedEntityType == null) + return cachedEntityType = computeEntityType(); return cachedEntityType; } diff --git a/src/main/java/net/minecraft/entity/EntityCreature.java b/src/main/java/net/minecraft/entity/EntityCreature.java index 5560a9c..b9ec499 100644 --- a/src/main/java/net/minecraft/entity/EntityCreature.java +++ b/src/main/java/net/minecraft/entity/EntityCreature.java @@ -8,6 +8,7 @@ import net.minecraft.entity.passive.EntityTameable; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.pathfinding.PathEntity; +import net.minecraft.server.MinecraftServer; import net.minecraft.util.ChunkCoordinates; import net.minecraft.util.MathHelper; import net.minecraft.util.Vec3; @@ -26,6 +27,7 @@ private EntityAIBase field_110178_bs = new EntityAIMoveTowardsRestriction(this, 1.0D); private boolean field_110180_bt; private static final String __OBFID = "CL_00001558"; + private int lastPathCountedTick; public EntityCreature(World p_i1602_1_) { @@ -57,6 +59,7 @@ if (this.entityToAttack != null) { this.pathToEntity = this.worldObj.getPathEntityToEntity(this, this.entityToAttack, f4, true, false, false, true); + this.lastPathCountedTick = MinecraftServer.getServer().getTickCounter(); } } else if (this.entityToAttack.isEntityAlive()) @@ -82,7 +85,12 @@ if (!this.hasAttacked && this.entityToAttack != null && (this.pathToEntity == null || this.rand.nextInt(20) == 0)) { - this.pathToEntity = this.worldObj.getPathEntityToEntity(this, this.entityToAttack, f4, true, false, false, true); + // ultramine - fixed path recounting each tick if target is not reachable + if(MinecraftServer.getServer().getTickCounter() - lastPathCountedTick > 10) + { + this.pathToEntity = this.worldObj.getPathEntityToEntity(this, this.entityToAttack, f4, true, false, false, true); + this.lastPathCountedTick = MinecraftServer.getServer().getTickCounter(); + } } else if (!this.hasAttacked && (this.pathToEntity == null && this.rand.nextInt(180) == 0 || this.rand.nextInt(120) == 0 || this.fleeingTick > 0) && this.entityAge < 100) { diff --git a/src/main/java/net/minecraft/entity/player/EntityPlayerMP.java b/src/main/java/net/minecraft/entity/player/EntityPlayerMP.java index 5376b2d..928b7dc 100644 --- a/src/main/java/net/minecraft/entity/player/EntityPlayerMP.java +++ b/src/main/java/net/minecraft/entity/player/EntityPlayerMP.java @@ -848,6 +848,9 @@ this.lastFoodLevel = -1; this.destroyedItemsNetCache.addAll(((EntityPlayerMP)p_71049_1_).destroyedItemsNetCache); this.translator = ((EntityPlayerMP)p_71049_1_).translator; + this.renderDistance = ((EntityPlayerMP)p_71049_1_).renderDistance; + this.chatVisibility = ((EntityPlayerMP)p_71049_1_).chatVisibility; + this.chatColours = ((EntityPlayerMP)p_71049_1_).chatColours; } protected void onNewPotionEffect(PotionEffect p_70670_1_) @@ -1045,7 +1048,8 @@ { String meta = getMeta("tablistcolor"); EnumChatFormatting color = meta.isEmpty() ? null : BasicTypeParser.parseColor(meta); - return color == null ? getCommandSenderName() : color.toString() + getCommandSenderName(); + String name = color == null ? getCommandSenderName() : color.toString() + getCommandSenderName(); + return name.length() > 16 ? name.substring(0, 16) : name; } public String translate(String key) diff --git a/src/main/java/net/minecraft/nbt/CompressedStreamTools.java b/src/main/java/net/minecraft/nbt/CompressedStreamTools.java index 2cd4047..4e3fe88 100644 --- a/src/main/java/net/minecraft/nbt/CompressedStreamTools.java +++ b/src/main/java/net/minecraft/nbt/CompressedStreamTools.java @@ -185,7 +185,7 @@ public static void write(NBTTagCompound p_74795_0_, File p_74795_1_) throws IOException { - DataOutputStream dataoutputstream = new DataOutputStream(new FileOutputStream(p_74795_1_)); + DataOutputStream dataoutputstream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(p_74795_1_))); try { @@ -210,7 +210,7 @@ } else { - DataInputStream datainputstream = new DataInputStream(new FileInputStream(p_152458_0_)); + DataInputStream datainputstream = new DataInputStream(new BufferedInputStream(new FileInputStream(p_152458_0_))); NBTTagCompound nbttagcompound; try diff --git a/src/main/java/net/minecraft/world/World.java b/src/main/java/net/minecraft/world/World.java index 3868d15..306b245 100644 --- a/src/main/java/net/minecraft/world/World.java +++ b/src/main/java/net/minecraft/world/World.java @@ -1921,7 +1921,7 @@ eventProxy.popState(); this.theProfiler.endStartSection("remove"); - if(unloadedEntityList.size() != 0) + if(!unloadedEntityList.isEmpty()) this.loadedEntityList.removeAll(new HashSet(unloadedEntityList)); int j; int l; @@ -4213,8 +4213,12 @@ public Block getBlockIfExists(int x, int y, int z) { - if(blockExists(x, y, z)) - return getBlock(x, y, z); + if(x >= -MAX_BLOCK_COORD && x < MAX_BLOCK_COORD && z >= -MAX_BLOCK_COORD && z < MAX_BLOCK_COORD && y >= 0 && y < 256) + { + Chunk chunk = getChunkIfExists(x >> 4, z >> 4); + if(chunk != null) + return chunk.getBlock(x & 15, y, z & 15); + } return Blocks.air; } @@ -4264,15 +4268,42 @@ } @SuppressWarnings("unchecked") - public void forceUnloadTileEntities() + public void processTileEntityUnload() { - if (!this.field_147483_b.isEmpty()) + if (this.field_147483_b.isEmpty()) + return; + + for (Object tile : field_147483_b) + ((TileEntity)tile).onChunkUnload(); + this.loadedTileEntityList.removeAll(this.field_147483_b); + this.field_147483_b.clear(); + } + + public void processEntityUnload() + { + if(unloadedEntityList.isEmpty()) + return; + //noinspection unchecked + this.loadedEntityList.removeAll(new HashSet(unloadedEntityList)); + + for (int i = 0; i < this.unloadedEntityList.size(); i++) { - for (Object tile : field_147483_b) - ((TileEntity)tile).onChunkUnload(); - this.loadedTileEntityList.removeAll(this.field_147483_b); - this.field_147483_b.clear(); + Entity entity = (Entity)this.unloadedEntityList.get(i); + int x = entity.chunkCoordX; + int z = entity.chunkCoordZ; + + if (entity.addedToChunk && this.chunkExists(x, z)) + { + this.getChunkFromChunkCoords(x, z).removeEntity(entity); + } } + + for (int i = 0; i < this.unloadedEntityList.size(); i++) + { + this.onEntityRemoved((Entity)this.unloadedEntityList.get(i)); + } + + this.unloadedEntityList.clear(); } public WorldEventProxy getEventProxy() diff --git a/src/main/java/net/minecraft/world/WorldServer.java b/src/main/java/net/minecraft/world/WorldServer.java index eb854e4..742d4a6 100644 --- a/src/main/java/net/minecraft/world/WorldServer.java +++ b/src/main/java/net/minecraft/world/WorldServer.java @@ -13,6 +13,7 @@ import java.util.Random; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.ThreadLocalRandom; import net.minecraft.block.Block; import net.minecraft.block.BlockEventData; @@ -320,9 +321,10 @@ protected void func_147456_g() { super.func_147456_g(); - int i = 0; - int j = 0; +// int i = 0; +// int j = 0; + Random random = ThreadLocalRandom.current(); for (IntByteCursor iter = activeChunks.cursor(); iter.moveNext();) { int chunkCoord = iter.key(); @@ -348,7 +350,7 @@ int k1; int l1; - if (provider.canDoLightning(chunk) && this.rand.nextInt(100000) == 0 && this.isRaining() && this.isThundering()) + if (provider.canDoLightning(chunk) && random.nextInt(100000) == 0 && this.isRaining() && this.isThundering()) { this.updateLCG = this.updateLCG * 3 + 1013904223; i1 = this.updateLCG >> 2; @@ -364,7 +366,7 @@ this.theProfiler.endStartSection("iceandsnow"); - if (provider.canDoRainSnowIce(chunk) && this.rand.nextInt(16) == 0) + if (provider.canDoRainSnowIce(chunk) && random.nextInt(16) == 0) { getEventProxy().pushState(WorldUpdateObjectType.WEATHER); this.updateLCG = this.updateLCG * 3 + 1013904223; @@ -413,12 +415,12 @@ int j2 = i2 & 15; int k2 = i2 >> 8 & 15; int l2 = i2 >> 16 & 15; - ++j; +// ++j; Block block = extendedblockstorage.getBlockByExtId(j2, l2, k2); if (block.getTickRandomly()) { - ++i; +// ++i; getEventProxy().startBlock(block, j2 + k, l2 + extendedblockstorage.getYLocation(), k2 + l); block.updateTick(this, j2 + k, l2 + extendedblockstorage.getYLocation(), k2 + l, this.rand); } @@ -497,10 +499,12 @@ public void updateEntities() { - if (this.playerEntities.isEmpty() && getPersistentChunks().isEmpty()) + if (this.playerEntities.isEmpty() && (getPersistentChunks().isEmpty() || !getConfig().chunkLoading.enableChunkLoaders)) { if (this.updateEntityTick++ >= 1200) { + processEntityUnload(); + processTileEntityUnload(); return; } } diff --git a/src/main/java/net/minecraft/world/chunk/Chunk.java b/src/main/java/net/minecraft/world/chunk/Chunk.java index fb6c30d..01f1c50 100644 --- a/src/main/java/net/minecraft/world/chunk/Chunk.java +++ b/src/main/java/net/minecraft/world/chunk/Chunk.java @@ -6,7 +6,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -19,7 +18,6 @@ import gnu.trove.set.TByteSet; import gnu.trove.set.hash.TByteHashSet; import net.minecraft.block.Block; -import net.minecraft.block.ITileEntityProvider; import net.minecraft.block.material.Material; import net.minecraft.command.IEntitySelector; import net.minecraft.crash.CrashReport; @@ -54,6 +52,8 @@ import org.ultramine.server.chunk.ChunkHash; import org.ultramine.server.chunk.IChunkDependency; import org.ultramine.server.chunk.PendingBlockUpdate; +import org.ultramine.server.internal.LambdaHolder; +import org.ultramine.server.util.WeakObjectPool; import org.ultramine.server.event.WorldUpdateObject; import org.ultramine.server.event.WorldUpdateObjectType; @@ -1554,12 +1554,15 @@ } } - /* ======================================== ULTRAMINE START =====================================*/ - + /*======================================== ULTRAMINE START =====================================*/ + + private static final WeakObjectPool> shortObjMapPool = new WeakObjectPool<>(LambdaHolder.newShortObjMap()); + private static final WeakObjectPool> treeSetPool = new WeakObjectPool<>(LambdaHolder.newTreeSet()); + private final ShortObjMap fastTileEntityMap = HashShortObjMaps.newMutableMap(); private final TByteSet updateLightCoords = new TByteHashSet(); - private Set pendingUpdatesSet; + private ShortObjMap pendingUpdatesSet; private TreeSet pendingUpdatesQueue; private ChunkBindState bindState = ChunkBindState.NONE; @@ -1575,7 +1578,24 @@ private short entityWaterCount; private short entityItemCount; private short entityXPOrbCount; - + + private void releasePendingUpdatesSets() + { + if(pendingUpdatesQueue != null) + { + if(!pendingUpdatesSet.isEmpty()) + pendingUpdatesSet.clear(); + if(!pendingUpdatesQueue.isEmpty()) + pendingUpdatesQueue.clear(); + + shortObjMapPool.release(pendingUpdatesSet); + treeSetPool.release(pendingUpdatesQueue); + + pendingUpdatesSet = null; + pendingUpdatesQueue = null; + } + } + private void convertTileEntityMap() { fastTileEntityMap.clear(); @@ -1595,14 +1615,11 @@ PendingBlockUpdate p = pendingUpdatesQueue.first(); if(p.scheduledTime <= time) { - pendingUpdatesSet.remove(p); + pendingUpdatesSet.remove(p.getChunkCoordHash()); pendingUpdatesQueue.remove(p); - if(pendingUpdatesQueue.size() == 0) - { - pendingUpdatesSet = null; - pendingUpdatesQueue = null; - } + if(pendingUpdatesQueue.isEmpty()) + releasePendingUpdatesSets(); isModified = true; return p; @@ -1615,13 +1632,16 @@ { if(pendingUpdatesQueue == null) { - pendingUpdatesSet = new HashSet(); - pendingUpdatesQueue = new TreeSet(); + pendingUpdatesSet = shortObjMapPool.getOrCreateInstance(); + pendingUpdatesQueue = treeSetPool.getOrCreateInstance(); } - - if(!check || !pendingUpdatesSet.contains(p)) + + PendingBlockUpdate prev = null; + if(!check || (prev = pendingUpdatesSet.get(p.getChunkCoordHash())) == null || !Block.isEqualTo(p.getBlock(), prev.getBlock())) { - pendingUpdatesSet.add(p); + if(prev != null) + pendingUpdatesQueue.remove(prev); + pendingUpdatesSet.put(p.getChunkCoordHash(), p); pendingUpdatesQueue.add(p); isModified = true; } @@ -1823,5 +1843,6 @@ if(exbs != null) exbs.free(); } + releasePendingUpdatesSets(); } } diff --git a/src/main/java/net/minecraftforge/common/DimensionManager.java b/src/main/java/net/minecraftforge/common/DimensionManager.java index c40978e..7d71ad4 100644 --- a/src/main/java/net/minecraftforge/common/DimensionManager.java +++ b/src/main/java/net/minecraftforge/common/DimensionManager.java @@ -21,7 +21,6 @@ import cpw.mods.fml.common.FMLLog; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.server.MinecraftServer; -import net.minecraft.world.MinecraftException; import net.minecraft.world.World; import net.minecraft.world.WorldManager; import net.minecraft.world.WorldProvider; @@ -327,7 +326,7 @@ { w.theChunkProviderServer.unloadAll(true); w.theChunkProviderServer.setWorldUnloaded(); - w.forceUnloadTileEntities(); + w.processTileEntityUnload(); w.saveOtherData(); } else diff --git a/src/main/java/org/ultramine/server/Teleporter.java b/src/main/java/org/ultramine/server/Teleporter.java index 7dd7d3d..c5bf748 100644 --- a/src/main/java/org/ultramine/server/Teleporter.java +++ b/src/main/java/org/ultramine/server/Teleporter.java @@ -86,11 +86,17 @@ private static void doTeleportation(EntityPlayerMP player, int dimension, double x, double y, double z, float yaw, float pitch) { - player.getData().core().setLastLocation(WarpLocation.getFromPlayer(player)); - + if(player.ridingEntity != null) + { + player.addChatMessage(new ChatComponentTranslation("ultramine.teleporter.fail.riding").setChatStyle(new ChatStyle().setColor(EnumChatFormatting.RED))); + return; + } + + WarpLocation lastLocation = WarpLocation.getFromPlayer(player); if(!player.setWorldPositionAndRotation(dimension, x, y, z, yaw, pitch)) player.addChatMessage(new ChatComponentTranslation("ultramine.teleporter.fail.dim").setChatStyle(new ChatStyle().setColor(EnumChatFormatting.RED))); - + + player.getData().core().setLastLocation(lastLocation); if(isServer) { player.getData().core().setNextTeleportationTime(System.currentTimeMillis() + ConfigurationHandler.getServerConfig().settings.teleportation.cooldown*1000); @@ -100,11 +106,7 @@ public static void tick() { - for(Iterator it = teleporters.iterator();it.hasNext();) - { - if(it.next().update()) - it.remove(); - } + teleporters.removeIf(Teleporter::update); } private final EntityPlayerMP target; diff --git a/src/main/java/org/ultramine/server/chunk/PendingBlockUpdate.java b/src/main/java/org/ultramine/server/chunk/PendingBlockUpdate.java index 3fb1f3e..66678fa 100644 --- a/src/main/java/org/ultramine/server/chunk/PendingBlockUpdate.java +++ b/src/main/java/org/ultramine/server/chunk/PendingBlockUpdate.java @@ -43,6 +43,11 @@ PendingBlockUpdate p = (PendingBlockUpdate)o; return this.hash == p.hash && Block.isEqualTo(this.block, p.block); } + + public short getChunkCoordHash() + { + return hash; + } public int hashCode() { diff --git a/src/main/java/org/ultramine/server/data/NBTFileDataProvider.java b/src/main/java/org/ultramine/server/data/NBTFileDataProvider.java index 08b6e6d..6c228ed 100644 --- a/src/main/java/org/ultramine/server/data/NBTFileDataProvider.java +++ b/src/main/java/org/ultramine/server/data/NBTFileDataProvider.java @@ -2,13 +2,14 @@ import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -33,6 +34,8 @@ private final ServerConfigurationManager mgr; private File umPlayerDir; private List fastWarps = Collections.emptyList(); + private final Map savingPlayersCache = new ConcurrentHashMap<>(); + private long cachedPlayerCounter; public NBTFileDataProvider(ServerConfigurationManager mgr) { @@ -168,8 +171,11 @@ public NBTTagCompound loadPlayer(SaveHandler sh, GameProfile player) { - File dir = sh.getPlayerSaveDir(); - File file = new File(dir, player.getId().toString() + ".dat"); + File file = getPlayerNbtFile(sh, player); + CachedPlayerStruct data = savingPlayersCache.get(file); + if(data != null) + return data.nbt; + if(file.exists()) { try @@ -187,7 +193,17 @@ public void savePlayer(SaveHandler sh, GameProfile player, NBTTagCompound nbt) { - AsyncIOUtils.safeWriteNBT(new File(sh.getPlayerSaveDir(), player.getId().toString() + ".dat"), nbt); + File file = getPlayerNbtFile(sh, player); + long nextId = cachedPlayerCounter++; + savingPlayersCache.put(file, new CachedPlayerStruct(nbt, nextId)); + AsyncIOUtils.safeWriteNBT(file, nbt, () -> + savingPlayersCache.computeIfPresent(file, (file1, data) -> data.id == nextId ? null : data) + ); + } + + private static File getPlayerNbtFile(SaveHandler sh, GameProfile player) + { + return new File(sh.getPlayerSaveDir(), player.getId().toString() + ".dat"); } private NBTTagCompound getPlayerDataNBT(String username) @@ -227,23 +243,6 @@ return pdata; } - private void safeWriteNBT(File file, NBTTagCompound nbt) - { - try - { - File file1 = new File(file.getParentFile(), file.getName()+".tmp"); - CompressedStreamTools.writeCompressed(nbt, new FileOutputStream(file1)); - - if (file.exists()) - file.delete(); - file1.renameTo(file); - } - catch(IOException e) - { - log.warn("Failed to write file: "+file.getAbsolutePath(), e); - } - } - private void writeWarpList() { File file = mgr.getServerInstance().getStorageFile("warps.yml"); @@ -258,4 +257,16 @@ public Map warps; public List fastWarps; } + + private static class CachedPlayerStruct + { + public NBTTagCompound nbt; + public long id; + + public CachedPlayerStruct(NBTTagCompound nbt, long id) + { + this.nbt = nbt; + this.id = id; + } + } } diff --git a/src/main/java/org/ultramine/server/internal/LambdaHolder.java b/src/main/java/org/ultramine/server/internal/LambdaHolder.java index a6416e9..d34b795 100644 --- a/src/main/java/org/ultramine/server/internal/LambdaHolder.java +++ b/src/main/java/org/ultramine/server/internal/LambdaHolder.java @@ -2,11 +2,31 @@ import net.minecraft.entity.Entity; import net.minecraft.tileentity.TileEntity; +import net.openhft.koloboke.collect.map.ShortObjMap; +import net.openhft.koloboke.collect.map.hash.HashShortObjMaps; +import java.util.TreeSet; import java.util.function.Predicate; +import java.util.function.Supplier; +/** Java 8 features like lambdas must not be used in net.minecraft classes, so all lambdas used there, located here*/ public class LambdaHolder { public static final Predicate ENTITY_REMOVAL_PREDICATE = o -> ((Entity)o).removeThisTick; public static final Predicate TILE_ENTITY_REMOVAL_PREDICATE = o -> ((TileEntity)o).removeThisTick; + + private static final Supplier> NEW_TREE_SET = TreeSet::new; + private static final Supplier> NEW_SHORT_OBJ_MAP = HashShortObjMaps::newMutableMap; + + @SuppressWarnings("unchecked") + public static Supplier> newTreeSet() + { + return (Supplier>) (Object) NEW_TREE_SET; + } + + @SuppressWarnings("unchecked") + public static Supplier> newShortObjMap() + { + return (Supplier>) (Object) NEW_SHORT_OBJ_MAP; + } } diff --git a/src/main/java/org/ultramine/server/util/AsyncIOUtils.java b/src/main/java/org/ultramine/server/util/AsyncIOUtils.java index e6c8ee9..7344d5f 100644 --- a/src/main/java/org/ultramine/server/util/AsyncIOUtils.java +++ b/src/main/java/org/ultramine/server/util/AsyncIOUtils.java @@ -27,9 +27,9 @@ { FileUtils.writeStringToFile(file, data, Charsets.UTF_8); } - catch(IOException e) + catch(Exception e) { - log.warn("Failed to write file: "+file.getAbsolutePath(), e); + log.error("Failed to write file: "+file.getAbsolutePath(), e); } } }); @@ -46,16 +46,21 @@ { FileUtils.writeByteArrayToFile(file, data); } - catch(IOException e) + catch(Exception e) { - log.warn("Failed to write file: "+file.getAbsolutePath(), e); + log.error("Failed to write file: "+file.getAbsolutePath(), e); } } }); } - + public static void safeWriteNBT(final File file, final NBTTagCompound nbt) { + safeWriteNBT(file, nbt, null); + } + + public static void safeWriteNBT(final File file, final NBTTagCompound nbt, Runnable callback) + { GlobalExecutors.writingIO().execute(new Runnable() { @Override @@ -67,12 +72,14 @@ CompressedStreamTools.writeCompressed(nbt, new FileOutputStream(file1)); if (file.exists()) - file.delete(); - file1.renameTo(file); + FileUtils.forceDelete(file); + FileUtils.moveFile(file1, file); + if(callback != null) + callback.run(); } - catch(IOException e) + catch(Exception e) { - log.warn("Failed to write file: "+file.getAbsolutePath(), e); + log.error("Failed to write file: "+file.getAbsolutePath(), e); } } }); diff --git a/src/main/java/org/ultramine/server/util/WeakObjectPool.java b/src/main/java/org/ultramine/server/util/WeakObjectPool.java new file mode 100644 index 0000000..e86c4ce --- /dev/null +++ b/src/main/java/org/ultramine/server/util/WeakObjectPool.java @@ -0,0 +1,42 @@ +package org.ultramine.server.util; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +public class WeakObjectPool +{ + private final List> pool = new ArrayList<>(); + private final Supplier factory; + private final int limit; + + public WeakObjectPool(Supplier factory, int limit) + { + this.factory = factory; + this.limit = limit; + } + + public WeakObjectPool(Supplier factory) + { + this(factory, Integer.MAX_VALUE); + } + + public T getOrCreateInstance() + { + for(int size; (size = pool.size()) != 0;) + { + T val = pool.remove(size - 1).get(); + if(val != null) + return val; + } + + return factory.get(); + } + + public void release(T val) + { + if(pool.size() < limit) + pool.add(new WeakReference<>(val)); + } +} diff --git a/src/main/java/org/ultramine/server/world/WorldDescriptor.java b/src/main/java/org/ultramine/server/world/WorldDescriptor.java index ba099c4..a4c40e4 100644 --- a/src/main/java/org/ultramine/server/world/WorldDescriptor.java +++ b/src/main/java/org/ultramine/server/world/WorldDescriptor.java @@ -312,7 +312,7 @@ world.theChunkProviderServer.setWorldUnloaded(); world.theChunkProviderServer.unloadAll(save); - world.forceUnloadTileEntities(); + world.processTileEntityUnload(); if(save) world.saveOtherData(); diff --git a/src/main/resources/assets/ultramine/lang/en_US.lang b/src/main/resources/assets/ultramine/lang/en_US.lang index 710caf8..c95910d 100644 --- a/src/main/resources/assets/ultramine/lang/en_US.lang +++ b/src/main/resources/assets/ultramine/lang/en_US.lang @@ -1,6 +1,7 @@ #Server core ultramine.teleporter.fail.cooldownd=You will be able to teleport in %s seconds ultramine.teleporter.fail.dim=Failed to teleport: the world is not exists or was held +ultramine.teleporter.fail.riding=You can not teleport while riding ultramine.teleporter.delay=You will be teleported in %s seconds ultramine.teleporter.canceled=Teleportation canceled diff --git a/src/main/resources/assets/ultramine/lang/ru_RU.lang b/src/main/resources/assets/ultramine/lang/ru_RU.lang index ad3a9fd..0bde975 100644 --- a/src/main/resources/assets/ultramine/lang/ru_RU.lang +++ b/src/main/resources/assets/ultramine/lang/ru_RU.lang @@ -1,6 +1,7 @@ #Server core ultramine.teleporter.fail.cooldownd=Вы сможете телепортироваться через %s секунд ultramine.teleporter.fail.dim=Не удалось телепортироваться: мир не существует или заблокирован +ultramine.teleporter.fail.riding=Нельзя телепортироваться во время езды ultramine.teleporter.delay=Вы будете телепортированы через %s секунд ultramine.teleporter.canceled=Телепортация отменена