From 9bd098bd4f2dbe46605a284d90dc5aaaf0be7a19 Mon Sep 17 00:00:00 2001
From: LemADEC <lemadec.fr@gmail.com>
Date: Mon, 14 Aug 2017 16:12:25 +0200
Subject: [PATCH] Updated force field range to double it

Improved force field memory usage
Updated force field stabilisation to increase speed
Added 64 blocks range cap with fusion and inversion upgrades
---
 build.gradle                                  |   4 +-
 .../warpdrive/data/EnumForceFieldShape.java   | 112 ++++++++++++++----
 .../warpdrive/data/EnumForceFieldUpgrade.java |   4 +-
 .../cr0s/warpdrive/data/ForceFieldSetup.java  |  37 +++---
 4 files changed, 111 insertions(+), 46 deletions(-)

diff --git a/build.gradle b/build.gradle
index 61e7ec24..0b554fe9 100644
--- a/build.gradle
+++ b/build.gradle
@@ -119,9 +119,9 @@ idea {
 }
 
 runClient {
-    jvmArgs "-Xmx1024m", "-Xms1024m", "-ea"
+    jvmArgs "-Xmx2048m", "-Xms1024m", "-ea"
 }
 
 runServer {
-    jvmArgs "-Xmx1024m", "-Xms1024m", "-ea"
+    jvmArgs "-Xmx2048m", "-Xms1024m", "-ea"
 }
\ No newline at end of file
diff --git a/src/main/java/cr0s/warpdrive/data/EnumForceFieldShape.java b/src/main/java/cr0s/warpdrive/data/EnumForceFieldShape.java
index 4284bc98..bf1d4518 100644
--- a/src/main/java/cr0s/warpdrive/data/EnumForceFieldShape.java
+++ b/src/main/java/cr0s/warpdrive/data/EnumForceFieldShape.java
@@ -1,6 +1,7 @@
 package cr0s.warpdrive.data;
 
 
+import cr0s.warpdrive.WarpDrive;
 import cr0s.warpdrive.api.IForceFieldShape;
 
 import java.util.HashMap;
