Add beacon activation logic from merge 60

Closes !60
This commit is contained in:
James Seibel
2024-07-02 17:50:25 -05:00
parent 3b4a0ff4bc
commit e90c7cd8a0
5 changed files with 176 additions and 90 deletions
@@ -141,7 +141,7 @@ public class ServerApi
//=======================//
public void serverChunkLoadEvent(IChunkWrapper chunkWrapper, ILevelWrapper level) { SharedApi.INSTANCE.applyChunkUpdate(chunkWrapper, level, false); }
public void serverChunkSaveEvent(IChunkWrapper chunkWrapper, ILevelWrapper level) { SharedApi.INSTANCE.applyChunkUpdate(chunkWrapper, level, false); }
public void serverChunkSaveEvent(IChunkWrapper chunkWrapper, ILevelWrapper level) { SharedApi.INSTANCE.applyChunkUpdate(chunkWrapper, level, true); }
@@ -311,6 +311,18 @@ public class SharedApi
}
// having a list of the nearby chunks is needed for lighting and beacon generation
ArrayList<IChunkWrapper> nearbyChunkList;
if (neighbourChunkList != null)
{
nearbyChunkList = neighbourChunkList;
}
else
{
nearbyChunkList = new ArrayList<>(1);
nearbyChunkList.add(chunkWrapper);
}
// Save or populate the chunk wrapper's lighting
// this is done so we don't have to worry about MC unloading the lighting data for this chunk
@@ -330,24 +342,13 @@ public class SharedApi
else
{
// generate the chunk's lighting, using neighboring chunks if present
ArrayList<IChunkWrapper> nearbyChunkList;
if (neighbourChunkList != null)
{
nearbyChunkList = neighbourChunkList;
}
else
{
nearbyChunkList = new ArrayList<>(1);
nearbyChunkList.add(chunkWrapper);
}
DhLightingEngine.INSTANCE.lightChunk(chunkWrapper, nearbyChunkList, dhLevel.hasSkyLight() ? 15 : 0);
}
// get this chunk's active beacons
List<BeaconBeamDTO> beaconBeamList = chunkWrapper.getAllActiveBeacons();
List<BeaconBeamDTO> beaconBeamList = chunkWrapper.getAllActiveBeacons(nearbyChunkList);
dhLevel.setBeaconBeamsForChunk(chunkWrapper.getChunkPos(), beaconBeamList);
@@ -0,0 +1,117 @@
package com.seibel.distanthorizons.core.generation;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashSet;
/** holds adjacent chunks without having to create new Pos objects */
public class AdjacentChunkHolder
{
final IChunkWrapper[] chunkArray = new IChunkWrapper[9];
//==============//
// constructors //
//==============//
public AdjacentChunkHolder(IChunkWrapper centerWrapper) { this.chunkArray[4] = centerWrapper; }
public AdjacentChunkHolder(IChunkWrapper centerWrapper, @NotNull ArrayList<IChunkWrapper> nearbyChunkList)
{
this.chunkArray[4] = centerWrapper;
DhChunkPos centerChunkPos = centerWrapper.getChunkPos();
// generate the list of chunk pos we need,
// currently a 3x3 grid
HashSet<DhChunkPos> requestedAdjacentPositions = new HashSet<>(9);
for (int xOffset = -1; xOffset <= 1; xOffset++)
{
for (int zOffset = -1; zOffset <= 1; zOffset++)
{
DhChunkPos adjacentPos = new DhChunkPos(centerChunkPos.x + xOffset, centerChunkPos.z + zOffset);
requestedAdjacentPositions.add(adjacentPos);
}
}
for (int chunkIndex = 0; chunkIndex < nearbyChunkList.size(); chunkIndex++) // using iterators in high traffic areas can cause GC issues due to allocating a bunch of iterators, use an indexed for-loop instead
{
IChunkWrapper chunk = nearbyChunkList.get(chunkIndex);
if (chunk != null && requestedAdjacentPositions.contains(chunk.getChunkPos()))
{
// remove the newly found position
requestedAdjacentPositions.remove(chunk.getChunkPos());
// add the adjacent chunk
this.add(chunk);
}
if (requestedAdjacentPositions.isEmpty())
{
// we found every chunk we needed, we don't need to keep iterating
break;
}
}
}
//=========//
// methods //
//=========//
public void add(IChunkWrapper centerWrapper)
{
DhChunkPos centerPos = this.chunkArray[4].getChunkPos();
DhChunkPos offsetPos = centerWrapper.getChunkPos();
int offsetX = offsetPos.x - centerPos.x;
if (offsetX < -1 || offsetX > 1)
{
return;
}
int offsetZ = offsetPos.z - centerPos.z;
if (offsetZ < -1 || offsetZ > 1)
{
return;
}
// equivalent to 4 + offsetX + (offsetZ * 3).
this.chunkArray[4 + offsetX + offsetZ + (offsetZ << 1)] = centerWrapper;
}
public IChunkWrapper getByBlockPos(int blockX, int blockZ)
{
int chunkX = BitShiftUtil.divideByPowerOfTwo(blockX, 4);
int chunkZ = BitShiftUtil.divideByPowerOfTwo(blockZ, 4);
IChunkWrapper centerChunk = this.chunkArray[4];
DhChunkPos centerPos = centerChunk.getChunkPos();
if (centerPos.x == chunkX && centerPos.z == chunkZ)
{
return centerChunk;
}
int offsetX = chunkX - centerPos.x;
if (offsetX < -1 || offsetX > 1)
{
return null;
}
int offsetZ = chunkZ - centerPos.z;
if (offsetZ < -1 || offsetZ > 1)
{
return null;
}
// equivalent to 4 + offsetX + (offsetZ * 3).
return this.chunkArray[4 + offsetX + offsetZ + (offsetZ << 1)];
}
}
@@ -306,64 +306,6 @@ public class DhLightingEngine
}
/** holds the adjacent chunks without having to create new Pos objects */
private static class AdjacentChunkHolder
{
final IChunkWrapper[] chunkArray = new IChunkWrapper[9];
public AdjacentChunkHolder(IChunkWrapper centerWrapper) { this.chunkArray[4] = centerWrapper; }
public void add(IChunkWrapper centerWrapper)
{
DhChunkPos centerPos = this.chunkArray[4].getChunkPos();
DhChunkPos offsetPos = centerWrapper.getChunkPos();
int offsetX = offsetPos.x - centerPos.x;
if (offsetX < -1 || offsetX > 1)
{
return;
}
int offsetZ = offsetPos.z - centerPos.z;
if (offsetZ < -1 || offsetZ > 1)
{
return;
}
// equivalent to 4 + offsetX + (offsetZ * 3).
this.chunkArray[4 + offsetX + offsetZ + (offsetZ << 1)] = centerWrapper;
}
public IChunkWrapper getByBlockPos(int blockX, int blockZ)
{
int chunkX = BitShiftUtil.divideByPowerOfTwo(blockX, 4);
int chunkZ = BitShiftUtil.divideByPowerOfTwo(blockZ, 4);
IChunkWrapper centerChunk = this.chunkArray[4];
DhChunkPos centerPos = centerChunk.getChunkPos();
if (centerPos.x == chunkX && centerPos.z == chunkZ)
{
return centerChunk;
}
int offsetX = chunkX - centerPos.x;
if (offsetX < -1 || offsetX > 1)
{
return null;
}
int offsetZ = chunkZ - centerPos.z;
if (offsetZ < -1 || offsetZ > 1)
{
return null;
}
// equivalent to 4 + offsetX + (offsetZ * 3).
return this.chunkArray[4 + offsetX + offsetZ + (offsetZ << 1)];
}
}
/**
* Holds all potential {@link LightPos} objects a lighting task may need.
* This is done so existing {@link LightPos} objects can be repurposed instead of destroyed,
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.core.wrapperInterfaces.chunk;
import com.seibel.distanthorizons.core.generation.AdjacentChunkHolder;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
@@ -31,6 +32,8 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
public interface IChunkWrapper extends IBindable
@@ -38,6 +41,15 @@ public interface IChunkWrapper extends IBindable
/** useful for debugging, but can slow down chunk operations quite a bit due to being called every time. */
boolean RUN_RELATIVE_POS_INDEX_VALIDATION = ModInfo.IS_DEV_BUILD;
/** should be all lowercase */
List<String> BEACON_BASE_BLOCK_NAME_LIST = Arrays.asList(
"iron_block",
"gold_block",
"diamond_block",
"emerald_block",
"netherite_block"
);
DhChunkPos getChunkPos();
@@ -246,10 +258,12 @@ public interface IChunkWrapper extends IBindable
return hash;
}
default List<BeaconBeamDTO> getAllActiveBeacons()
default List<BeaconBeamDTO> getAllActiveBeacons(ArrayList<IChunkWrapper> neighbourChunkList)
{
ArrayList<BeaconBeamDTO> beaconPosList = new ArrayList<>();
AdjacentChunkHolder adjacentChunkHolder = new AdjacentChunkHolder(this, neighbourChunkList);
// since beacons emit light we can check only the positions that are emitting light
ArrayList<DhBlockPos> blockPosList = this.getBlockLightPosList();
for (int i = 0; i < blockPosList.size(); i++)
@@ -260,7 +274,7 @@ public interface IChunkWrapper extends IBindable
IBlockStateWrapper block = this.getBlockState(relPos);
if (block.getSerialString().toLowerCase().contains("minecraft:beacon"))
{
if (isBeaconActive(relPos.x, relPos.y, relPos.z, this))
if (isBeaconActive(pos, adjacentChunkHolder))
{
BeaconBeamDTO beam = new BeaconBeamDTO(blockPosList.get(i), Color.WHITE);
beaconPosList.add(beam);
@@ -270,31 +284,43 @@ public interface IChunkWrapper extends IBindable
return beaconPosList;
}
static boolean isBeaconActive(int relBlockX, int y, int relBlockZ, IChunkWrapper chunkWrapper)
static boolean isBeaconActive(DhBlockPos beaconPos, AdjacentChunkHolder chunkHolder)
{
DhBlockPos beaconRelPos = beaconPos.convertToChunkRelativePos();
DhBlockPos baseRelPos = new DhBlockPos(0, beaconRelPos.y-1, 0);
for (int x = -1; x<= 1; x++)
{
for (int z = -1; z <= 1; z++)
{
if ((relBlockX + x < 0 || relBlockX + x >= LodUtil.CHUNK_WIDTH)
|| (relBlockZ + z < 0 || relBlockZ + z >= LodUtil.CHUNK_WIDTH))
{
// if the beacon is on the border of a chunk and all other blocks are there, assume it's complete
//TODO! Check adjacent chunk, if possible
continue;
}
String blockId = chunkWrapper.getBlockState(relBlockX + x, y -1, relBlockZ + z).getSerialString();
baseRelPos.x = beaconRelPos.x + x;
baseRelPos.z = beaconRelPos.z + z;
baseRelPos.mutateToChunkRelativePos(baseRelPos);
if (!(blockId.contains("diamond_block")
|| blockId.contains("iron_block")
|| blockId.contains("emerald_block")
|| blockId.contains("netherite_block")
|| blockId.contains("gold_block")))
{
return false;
IChunkWrapper chunk = chunkHolder.getByBlockPos(beaconPos.x + x, beaconPos.z + z);
if (chunk != null)
{
String blockSerial = chunk.getBlockState(baseRelPos.x, baseRelPos.y, baseRelPos.z).getSerialString();
boolean baseBlockFound = false;
for (int i = 0; i < BEACON_BASE_BLOCK_NAME_LIST.size(); i++)
{
String baseBlockName = BEACON_BASE_BLOCK_NAME_LIST.get(i);
if (blockSerial.contains(baseBlockName))
{
baseBlockFound = true;
break;
}
}
if (!baseBlockFound)
{
return false;
}
}
}
}
return true;
}