Merge remote-tracking branch 'upstream/master' into abel-misc-tweaks-2025-06

This commit is contained in:
abel1502 2025-06-02 18:40:37 +03:00
commit 640b0ae341
No known key found for this signature in database
GPG Key ID: 076926596A536338
13 changed files with 431 additions and 196 deletions

View File

@ -443,8 +443,10 @@ public abstract class BlockDummyable extends BlockContainer implements ICustomBl
y = pos[1];
z = pos[2];
for(AxisAlignedBB aabb :this.bounding) {
AxisAlignedBB boxlet = getAABBRotationOffset(aabb, x + 0.5, y, z + 0.5, ForgeDirection.getOrientation(world.getBlockMetadata(x, y, z) - offset).getRotation(ForgeDirection.UP));
ForgeDirection rot = ForgeDirection.getOrientation(world.getBlockMetadata(x, y, z) - offset).getRotation(ForgeDirection.UP);
for(AxisAlignedBB aabb : this.bounding) {
AxisAlignedBB boxlet = getAABBRotationOffset(aabb, x + 0.5, y, z + 0.5, rot);
if(entityBounding.intersectsWith(boxlet)) {
list.add(boxlet);
@ -469,6 +471,7 @@ public abstract class BlockDummyable extends BlockContainer implements ICustomBl
return AxisAlignedBB.getBoundingBox(aabb.minX, aabb.minY, aabb.minZ, aabb.maxX, aabb.maxY, aabb.maxZ).offset(x + 0.5, y + 0.5, z + 0.5);
}
// Don't mutate the xyz parameters, or the interaction max distance will bite you
@Override
public MovingObjectPosition collisionRayTrace(World world, int x, int y, int z, Vec3 startVec, Vec3 endVec) {
if(!this.useDetailedHitbox()) {
@ -480,12 +483,10 @@ public abstract class BlockDummyable extends BlockContainer implements ICustomBl
if(pos == null)
return super.collisionRayTrace(world, x, y, z, startVec, endVec);
x = pos[0];
y = pos[1];
z = pos[2];
ForgeDirection rot = ForgeDirection.getOrientation(world.getBlockMetadata(pos[0], pos[1], pos[2]) - offset).getRotation(ForgeDirection.UP);
for(AxisAlignedBB aabb :this.bounding) {
AxisAlignedBB boxlet = getAABBRotationOffset(aabb, x + 0.5, y, z + 0.5, ForgeDirection.getOrientation(world.getBlockMetadata(x, y, z) - offset).getRotation(ForgeDirection.UP));
for(AxisAlignedBB aabb : this.bounding) {
AxisAlignedBB boxlet = getAABBRotationOffset(aabb, pos[0] + 0.5, pos[1], pos[2] + 0.5, rot);
MovingObjectPosition intercept = boxlet.calculateIntercept(startVec, endVec);
if(intercept != null) {
@ -529,10 +530,10 @@ public abstract class BlockDummyable extends BlockContainer implements ICustomBl
double dZ = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * (double)interp;
float exp = 0.002F;
int meta = world.getBlockMetadata(x, y, z);
ForgeDirection rot = ForgeDirection.getOrientation(world.getBlockMetadata(x, y, z) - offset).getRotation(ForgeDirection.UP);
ICustomBlockHighlight.setup();
for(AxisAlignedBB aabb : this.bounding) RenderGlobal.drawOutlinedBoundingBox(getAABBRotationOffset(aabb.expand(exp, exp, exp), 0, 0, 0, ForgeDirection.getOrientation(meta - offset).getRotation(ForgeDirection.UP)).getOffsetBoundingBox(x - dX + 0.5, y - dY, z - dZ + 0.5), -1);
for(AxisAlignedBB aabb : this.bounding) RenderGlobal.drawOutlinedBoundingBox(getAABBRotationOffset(aabb.expand(exp, exp, exp), 0, 0, 0, rot).getOffsetBoundingBox(x - dX + 0.5, y - dY, z - dZ + 0.5), -1);
ICustomBlockHighlight.cleanup();
}

View File

@ -26,8 +26,7 @@ public class BombConfig {
public static int fDelay = 4;
public static int limitExplosionLifespan = 0;
public static boolean chunkloading = true;
public static boolean parallelization = true;
public static boolean accumulatedDestruction = true;
public static int explosionAlgorithm = 2;
public static void loadFromConfig(Configuration config) {
@ -95,8 +94,7 @@ public class BombConfig {
falloutDelayProp.comment = "How many ticks to wait for the next fallout chunk computation";
fDelay = falloutDelayProp.getInt();
chunkloading = CommonConfig.createConfigBool(config, CATEGORY_NUKE, "6.XX_enableChunkLoading", "Allows all types of procedural explosions to keep the central chunk loaded.", true);
parallelization = CommonConfig.createConfigBool(config, CATEGORY_NUKE, "6.XX_enableParallelization", "Allows explosions to use multiple threads.", true);
accumulatedDestruction = CommonConfig.createConfigBool(config, CATEGORY_NUKE, "6.XX_enableAccumulatedDestruction", "Enables the accumulated destruction model for explosions. Blocks accumulate damage and are only destroyed once their resistance is exceeded.\nMore physically accurate, slightly slower. Requires enableParallelization = true.", true);
chunkloading = CommonConfig.createConfigBool(config, CATEGORY_NUKE, "6.05_enableChunkLoading", "Allows all types of procedural explosions to keep the central chunk loaded.", true);
explosionAlgorithm = CommonConfig.createConfigInt(config, CATEGORY_NUKE, "6.06_explosionAlgorithm", "Configures the algorithm of mk5 explosion. \n0 = Legacy, 1 = Threaded DDA, 2 = Threaded DDA with damage accumulation.", 2);
}
}

View File

@ -70,7 +70,7 @@ public class EntityNukeExplosionMK5 extends EntityExplosionChunkloading {
if(explosion == null) {
explosionStart = System.currentTimeMillis();
if (BombConfig.parallelization) {
if (BombConfig.explosionAlgorithm == 1 || BombConfig.explosionAlgorithm == 2) {
explosion = new ExplosionNukeRayParallelized(worldObj, posX, posY, posZ,
strength, speed, length);
} else {
@ -82,20 +82,17 @@ public class EntityNukeExplosionMK5 extends EntityExplosionChunkloading {
if(!explosion.isComplete()) {
explosion.cacheChunksTick(BombConfig.mk5);
explosion.destructionTick(BombConfig.mk5);
} else if(fallout) {
if(GeneralConfig.enableExtendedLogging && explosionStart != 0)
MainRegistry.logger.log(Level.INFO, "[NUKE] Explosion complete. Time elapsed: " + (System.currentTimeMillis() - explosionStart) + "ms");
EntityFalloutRain fallout = new EntityFalloutRain(this.worldObj);
fallout.posX = this.posX;
fallout.posY = this.posY;
fallout.posZ = this.posZ;
fallout.setScale((int)(this.length * 2.5 + falloutAdd) * BombConfig.falloutRange / 100);
this.worldObj.spawnEntityInWorld(fallout);
this.clearChunkLoader();
this.setDead();
} else {
if(GeneralConfig.enableExtendedLogging && explosionStart != 0)
MainRegistry.logger.log(Level.INFO, "[NUKE] Explosion complete. Time elapsed: {}ms", (System.currentTimeMillis() - explosionStart));
if(fallout) {
EntityFalloutRain fallout = new EntityFalloutRain(this.worldObj);
fallout.posX = this.posX;
fallout.posY = this.posY;
fallout.posZ = this.posZ;
fallout.setScale((int)(this.length * 2.5 + falloutAdd) * BombConfig.falloutRange / 100);
this.worldObj.spawnEntityInWorld(fallout);
}
this.clearChunkLoader();
this.setDead();
}
@ -153,7 +150,7 @@ public class EntityNukeExplosionMK5 extends EntityExplosionChunkloading {
public static EntityNukeExplosionMK5 statFac(World world, int r, double x, double y, double z) {
if(GeneralConfig.enableExtendedLogging && !world.isRemote)
MainRegistry.logger.log(Level.INFO, "[NUKE] Initialized explosion at " + x + " / " + y + " / " + z + " with strength " + r + "!");
MainRegistry.logger.log(Level.INFO, "[NUKE] Initialized explosion at {} / {} / {} with strength {}!", x, y, z, r);
if(r == 0)
r = 25;

View File

@ -3,6 +3,9 @@ package com.hbm.explosion;
import com.hbm.config.BombConfig;
import com.hbm.interfaces.IExplosionRay;
import com.hbm.main.MainRegistry;
import com.hbm.util.ChunkKey;
import com.hbm.util.ConcurrentBitSet;
import com.hbm.util.SubChunkSnapshot;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.util.Vec3;
@ -15,14 +18,14 @@ import org.apache.logging.log4j.Level;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLongArray;
import java.util.concurrent.atomic.DoubleAdder;
import static com.hbm.util.SubChunkSnapshot.getSnapshot;
public class ExplosionNukeRayParallelized implements IExplosionRay {
private static final int WORLD_HEIGHT = 256;
private static final int BITSET_SIZE = 16 * WORLD_HEIGHT * 16;
private static final int WORDS_PER_SET = BITSET_SIZE >>> 6; // (16*256*16)/64
protected final World world;
private final double explosionX, explosionY, explosionZ;
@ -85,7 +88,7 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
Thread.currentThread().interrupt();
} finally {
collectFinished = true;
if (BombConfig.accumulatedDestruction) {
if (BombConfig.explosionAlgorithm == 2) {
pool.submit(this::runConsolidation);
} else {
consolidationFinished = true;
@ -112,7 +115,7 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
ChunkKey ck = cacheQueue.poll();
if (ck == null) break;
snapshots.computeIfAbsent(ck, key -> {
SubChunkSnapshot snap = createSubChunk(key.pos, key.subY);
SubChunkSnapshot snap = getSnapshot(this.world, key.pos, key.subY);
return snap == null ? SubChunkSnapshot.EMPTY : snap;
});
}
@ -146,37 +149,33 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
ExtendedBlockStorage storage = storages[subY];
if (storage == null) continue;
int yPrimeMin = 255 - ((subY << 4) + 15);
int startBit = yPrimeMin << 8;
int yPrimeMax = 255 - (subY << 4);
int endBit = (yPrimeMax << 8) | 0xFF;
int startBit = (WORLD_HEIGHT - 1 - ((subY << 4) + 15)) << 8;
int endBit = ((WORLD_HEIGHT - 1 - (subY << 4)) << 8) | 0xFF;
int bit = bs.nextSetBit(startBit);
if (bit < 0 || bit > endBit) continue;
while (bit >= 0 && bit <= endBit && System.nanoTime() < deadline) {
int yGlobal = 255 - (bit >>> 8);
int yGlobal = WORLD_HEIGHT - 1 - (bit >>> 8);
int xGlobal = (cp.chunkXPos << 4) | ((bit >>> 4) & 0xF);
int zGlobal = (cp.chunkZPos << 4) | (bit & 0xF);
if (world.getTileEntity(xGlobal, yGlobal, zGlobal) != null) {
chunk.removeTileEntity(xGlobal & 0xF, yGlobal, zGlobal & 0xF); // world Y
world.removeTileEntity(xGlobal, yGlobal, zGlobal);
}
int xLocal = xGlobal & 0xF;
int yLocal = yGlobal & 0xF;
int zLocal = zGlobal & 0xF;
storage.func_150818_a(xLocal, yLocal, zLocal, Blocks.air);
storage.setExtBlockMetadata(xLocal, yLocal, zLocal, 0);
chunkModified = true;
if (storage.getBlockByExtId(xLocal, yLocal, zLocal) != Blocks.air) {
if (world.getTileEntity(xGlobal, yGlobal, zGlobal) != null) {
world.removeTileEntity(xGlobal, yGlobal, zGlobal);
}
world.notifyBlocksOfNeighborChange(xGlobal, yGlobal, zGlobal, Blocks.air);
world.markBlockForUpdate(xGlobal, yGlobal, zGlobal);
storage.func_150818_a(xLocal, yLocal, zLocal, Blocks.air);
storage.setExtBlockMetadata(xLocal, yLocal, zLocal, 0);
chunkModified = true;
world.updateLightByType(EnumSkyBlock.Sky, xGlobal, yGlobal, zGlobal);
world.updateLightByType(EnumSkyBlock.Block, xGlobal, yGlobal, zGlobal);
world.notifyBlocksOfNeighborChange(xGlobal, yGlobal, zGlobal, Blocks.air);
world.markBlockForUpdate(xGlobal, yGlobal, zGlobal);
world.updateLightByType(EnumSkyBlock.Sky, xGlobal, yGlobal, zGlobal);
world.updateLightByType(EnumSkyBlock.Block, xGlobal, yGlobal, zGlobal);
}
bs.clear(bit);
bit = bs.nextSetBit(bit + 1);
}
@ -186,7 +185,6 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
chunk.setChunkModified();
world.markBlockRangeForRenderUpdate(cp.chunkXPos << 4, 0, cp.chunkZPos << 4, (cp.chunkXPos << 4) | 15, WORLD_HEIGHT - 1, (cp.chunkZPos << 4) | 15);
}
if (bs.isEmpty()) {
destructionMap.remove(cp);
for (int sy = 0; sy < (WORLD_HEIGHT >> 4); sy++) {
@ -244,49 +242,6 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
if (this.orderedChunks != null) this.orderedChunks.clear();
}
private SubChunkSnapshot createSubChunk(ChunkCoordIntPair cpos, int subY) {
if (!world.getChunkProvider().chunkExists(cpos.chunkXPos, cpos.chunkZPos)) {
return SubChunkSnapshot.EMPTY;
}
Chunk chunk = world.getChunkFromChunkCoords(cpos.chunkXPos, cpos.chunkZPos);
ExtendedBlockStorage ebs = chunk.getBlockStorageArray()[subY];
if (ebs == null || ebs.isEmpty()) {
return SubChunkSnapshot.EMPTY;
}
short[] data = new short[16 * 16 * 16];
List<Block> palette = new ArrayList<>();
palette.add(Blocks.air);
Map<Block, Short> idxMap = new HashMap<>();
idxMap.put(Blocks.air, (short) 0);
boolean allAir = true;
for (int ly = 0; ly < 16; ly++) {
for (int lz = 0; lz < 16; lz++) {
for (int lx = 0; lx < 16; lx++) {
Block block = ebs.getBlockByExtId(lx, ly, lz);
int idx;
if (block == Blocks.air) {
idx = 0;
} else {
allAir = false;
Short e = idxMap.get(block);
if (e == null) {
idxMap.put(block, (short) palette.size());
palette.add(block);
idx = palette.size() - 1;
} else {
idx = e;
}
}
data[(ly << 8) | (lz << 4) | lx] = (short) idx;
}
}
}
if (allAir) return SubChunkSnapshot.EMPTY;
return new SubChunkSnapshot(palette.toArray(new Block[0]), data);
}
private List<Vec3> generateSphereRays(int count) {
List<Vec3> list = new ArrayList<>(count);
if (count <= 0) return list;
@ -316,7 +271,7 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
continue;
}
ConcurrentBitSet chunkDestructionBitSet = destructionMap.computeIfAbsent(cp, k -> new ConcurrentBitSet());
ConcurrentBitSet chunkDestructionBitSet = destructionMap.computeIfAbsent(cp, k -> new ConcurrentBitSet(BITSET_SIZE));
Iterator<Map.Entry<Integer, DoubleAdder>> damageEntryIterator = accumulator.entrySet().iterator();
while (damageEntryIterator.hasNext()) {
@ -330,7 +285,7 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
continue;
}
int yGlobal = 255 - (bitIndex >>> 8);
int yGlobal = WORLD_HEIGHT - 1 - (bitIndex >>> 8);
int subY = yGlobal >> 4;
if (subY < 0) {
@ -370,91 +325,6 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
consolidationFinished = true;
}
private static class ChunkKey {
final ChunkCoordIntPair pos;
final int subY;
ChunkKey(int cx, int cz, int sy) {
this.pos = new ChunkCoordIntPair(cx, cz);
this.subY = sy;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ChunkKey)) return false;
ChunkKey k = (ChunkKey) o;
return subY == k.subY && pos.equals(k.pos);
}
@Override
public int hashCode() {
return Objects.hash(pos.chunkXPos, pos.chunkZPos, subY);
}
}
private static class SubChunkSnapshot {
private static final SubChunkSnapshot EMPTY = new SubChunkSnapshot(new Block[]{Blocks.air}, null);
private final Block[] palette;
private final short[] data;
SubChunkSnapshot(Block[] p, short[] d) {
this.palette = p;
this.data = d;
}
Block getBlock(int x, int y, int z) {
if (this == EMPTY || data == null) return Blocks.air;
short idx = data[(y << 8) | (z << 4) | x];
return (idx >= 0 && idx < palette.length) ? palette[idx] : Blocks.air;
}
}
private static final class ConcurrentBitSet {
private final AtomicLongArray words = new AtomicLongArray(WORDS_PER_SET);
void set(int bit) {
if (bit < 0 || bit >= BITSET_SIZE) return;
int wd = bit >>> 6;
long m = 1L << (bit & 63);
while (true) {
long o = words.get(wd);
long u = o | m;
if (o == u || words.compareAndSet(wd, o, u)) return;
}
}
void clear(int bit) {
if (bit < 0 || bit >= BITSET_SIZE) return;
int wd = bit >>> 6;
long m = ~(1L << (bit & 63));
while (true) {
long oldWord = words.get(wd);
long newWord = oldWord & m;
if (oldWord == newWord || words.compareAndSet(wd, oldWord, newWord)) {
return;
}
}
}
int nextSetBit(int from) {
if (from < 0) from = 0;
int wd = from >>> 6;
if (wd >= WORDS_PER_SET) return -1;
long w = words.get(wd) & (~0L << (from & 63));
while (true) {
if (w != 0) return (wd << 6) + Long.numberOfTrailingZeros(w);
if (++wd == WORDS_PER_SET) return -1;
w = words.get(wd);
}
}
boolean isEmpty() {
for (int i = 0; i < WORDS_PER_SET; i++) if (words.get(i) != 0) return false;
return true;
}
}
private static class ChunkDamageAccumulator {
// key = bitIndex, value = total accumulated damage
private final ConcurrentHashMap<Integer, DoubleAdder> damageMap = new ConcurrentHashMap<>();
@ -602,7 +472,7 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
energy -= damageDealt;
if (damageDealt > 0) {
int bitIndex = ((WORLD_HEIGHT - 1 - y) << 8) | ((x & 0xF) << 4) | (z & 0xF);
if (BombConfig.accumulatedDestruction) {
if (BombConfig.explosionAlgorithm == 2) {
ChunkCoordIntPair chunkPos = ck.pos;
ChunkDamageAccumulator chunkAccumulator =
accumulatedDamageMap.computeIfAbsent(chunkPos, k -> new ChunkDamageAccumulator());
@ -611,7 +481,7 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
if (energy > 0) {
ConcurrentBitSet bs = destructionMap.computeIfAbsent(
ck.pos,
posKey -> new ConcurrentBitSet()
posKey -> new ConcurrentBitSet(BITSET_SIZE)
);
bs.set(bitIndex);
}

View File

@ -1,11 +1,31 @@
package com.hbm.interfaces;
/**
* Interface for procedural explosions.
* @author mlbv
*/
public interface IExplosionRay {
boolean isComplete();
/**
* Called every tick. Caches the chunks affected by the explosion.
* All heavy calculations are recommended to be done off the main thread.
* @param processTimeMs maximum time to process in this tick
*/
void cacheChunksTick(int processTimeMs);
void cacheChunksTick(int processTime);
void destructionTick(int processTime);
/**
* Called every tick to apply block destruction to the affected chunks.
* @param processTimeMs maximum time to process in this tick
*/
void destructionTick(int processTimeMs);
/**
* Immediately cancels the explosion.
*/
void cancel();
/**
* @return true if the explosion is finished or cancelled.
*/
boolean isComplete();
}

View File

@ -41,7 +41,7 @@ public class GUIMachineChemicalPlant extends GuiInfoContainer {
protected void mouseClicked(int x, int y, int button) {
super.mouseClicked(x, y, button);
if(this.checkClick(x, y, 7, 125, 18, 18)) GUIScreenRecipeSelector.openSelector(ChemicalPlantRecipes.INSTANCE, chemplant, "", 0, this);
if(this.checkClick(x, y, 7, 125, 18, 18)) GUIScreenRecipeSelector.openSelector(ChemicalPlantRecipes.INSTANCE, chemplant, chemplant.chemplantModule.recipe, 0, this);
}
@Override

View File

@ -1,25 +1,45 @@
package com.hbm.inventory.gui;
import java.util.ArrayList;
import java.util.List;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import com.hbm.interfaces.IControlReceiver;
import com.hbm.inventory.recipes.loader.GenericRecipe;
import com.hbm.inventory.recipes.loader.GenericRecipes;
import com.hbm.lib.RefStrings;
import cpw.mods.fml.common.FMLCommonHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.GuiTextField;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.util.ResourceLocation;
public class GUIScreenRecipeSelector extends GuiScreen {
protected static final ResourceLocation texture = new ResourceLocation(RefStrings.MODID + ":textures/gui/processing/gui_recipe_selector.png");
//basic GUI setup
protected int xSize = 176;
protected int ySize = 132;
protected int guiLeft;
protected int guiTop;
// search crap
protected GenericRecipes recipeSet;
protected List<GenericRecipe> recipes = new ArrayList();
protected GuiTextField search;
protected int pageIndex;
protected int size;
protected String selection;
// callback
protected int index;
protected IControlReceiver tile;
protected GuiScreen previousScreen;
public static void openSelector(GenericRecipes recipeSet, IControlReceiver tile, String selection, int index, GuiScreen previousScreen) {
@ -27,7 +47,13 @@ public class GUIScreenRecipeSelector extends GuiScreen {
}
public GUIScreenRecipeSelector(GenericRecipes recipeSet, IControlReceiver tile, String selection, int index, GuiScreen previousScreen) {
this.recipeSet = recipeSet;
this.tile = tile;
this.selection = selection;
this.index = index;
this.previousScreen = previousScreen;
regenerateRecipes();
}
@Override
@ -35,6 +61,41 @@ public class GUIScreenRecipeSelector extends GuiScreen {
super.initGui();
this.guiLeft = (this.width - this.xSize) / 2;
this.guiTop = (this.height - this.ySize) / 2;
Keyboard.enableRepeatEvents(true);
this.search = new GuiTextField(this.fontRendererObj, guiLeft + 28, guiTop + 111, 102, 12);
this.search.setTextColor(-1);
this.search.setDisabledTextColour(-1);
this.search.setEnableBackgroundDrawing(false);
this.search.setMaxStringLength(32);
}
private void regenerateRecipes() {
this.recipes.clear();
this.recipes.addAll(recipeSet.recipeOrderedList);
resetPaging();
}
private void search(String search) {
this.recipes.clear();
if(search.isEmpty()) {
this.recipes.addAll(recipeSet.recipeOrderedList);
} else {
for(Object o : recipeSet.recipeOrderedList) {
GenericRecipe recipe = (GenericRecipe) o;
if(recipe.matchesSearch(search)) this.recipes.add(recipe);
}
}
resetPaging();
}
private void resetPaging() {
this.pageIndex = 0;
this.size = Math.max(0, (int)Math.ceil((this.recipes.size() - 40) / 8D));
}
@Override
@ -44,6 +105,16 @@ public class GUIScreenRecipeSelector extends GuiScreen {
GL11.glDisable(GL11.GL_LIGHTING);
this.drawGuiContainerForegroundLayer(mouseX, mouseY);
GL11.glEnable(GL11.GL_LIGHTING);
this.handleScroll();
}
protected void handleScroll() {
if(!Mouse.isButtonDown(0) && !Mouse.isButtonDown(1) && Mouse.next()) {
int scroll = Mouse.getEventDWheel();
if(scroll > 0 && this.pageIndex > 0) this.pageIndex--;
if(scroll < 0 && this.pageIndex < this.size) this.pageIndex++;
}
}
private void drawGuiContainerForegroundLayer(int x, int y) {
@ -54,14 +125,53 @@ public class GUIScreenRecipeSelector extends GuiScreen {
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
Minecraft.getMinecraft().getTextureManager().bindTexture(texture);
drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize);
for(int i = pageIndex * 8; i < pageIndex * 8 + 40; i++) {
if(i >= recipes.size()) break;
int ind = i - pageIndex * 8;
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
RenderHelper.enableGUIStandardItemLighting();
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
GenericRecipe recipe = recipes.get(i);
FontRenderer font = recipe.getIcon().getItem().getFontRenderer(recipe.getIcon());
if(font == null) font = fontRendererObj;
itemRender.zLevel = 100.0F;
itemRender.renderItemAndEffectIntoGUI(font, this.mc.getTextureManager(), recipe.getIcon(), guiLeft + 8 + 18 * (ind % 8), guiTop + 18 + 18 * (ind / 8));
itemRender.zLevel = 0.0F;
GL11.glEnable(GL11.GL_ALPHA_TEST);
GL11.glDisable(GL11.GL_LIGHTING);
this.mc.getTextureManager().bindTexture(texture);
if(recipe.name.equals(this.selection))
this.drawTexturedModalRect(guiLeft + 7 + 18 * (ind % 8), guiTop + 17 + 18 * (ind / 8), 192, 0, 18, 18);
}
}
@Override
protected void keyTyped(char typedChar, int keyCode) {
if(this.search.textboxKeyTyped(typedChar, keyCode)) {
search(this.search.getText());
return;
}
if(keyCode == 1 || keyCode == this.mc.gameSettings.keyBindInventory.getKeyCode()) {
FMLCommonHandler.instance().showGuiScreen(previousScreen);
}
}
@Override
public void onGuiClosed() {
Keyboard.enableRepeatEvents(false);
}
@Override public boolean doesGuiPauseGame() { return false; }
}

View File

@ -3,6 +3,9 @@ package com.hbm.inventory.recipes;
import com.hbm.inventory.FluidStack;
import com.hbm.inventory.RecipesCommon.ComparableStack;
import com.hbm.inventory.fluid.Fluids;
import com.hbm.inventory.material.MaterialShapes;
import com.hbm.inventory.material.Mats;
import com.hbm.inventory.material.NTMMaterial;
import com.hbm.inventory.recipes.loader.GenericRecipe;
import com.hbm.inventory.recipes.loader.GenericRecipes;
import com.hbm.items.ModItems;
@ -40,5 +43,12 @@ public class ChemicalPlantRecipes extends GenericRecipes<GenericRecipe> {
pool.add(new ChanceOutput(new ItemStack(ModItems.billet_cobalt), 5));
}})
.setOutputFluids(new FluidStack(Fluids.BIOGAS, 2000)));
for(NTMMaterial mat : Mats.orderedList) {
if(mat.autogen.contains(MaterialShapes.CASTPLATE)) this.register(new GenericRecipe(mat.getUnlocalizedName() + ".plate").setup(60, 100).setOutputItems(new ChanceOutput(new ItemStack(ModItems.plate_cast, 1, mat.id))));
if(mat.autogen.contains(MaterialShapes.WELDEDPLATE)) this.register(new GenericRecipe(mat.getUnlocalizedName() + ".weld").setup(60, 100).setOutputItems(new ChanceOutput(new ItemStack(ModItems.plate_welded, 1, mat.id))));
if(mat.autogen.contains(MaterialShapes.DENSEWIRE)) this.register(new GenericRecipe(mat.getUnlocalizedName() + ".wire").setup(60, 100).setOutputItems(new ChanceOutput(new ItemStack(ModItems.wire_dense, 1, mat.id))));
if(mat.autogen.contains(MaterialShapes.MECHANISM)) this.register(new GenericRecipe(mat.getUnlocalizedName() + ".mechanism").setup(60, 100).setOutputItems(new ChanceOutput(new ItemStack(ModItems.part_mechanism, 1, mat.id))));
}
}
}

View File

@ -1,5 +1,7 @@
package com.hbm.inventory.recipes.loader;
import java.util.Locale;
import com.hbm.inventory.FluidStack;
import com.hbm.inventory.RecipesCommon.AStack;
import com.hbm.inventory.recipes.loader.GenericRecipes.ChanceOutput;
@ -21,7 +23,7 @@ public class GenericRecipe {
public FluidStack[] outputFluid;
public int duration;
public long power;
public ItemStack icon;
protected ItemStack icon;
public boolean writeIcon = false;
public boolean customLocalization = false;
@ -32,6 +34,7 @@ public class GenericRecipe {
public GenericRecipe setDuration(int duration) { this.duration = duration; return this; }
public GenericRecipe setPower(long power) { this.power = power; return this; }
public GenericRecipe setup(int duration, long power) { return this.setDuration(duration).setPower(power); }
public GenericRecipe setupNamed(int duration, long power) { return this.setDuration(duration).setPower(power).setNamed(); }
public GenericRecipe setIcon(ItemStack icon) { this.icon = icon; this.writeIcon = true; return this; }
public GenericRecipe setIcon(Item item, int meta) { return this.setIcon(new ItemStack(item, 1, meta)); }
public GenericRecipe setIcon(Item item) { return this.setIcon(new ItemStack(item)); }
@ -60,10 +63,11 @@ public class GenericRecipe {
}
public String getName() {
if(customLocalization) {
return I18nUtil.resolveKey(name);
}
if(customLocalization) return I18nUtil.resolveKey(name);
return this.getIcon().getDisplayName();
}
public boolean matchesSearch(String substring) {
return getName().toLowerCase(Locale.US).contains(substring.toLowerCase(Locale.US));
}
}

View File

@ -72,6 +72,7 @@ public abstract class GenericRecipes<T extends GenericRecipe> extends Serializab
if(this.hasPower()) recipe.setPower(obj.get("power").getAsLong());
if(obj.has("icon")) recipe.setIcon(this.readItemStack(obj.get("icon").getAsJsonArray()));
if(obj.has("named") && obj.get("named").getAsBoolean()) recipe.setNamed();
readExtraData(element, recipe);
@ -117,6 +118,8 @@ public abstract class GenericRecipes<T extends GenericRecipe> extends Serializab
this.writeItemStack(recipe.icon, writer);
}
if(recipe.customLocalization) writer.name("named").value(true);
writeExtraData(recipe, writer);
}

View File

@ -0,0 +1,32 @@
package com.hbm.util;
import net.minecraft.world.ChunkCoordIntPair;
import java.util.Objects;
/**
* Unique identifier for sub-chunks.
* @Author mlbv
*/
public class ChunkKey {
public final ChunkCoordIntPair pos;
public final int subY;
public ChunkKey(int cx, int cz, int sy) {
this.pos = new ChunkCoordIntPair(cx, cz);
this.subY = sy;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ChunkKey)) return false;
ChunkKey k = (ChunkKey) o;
return subY == k.subY && pos.equals(k.pos);
}
@Override
public int hashCode() {
return Objects.hash(pos.chunkXPos, pos.chunkZPos, subY);
}
}

View File

@ -0,0 +1,70 @@
package com.hbm.util;
import java.util.concurrent.atomic.AtomicLongArray;
import java.util.concurrent.atomic.LongAdder;
public class ConcurrentBitSet {
private final AtomicLongArray words;
private final int size;
private final LongAdder bitCount = new LongAdder();
public ConcurrentBitSet(int size) {
this.size = size;
int wordCount = (size + 63) >>> 6;
this.words = new AtomicLongArray(wordCount);
}
public void set(int bit) {
if (bit < 0 || bit >= size) return;
int wordIndex = bit >>> 6;
long mask = 1L << (bit & 63);
while (true) {
long oldWord = words.get(wordIndex);
long newWord = oldWord | mask;
if (oldWord == newWord) return;
if (words.compareAndSet(wordIndex, oldWord, newWord)) {
bitCount.increment();
return;
}
}
}
public void clear(int bit) {
if (bit < 0 || bit >= size) return;
int wordIndex = bit >>> 6;
long mask = ~(1L << (bit & 63));
while (true) {
long oldWord = words.get(wordIndex);
long newWord = oldWord & mask;
if (oldWord == newWord) return;
if (words.compareAndSet(wordIndex, oldWord, newWord)) {
bitCount.decrement();
return;
}
}
}
public int nextSetBit(int from) {
if (from < 0) from = 0;
int wordIndex = from >>> 6;
if (wordIndex >= words.length()) return -1;
long word = words.get(wordIndex) & (~0L << (from & 63));
while (true) {
if (word != 0) {
int idx = (wordIndex << 6) + Long.numberOfTrailingZeros(word);
return (idx < size) ? idx : -1;
}
wordIndex++;
if (wordIndex >= words.length()) return -1;
word = words.get(wordIndex);
}
}
public boolean isEmpty() {
return bitCount.sum() == 0;
}
public long cardinality() {
return bitCount.sum();
}
}

View File

@ -0,0 +1,120 @@
package com.hbm.util;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.world.ChunkCoordIntPair;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A snapshot of a 16×16×16 sub-chunk.
* @Author mlbv
*/
public class SubChunkSnapshot {
/**
* A sub-chunk that contains only air.
*/
public static final SubChunkSnapshot EMPTY = new SubChunkSnapshot(new Block[]{Blocks.air}, null);
private final Block[] palette;
private final short[] data;
private SubChunkSnapshot(Block[] p, short[] d) {
this.palette = p;
this.data = d;
}
/**
* Creates a SubChunkSnapshot from a loaded chunk.
*
* @param world
* The World instance from which to retrieve the chunk.
* @param cpos
* The ChunkCoordIntPair identifying the chunk coordinates (x, z).
* @param subY
* The vertical sub-chunk index (015) within the chunk.
* @return
* A SubChunkSnapshot containing the palette and block data for the sub-chunk,
* or SubChunkSnapshot.EMPTY if the region is unloaded or contains only air.
*/
public static SubChunkSnapshot getSnapshot(World world, ChunkCoordIntPair cpos, int subY) {
if (!world.getChunkProvider().chunkExists(cpos.chunkXPos, cpos.chunkZPos)) {
return SubChunkSnapshot.EMPTY;
}
return getOrLoadSnapshot(world, cpos, subY);
}
/**
* Creates a SubChunkSnapshot.
*
* @param world
* The World instance from which to retrieve the chunk.
* @param cpos
* The ChunkCoordIntPair identifying the chunk coordinates (x, z).
* @param subY
* The vertical sub-chunk index (015) within the chunk.
* @return
* A SubChunkSnapshot containing the palette and block data for the sub-chunk,
* or SubChunkSnapshot.EMPTY if the region contains only air.
*/
public static SubChunkSnapshot getOrLoadSnapshot(World world, ChunkCoordIntPair cpos, int subY){
Chunk chunk = world.getChunkFromChunkCoords(cpos.chunkXPos, cpos.chunkZPos);
ExtendedBlockStorage ebs = chunk.getBlockStorageArray()[subY];
if (ebs == null || ebs.isEmpty()) return SubChunkSnapshot.EMPTY;
short[] data = new short[16 * 16 * 16];
List<Block> palette = new ArrayList<>();
palette.add(Blocks.air);
Map<Block, Short> idxMap = new HashMap<>();
idxMap.put(Blocks.air, (short) 0);
boolean allAir = true;
for (int ly = 0; ly < 16; ly++) {
for (int lz = 0; lz < 16; lz++) {
for (int lx = 0; lx < 16; lx++) {
Block block = ebs.getBlockByExtId(lx, ly, lz);
int idx;
if (block == Blocks.air) {
idx = 0;
} else {
allAir = false;
Short e = idxMap.get(block);
if (e == null) {
idxMap.put(block, (short) palette.size());
palette.add(block);
idx = palette.size() - 1;
} else {
idx = e;
}
}
data[(ly << 8) | (lz << 4) | lx] = (short) idx;
}
}
}
if (allAir) return SubChunkSnapshot.EMPTY;
return new SubChunkSnapshot(palette.toArray(new Block[0]), data);
}
/**
* Retrieves the Block at the specified local coordinates within this sub-chunk snapshot.
*
* @param x
* The local x-coordinate within the sub-chunk (015).
* @param y
* The local y-coordinate within the sub-chunk (015).
* @param z
* The local z-coordinate within the sub-chunk (015).
* @return
* The Block instance at the given position.
*/
public Block getBlock(int x, int y, int z) {
if (this == EMPTY || data == null) return Blocks.air;
short idx = data[(y << 8) | (z << 4) | x];
return (idx >= 0 && idx < palette.length) ? palette[idx] : Blocks.air;
}
}