@@ -39,13 +40,51 @@ public enum EnumForceFieldShape implements IForceFieldShape {
 	
 	@Override
 	public Map<VectorI, Boolean> getVertexes(ForceFieldSetup forceFieldSetup) {
-		VectorI vScale = forceFieldSetup.vMax.clone().translateBack(forceFieldSetup.vMin);
-		Map<VectorI, Boolean> mapVertexes = new HashMap<>(vScale.x * vScale.y * vScale.z);
+		final VectorI vScale = forceFieldSetup.vMax.clone().translateBack(forceFieldSetup.vMin);
+		final boolean isFusionOrInverted = forceFieldSetup.hasFusion || forceFieldSetup.isInverted;
+		final int sizeEstimation;
+		if (!isFusionOrInverted) {// surface only
+			// plane surface    is r^2
+			// sphere surface   is 4*PI*r^2
+			// cylinder surface is 2*PI*r^2 + 2*PI*r*h = 2*PI*r^2 * 1.5
+			// cube surface     is 4*6*r^2
+			final int maxRadius = 1 + (int) Math.ceil(Math.max(vScale.x, Math.max(vScale.y, vScale.z)) / 2.0F);
+			switch(this) {
+			case SPHERE:
+				sizeEstimation = (int) Math.ceil(4 * Math.PI * maxRadius * maxRadius);
+				break;
+			
+			case CYLINDER_H:
+			case CYLINDER_V:
+			case TUBE:
+				sizeEstimation = (int) Math.ceil(4 * Math.PI * maxRadius * maxRadius * 1.5F);
+				break;
+			
+			case CUBE:
+			case TUNNEL:
+				sizeEstimation = 4 * 6 * maxRadius * maxRadius;
+				break;
+			
+			case PLANE:
+				sizeEstimation = maxRadius * maxRadius;
+				break;
+			
+			default:
+				assert(false);
+				sizeEstimation = 8;
+				break;
+			}
+		} else {
+			sizeEstimation = vScale.x * vScale.y * vScale.z;
+		}
+		final Map<VectorI, Boolean> mapVertexes = new HashMap<>(sizeEstimation);
+		
 		float radius;
 		float halfThickness = forceFieldSetup.thickness / 2.0F;
 		float radiusInterior2;
 		float radiusPerimeter2;
 		VectorI vCenter;
+		boolean isPerimeter;
 		switch(this) {
 		case SPHERE:
 			radius = forceFieldSetup.vMax.y;
@@ -53,13 +92,16 @@ public enum EnumForceFieldShape implements IForceFieldShape {
 			radiusPerimeter2 = (radius + halfThickness) * (radius + halfThickness);
 			vCenter = new VectorI(0, 0, 0);
 			for (int y = forceFieldSetup.vMin.y; y <= forceFieldSetup.vMax.y; y++) {
-				int y2 = (y - vCenter.y) * (y - vCenter.y);
+				final int y2 = (y - vCenter.y) * (y - vCenter.y);
 				for (int x = forceFieldSetup.vMin.x; x <= forceFieldSetup.vMax.x; x++) {
-					int x2 = (x - vCenter.x) * (x - vCenter.x);
+					final int x2 = (x - vCenter.x) * (x - vCenter.x);
 					for (int z = forceFieldSetup.vMin.z; z <= forceFieldSetup.vMax.z; z++) {
-						int z2 = (z - vCenter.z) * (z - vCenter.z);
+						final int z2 = (z - vCenter.z) * (z - vCenter.z);
 						if (x2 + y2 + z2 <= radiusPerimeter2) {
-							mapVertexes.put(new VectorI(x, y, z), x2 + y2 + z2 >= radiusInterior2);
+							isPerimeter = x2 + y2 + z2 >= radiusInterior2;
+							if (isPerimeter || isFusionOrInverted) {
+								mapVertexes.put(new VectorI(x, y, z), isPerimeter);
+							}
 						}
 					}
 				}
@@ -76,9 +118,11 @@ public enum EnumForceFieldShape implements IForceFieldShape {
 				for (int z = forceFieldSetup.vMin.z; z <= forceFieldSetup.vMax.z; z++) {
 					int z2 = (z - vCenter.z) * (z - vCenter.z);
 					if (y2 + z2 <= radiusPerimeter2) {
-						boolean isPerimeter = y2 + z2 >= radiusInterior2;
-						for (int x = forceFieldSetup.vMin.x; x <= forceFieldSetup.vMax.x; x++) {
-							mapVertexes.put(new VectorI(x, y, z), isPerimeter);
+						isPerimeter = y2 + z2 >= radiusInterior2;
+						if (isPerimeter || isFusionOrInverted) {
+							for (int x = forceFieldSetup.vMin.x; x <= forceFieldSetup.vMax.x; x++) {
+								mapVertexes.put(new VectorI(x, y, z), isPerimeter);
+							}
 						}
 					}
 				}
@@ -95,9 +139,11 @@ public enum EnumForceFieldShape implements IForceFieldShape {
 				for (int y = forceFieldSetup.vMin.y; y <= forceFieldSetup.vMax.y; y++) {
 					int y2 = (y - vCenter.y) * (y - vCenter.y);
 					if (x2 + y2 <= radiusPerimeter2) {
-						boolean isPerimeter = x2 + y2 >= radiusInterior2;
-						for (int z = forceFieldSetup.vMin.z; z <= forceFieldSetup.vMax.z; z++) {
-							mapVertexes.put(new VectorI(x, y, z), isPerimeter);
+						isPerimeter = x2 + y2 >= radiusInterior2;
+						if (isPerimeter || isFusionOrInverted) {
+							for (int z = forceFieldSetup.vMin.z; z <= forceFieldSetup.vMax.z; z++) {
+								mapVertexes.put(new VectorI(x, y, z), isPerimeter);
+							}
 						}
 					}
 				}
@@ -114,9 +160,11 @@ public enum EnumForceFieldShape implements IForceFieldShape {
 				for (int z = forceFieldSetup.vMin.z; z <= forceFieldSetup.vMax.z; z++) {
 					int z2 = (z - vCenter.z) * (z - vCenter.z);
 					if (x2 + z2 <= radiusPerimeter2) {
-						boolean isPerimeter = x2 + z2 >= radiusInterior2;
-						for (int y = forceFieldSetup.vMin.y; y <= forceFieldSetup.vMax.y; y++) {
-							mapVertexes.put(new VectorI(x, y, z), isPerimeter);
+						isPerimeter = x2 + z2 >= radiusInterior2;
+						if (isPerimeter || isFusionOrInverted) {
+							for (int y = forceFieldSetup.vMin.y; y <= forceFieldSetup.vMax.y; y++) {
+								mapVertexes.put(new VectorI(x, y, z), isPerimeter);
+							}
 						}
 					}
 				}
@@ -133,7 +181,10 @@ public enum EnumForceFieldShape implements IForceFieldShape {
 					for (int z = forceFieldSetup.vMin.z; z <= forceFieldSetup.vMax.z; z++) {
 						boolean zFace = Math.abs(z - forceFieldSetup.vMin.z) <= halfThickness
 						             || Math.abs(z - forceFieldSetup.vMax.z) <= halfThickness;
-						mapVertexes.put(new VectorI(x, y, z), xFace || yFace || zFace);
+						isPerimeter = xFace || yFace || zFace;
+						if (isPerimeter || isFusionOrInverted) {
+							mapVertexes.put(new VectorI(x, y, z), isPerimeter);
+						}
 					}
 				}
 			}
@@ -141,11 +192,13 @@ public enum EnumForceFieldShape implements IForceFieldShape {
 		
 		case PLANE:
 			for (int y = forceFieldSetup.vMin.y; y <= forceFieldSetup.vMax.y; y++) {
-				boolean yFace = Math.abs(y - forceFieldSetup.vMin.y) <= halfThickness
-				             || Math.abs(y - forceFieldSetup.vMax.y) <= halfThickness;
-				for (int x = forceFieldSetup.vMin.x; x <= forceFieldSetup.vMax.x; x++) {
-					for (int z = forceFieldSetup.vMin.z; z <= forceFieldSetup.vMax.z; z++) {
-						mapVertexes.put(new VectorI(x, y, z), yFace);
+				isPerimeter = Math.abs(y - forceFieldSetup.vMin.y) <= halfThickness
+				           || Math.abs(y - forceFieldSetup.vMax.y) <= halfThickness;
+				if (isPerimeter || isFusionOrInverted) {
+					for (int x = forceFieldSetup.vMin.x; x <= forceFieldSetup.vMax.x; x++) {
+						for (int z = forceFieldSetup.vMin.z; z <= forceFieldSetup.vMax.z; z++) {
+							mapVertexes.put(new VectorI(x, y, z), isPerimeter);
+						}
 					}
 				}
 			}
@@ -157,10 +210,12 @@ public enum EnumForceFieldShape implements IForceFieldShape {
 					boolean xFace = Math.abs(x - forceFieldSetup.vMin.x) <= halfThickness
 					             || Math.abs(x - forceFieldSetup.vMax.x) <= halfThickness;
 					for (int z = forceFieldSetup.vMin.z; z <= forceFieldSetup.vMax.z; z++) {
-						boolean isPerimeter = xFace
-						                   || Math.abs(z - forceFieldSetup.vMin.z) <= halfThickness
-						                   || Math.abs(z - forceFieldSetup.vMax.z) <= halfThickness;
-						mapVertexes.put(new VectorI(x, y, z), isPerimeter);
+						isPerimeter = xFace
+						           || Math.abs(z - forceFieldSetup.vMin.z) <= halfThickness
+						           || Math.abs(z - forceFieldSetup.vMax.z) <= halfThickness;
+						if (isPerimeter || isFusionOrInverted) {
+							mapVertexes.put(new VectorI(x, y, z), isPerimeter);
+						}
 					}
 				}
 			}
@@ -171,6 +226,13 @@ public enum EnumForceFieldShape implements IForceFieldShape {
 			
 		}
 		
+		if (mapVertexes.size() > sizeEstimation) {
+			WarpDrive.logger.warn(String.format("Underestimated memory location lag %d > %d for shape %s with size %s, isFusionOrInverted %s. Please report this to the mod author",
+			                                    mapVertexes.size(), sizeEstimation,
+			                                    unlocalizedName,
+			                                    vScale,
+			                                    isFusionOrInverted));
+		}
 		return mapVertexes;
 	}
 }
diff --git a/src/main/java/cr0s/warpdrive/data/EnumForceFieldUpgrade.java b/src/main/java/cr0s/warpdrive/data/EnumForceFieldUpgrade.java
index 8b994a41..4c0b56e0 100644
--- a/src/main/java/cr0s/warpdrive/data/EnumForceFieldUpgrade.java
+++ b/src/main/java/cr0s/warpdrive/data/EnumForceFieldUpgrade.java
@@ -39,13 +39,13 @@ public enum EnumForceFieldUpgrade implements IForceFieldUpgrade, IForceFieldUpgr
 	INVERSION    ("inversion"    ,        1,      0,    1.0F,     1.0F,  1.250F,  1.250F,   0.000F,  0.000F, 1500.0F, 0.150F,  0.150F,   20.0F, "value is boolean"),
 	ITEM_PORT    ("item_port"    ,        0,      1,    1.0F,    10.0F,  0.000F,  0.000F,   0.950F,  0.900F,   50.0F, 0.120F,  0.500F,    2.0F, "value is boolean"),
 	PUMPING      ("pumping"      ,        0,      1, 1000.0F, 50000.0F,  0.800F,  1.000F,   0.400F,  1.000F,  800.0F, 0.150F,  4.500F,    0.0F, "value is viscosity"),
-	RANGE        ("range"        ,        4,      1,    8.0F,    56.0F,  1.150F,  0.800F,   1.150F,  0.800F,   10.0F, 0.300F,  0.750F,   12.0F, "value is bonus blocks"),
+	RANGE        ("range"        ,        4,      1,    8.0F,   128.0F,  1.150F,  0.450F,   1.150F,  0.450F,   10.0F, 0.300F,  0.750F,   12.0F, "value is bonus blocks"),
 	REPULSION    ("repulsion"    ,        0,      1,    1.0F,     4.0F,  0.000F,  0.000F,   0.000F,  0.000F,   50.0F, 0.150F,  0.000F,    5.0F, "value is acceleration"),
 	ROTATION     ("rotation"     ,        1,      0,    1.0F,     1.0F,  0.000F,  0.000F,   0.000F,  0.000F,  100.0F, 0.000F,  0.000F,    0.0F, "value is boolean"),
 	SHOCK        ("shock"        ,        3,      1,    1.0F,    10.0F,  0.800F,  0.800F,   0.800F,  0.800F,  300.0F, 0.600F,  4.000F,   30.0F, "value is damage points"),
 	SILENCER     ("silencer"     ,        1,      0,    1.0F,     1.0F,  0.000F,  0.000F,   0.000F,  0.000F,    0.0F, 0.120F,  0.620F,    0.0F, "value is boolean"),
 	SPEED        ("speed"        ,        4,      1,    1.0F,    20.0F,  1.250F,  6.000F,   1.200F,  5.000F,  200.0F, 0.135F,  1.250F,   15.0F, "value is not used (just a counter)"),
-	STABILIZATION("stabilization",        0,      1,    1.0F,     6.0F,  0.250F,  0.550F,   0.025F,  0.150F,  400.0F, 0.050F, 73.600F,    0.0F, "value is boolean"),
+	STABILIZATION("stabilization",        0,      1,    1.0F,     9.0F,  0.250F,  0.850F,   0.025F,  0.450F,  400.0F, 0.050F, 73.600F,    0.0F, "value is boolean"),
 	THICKNESS    ("thickness"    ,        5,      1,    0.2F,     1.0F,  0.800F,  1.600F,   0.900F,  1.500F,  100.0F, 0.400F,  2.200F,    5.0F, "value is bonus ratio"),
 	TRANSLATION  ("translation"  ,        1,      0,    1.0F,     1.0F,  0.000F,  0.000F,   0.000F,  0.000F,  100.0F, 0.000F,  0.000F,    0.0F, "value is boolean"),
 	;
diff --git a/src/main/java/cr0s/warpdrive/data/ForceFieldSetup.java b/src/main/java/cr0s/warpdrive/data/ForceFieldSetup.java
index 84451fef..b7ca0dab 100644
--- a/src/main/java/cr0s/warpdrive/data/ForceFieldSetup.java
+++ b/src/main/java/cr0s/warpdrive/data/ForceFieldSetup.java
@@ -256,23 +256,6 @@ public class ForceFieldSetup extends GlobalPosition {
 		scanSpeed = Math.min(FORCEFIELD_MAX_SCAN_SPEED_BLOCKS_PER_SECOND, scanSpeed);
 		placeSpeed = Math.min(FORCEFIELD_MAX_PLACE_SPEED_BLOCKS_PER_SECOND, placeSpeed);
 		
-		// range is maximum distance
-		double range = getScaledUpgrade(EnumForceFieldUpgrade.RANGE);
-		if (range == 0.0D) {
-			range = 8.0D;
-			v3Min = new Vector3(-1.0D, -1.0D, -1.0D);
-			v3Max = new Vector3( 1.0D,  1.0D,  1.0D);
-		}
-		vMin.x = (int) Math.round(Math.min(0.0D, Math.max(-1.0D, v3Min.x)) * range);
-		vMin.y = (int) Math.round(Math.min(0.0D, Math.max(-1.0D, v3Min.y)) * range);
-		vMin.z = (int) Math.round(Math.min(0.0D, Math.max(-1.0D, v3Min.z)) * range);
-		vMax.x = (int) Math.round(Math.min(1.0D, Math.max( 0.0D, v3Max.x)) * range);
-		vMax.y = (int) Math.round(Math.min(1.0D, Math.max( 0.0D, v3Max.y)) * range);
-		vMax.z = (int) Math.round(Math.min(1.0D, Math.max( 0.0D, v3Max.z)) * range);
-		vTranslation.x += (int) Math.round(Math.min(1.0D, Math.max(-1.0D, v3Translation.x)) * range);
-		vTranslation.y += (int) Math.round(Math.min(1.0D, Math.max(-1.0D, v3Translation.y)) * range);
-		vTranslation.z += (int) Math.round(Math.min(1.0D, Math.max(-1.0D, v3Translation.z)) * range);
-		
 		// acceleration is a compound of attraction and repulsion
 		accelerationLevel = getScaledUpgrade(EnumForceFieldUpgrade.ATTRACTION) - getScaledUpgrade(EnumForceFieldUpgrade.REPULSION);
 		
@@ -289,6 +272,26 @@ public class ForceFieldSetup extends GlobalPosition {
 		breaking_maxHardness = getScaledUpgrade(EnumForceFieldUpgrade.BREAKING);
 		pumping_maxViscosity = getScaledUpgrade(EnumForceFieldUpgrade.PUMPING);
 		thickness = 1.0F + getScaledUpgrade(EnumForceFieldUpgrade.THICKNESS);
+		
+		// range is maximum distance
+		double range = getScaledUpgrade(EnumForceFieldUpgrade.RANGE);
+		if (range == 0.0D) {
+			range = 8.0D;
+			v3Min = new Vector3(-1.0D, -1.0D, -1.0D);
+			v3Max = new Vector3( 1.0D,  1.0D,  1.0D);
+		}
+		if (hasFusion || isInverted) {
+			range = Math.min(64.0D, range);
+		}
+		vMin.x = (int) Math.round(Math.min(0.0D, Math.max(-1.0D, v3Min.x)) * range);
+		vMin.y = (int) Math.round(Math.min(0.0D, Math.max(-1.0D, v3Min.y)) * range);
+		vMin.z = (int) Math.round(Math.min(0.0D, Math.max(-1.0D, v3Min.z)) * range);
+		vMax.x = (int) Math.round(Math.min(1.0D, Math.max( 0.0D, v3Max.x)) * range);
+		vMax.y = (int) Math.round(Math.min(1.0D, Math.max( 0.0D, v3Max.y)) * range);
+		vMax.z = (int) Math.round(Math.min(1.0D, Math.max( 0.0D, v3Max.z)) * range);
+		vTranslation.x += (int) Math.round(Math.min(1.0D, Math.max(-1.0D, v3Translation.x)) * range);
+		vTranslation.y += (int) Math.round(Math.min(1.0D, Math.max(-1.0D, v3Translation.y)) * range);
+		vTranslation.z += (int) Math.round(Math.min(1.0D, Math.max(-1.0D, v3Translation.z)) * range);
 	}
 	
 	public double getEntityEnergyCost(final int countEntityInteractions) {