hytale-server/com/hypixel/hytale/server/spawning/local/LocalSpawnControllerSystem....

343 lines
17 KiB
Java

package com.hypixel.hytale.server.spawning.local;
import com.hypixel.hytale.builtin.weather.components.WeatherTracker;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.ResourceType;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.spatial.SpatialResource;
import com.hypixel.hytale.component.system.tick.TickingSystem;
import com.hypixel.hytale.function.function.TriIntObjectDoubleToByteFunction;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.protocol.BlockMaterial;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.spawning.SpawningPlugin;
import com.hypixel.hytale.server.spawning.assets.spawns.LightType;
import com.hypixel.hytale.server.spawning.beacons.LegacySpawnBeaconEntity;
import com.hypixel.hytale.server.spawning.util.LightRangePredicate;
import com.hypixel.hytale.server.spawning.wrappers.BeaconSpawnWrapper;
import it.unimi.dsi.fastutil.Pair;
import it.unimi.dsi.fastutil.objects.Object2ByteMap;
import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectList;
import java.util.List;
import java.util.logging.Level;
import javax.annotation.Nonnull;
public class LocalSpawnControllerSystem extends TickingSystem<EntityStore> {
public static final double RUN_FREQUENCY_SECONDS = 5.0;
private static final int LIGHT_LEVEL_EVALUATION_RADIUS = 4;
private final Archetype<EntityStore> controllerArchetype;
private final ComponentType<EntityStore, LocalSpawnController> spawnControllerComponentType;
private final ComponentType<EntityStore, TransformComponent> transformComponentype;
private final ComponentType<EntityStore, WeatherTracker> weatherTrackerComponentType;
private final ComponentType<EntityStore, LocalSpawnBeacon> localSpawnBeaconComponentType;
private final ComponentType<EntityStore, LegacySpawnBeaconEntity> spawnBeaconComponentType;
private final ResourceType<EntityStore, LocalSpawnState> localSpawnStateResourceType;
private final ResourceType<EntityStore, SpatialResource<Ref<EntityStore>, EntityStore>> beaconSpatialComponent;
public LocalSpawnControllerSystem(
ComponentType<EntityStore, LocalSpawnController> spawnControllerComponentType,
ComponentType<EntityStore, TransformComponent> transformComponentype,
ComponentType<EntityStore, WeatherTracker> weatherTrackerComponentType,
ComponentType<EntityStore, LocalSpawnBeacon> localSpawnBeaconComponentType,
ComponentType<EntityStore, LegacySpawnBeaconEntity> spawnBeaconComponentType,
ResourceType<EntityStore, LocalSpawnState> localSpawnStateResourceType,
ResourceType<EntityStore, SpatialResource<Ref<EntityStore>, EntityStore>> beaconSpatialComponent
) {
this.spawnControllerComponentType = spawnControllerComponentType;
this.transformComponentype = transformComponentype;
this.weatherTrackerComponentType = weatherTrackerComponentType;
this.localSpawnBeaconComponentType = localSpawnBeaconComponentType;
this.spawnBeaconComponentType = spawnBeaconComponentType;
this.localSpawnStateResourceType = localSpawnStateResourceType;
this.beaconSpatialComponent = beaconSpatialComponent;
this.controllerArchetype = Archetype.of(spawnControllerComponentType, PlayerRef.getComponentType());
}
@Override
public void tick(float dt, int systemIndex, @Nonnull Store<EntityStore> store) {
LocalSpawnState localSpawnState = store.getResource(this.localSpawnStateResourceType);
List<Ref<EntityStore>> controllers = localSpawnState.getLocalControllerList();
store.forEachChunk(this.controllerArchetype, (archetypeChunk, commandBuffer) -> {
for (int indexx = 0; indexx < archetypeChunk.size(); indexx++) {
LocalSpawnController spawnControllerComponentx = archetypeChunk.getComponent(indexx, this.spawnControllerComponentType);
assert spawnControllerComponentx != null;
if (spawnControllerComponentx.tickTimeToNextRunSeconds(dt)) {
controllers.add(archetypeChunk.getReferenceTo(indexx));
}
}
});
if (!controllers.isEmpty()) {
World world = store.getExternalData().getWorld();
List<LegacySpawnBeaconEntity> pendingSpawns = localSpawnState.getLocalPendingSpawns();
ObjectList<Ref<EntityStore>> existingBeacons = SpatialResource.getThreadLocalReferenceList();
for (int index = 0; index < controllers.size(); index++) {
Ref<EntityStore> reference = controllers.get(index);
LocalSpawnController spawnControllerComponent = store.getComponent(reference, this.spawnControllerComponentType);
assert spawnControllerComponent != null;
PlayerRef playerRefComponent = store.getComponent(reference, PlayerRef.getComponentType());
assert playerRefComponent != null;
SpawningPlugin.get().getLogger().at(Level.FINE).log("Running local spawn controller for player %s", playerRefComponent.getUsername());
TransformComponent transformComponent = store.getComponent(reference, this.transformComponentype);
assert transformComponent != null;
WeatherTracker weatherTrackerComponent = store.getComponent(reference, this.weatherTrackerComponentType);
assert weatherTrackerComponent != null;
weatherTrackerComponent.updateEnvironment(transformComponent, store);
int environmentIndex = weatherTrackerComponent.getEnvironmentId();
List<BeaconSpawnWrapper> possibleBeacons = SpawningPlugin.get().getBeaconSpawnsForEnvironment(environmentIndex);
if (possibleBeacons != null && !possibleBeacons.isEmpty()) {
BeaconSpawnWrapper firstBeacon = possibleBeacons.getFirst();
double largestDistance = firstBeacon.getBeaconRadius();
int[] firstRange = firstBeacon.getSpawn().getYRange();
int lowestY = firstRange[0];
int highestY = firstRange[1];
for (int i = 1; i < possibleBeacons.size(); i++) {
BeaconSpawnWrapper beacon = possibleBeacons.get(i);
double radius = beacon.getBeaconRadius();
if (radius > largestDistance) {
largestDistance = radius;
}
int[] yRange = beacon.getSpawn().getYRange();
if (yRange[0] < lowestY) {
lowestY = yRange[0];
}
if (yRange[1] > highestY) {
highestY = yRange[1];
}
}
largestDistance *= 2.0;
Vector3d position = transformComponent.getPosition();
double largestDistanceSquared = largestDistance * largestDistance;
int yDistance = Math.abs(lowestY) + Math.abs(highestY);
int y = MathUtil.floor(position.getY());
int minY = Math.max(0, y - yDistance);
int maxY = Math.min(319, y + yDistance);
SpatialResource<Ref<EntityStore>, EntityStore> spatialResource = store.getResource(this.beaconSpatialComponent);
spatialResource.getSpatialStructure().ordered(position, largestDistance, existingBeacons);
WorldTimeResource worldTimeResource = store.getResource(WorldTimeResource.getResourceType());
double sunlightFactor = worldTimeResource.getSunlightFactor();
int xPos = MathUtil.floor(position.getX());
int yPos = MathUtil.floor(position.getY());
int zPos = MathUtil.floor(position.getZ());
Object2ByteOpenHashMap<LightType> averageLightValues = new Object2ByteOpenHashMap();
averageLightValues.defaultReturnValue((byte)-1);
label134:
for (int i = 0; i < possibleBeacons.size(); i++) {
BeaconSpawnWrapper possibleBeacon = possibleBeacons.get(i);
if (possibleBeacon.spawnParametersMatch(store)) {
for (int j = 0; j < existingBeacons.size(); j++) {
Ref<EntityStore> existingBeaconReference = (Ref<EntityStore>)existingBeacons.get(j);
LegacySpawnBeaconEntity existingBeaconComponent = store.getComponent(existingBeaconReference, this.spawnBeaconComponentType);
assert existingBeaconComponent != null;
TransformComponent existingBeaconTransformComponent = store.getComponent(existingBeaconReference, this.transformComponentype);
assert existingBeaconTransformComponent != null;
double existingY = existingBeaconTransformComponent.getPosition().getY();
if (!(existingY > maxY) && !(existingY < minY)) {
int existingBeaconIndex = existingBeaconComponent.getSpawnWrapper().getSpawnIndex();
if (existingBeaconIndex == possibleBeacon.getSpawnIndex()) {
continue label134;
}
}
}
for (int j = 0; j < pendingSpawns.size(); j++) {
LegacySpawnBeaconEntity pending = pendingSpawns.get(j);
Ref<EntityStore> pendingReference = pending.getReference();
TransformComponent pendingTransformComponent = store.getComponent(pendingReference, TransformComponent.getComponentType());
assert pendingTransformComponent != null;
Vector3d pendingPosition = pendingTransformComponent.getPosition();
double pendingY = pendingPosition.getY();
if (!(pendingY > maxY) && !(pendingY < minY)) {
double xDiff = position.x - pendingPosition.x;
double zDiff = position.z - pendingPosition.z;
double distSquared = xDiff * xDiff + zDiff * zDiff;
if (!(distSquared > largestDistanceSquared)) {
int existingBeaconIndex = pending.getSpawnWrapper().getSpawnIndex();
if (existingBeaconIndex == possibleBeacon.getSpawnIndex()) {
continue label134;
}
}
}
}
if (spawnLightLevelMatches(world, xPos, yPos, zPos, sunlightFactor, possibleBeacon, averageLightValues)) {
Pair<Ref<EntityStore>, LegacySpawnBeaconEntity> beaconEntityPair = LegacySpawnBeaconEntity.create(
possibleBeacon, transformComponent.getPosition(), transformComponent.getRotation(), store
);
Ref<EntityStore> beaconRef = (Ref<EntityStore>)beaconEntityPair.first();
if (beaconRef != null && beaconRef.isValid()) {
store.ensureComponent(beaconRef, this.localSpawnBeaconComponentType);
SpawningPlugin.get()
.getLogger()
.at(Level.FINE)
.log(
"Placed spawn beacon of type %s at position %s for player %s",
possibleBeacon.getSpawn().getId(),
position,
playerRefComponent.getUsername()
);
pendingSpawns.add((LegacySpawnBeaconEntity)beaconEntityPair.second());
}
}
}
}
existingBeacons.clear();
averageLightValues.clear();
spawnControllerComponent.setTimeToNextRunSeconds(5.0);
} else {
spawnControllerComponent.setTimeToNextRunSeconds(5.0);
}
}
controllers.clear();
pendingSpawns.clear();
}
}
private static boolean spawnLightLevelMatches(
@Nonnull World world, int x, int y, int z, double sunlightFactor, @Nonnull BeaconSpawnWrapper wrapper, @Nonnull Object2ByteMap<LightType> averageValues
) {
LightRangePredicate lightRangePredicate = wrapper.getLightRangePredicate();
if (lightRangePredicate.isTestLightValue()) {
byte lightValue = getCachedAverageLightValue(
LightType.Light,
world,
x,
y,
z,
sunlightFactor,
(_x, _y, _z, _chunk, _sunlightFactor) -> LightRangePredicate.calculateLightValue(_chunk, _x, _y, _z, _sunlightFactor),
averageValues
);
if (!lightRangePredicate.testLight(lightValue)) {
return false;
}
}
if (lightRangePredicate.isTestSkyLightValue()) {
byte lightValue = getCachedAverageLightValue(
LightType.SkyLight, world, x, y, z, sunlightFactor, (_x, _y, _z, _chunk, _sunlightFactor) -> _chunk.getSkyLight(_x, _y, _z), averageValues
);
if (!lightRangePredicate.testSkyLight(lightValue)) {
return false;
}
}
if (lightRangePredicate.isTestSunlightValue()) {
byte lightValue = getCachedAverageLightValue(
LightType.Sunlight,
world,
x,
y,
z,
sunlightFactor,
(_x, _y, _z, _chunk, _sunlightFactor) -> (byte)(_chunk.getSkyLight(_x, _y, _z) * _sunlightFactor),
averageValues
);
if (!lightRangePredicate.testSunlight(lightValue)) {
return false;
}
}
if (lightRangePredicate.isTestRedLightValue()) {
byte lightValue = getCachedAverageLightValue(
LightType.RedLight, world, x, y, z, sunlightFactor, (_x, _y, _z, _chunk, _sunlightFactor) -> _chunk.getRedBlockLight(_x, _y, _z), averageValues
);
if (!lightRangePredicate.testRedLight(lightValue)) {
return false;
}
}
if (lightRangePredicate.isTestGreenLightValue()) {
byte lightValue = getCachedAverageLightValue(
LightType.GreenLight, world, x, y, z, sunlightFactor, (_x, _y, _z, _chunk, _sunlightFactor) -> _chunk.getGreenBlockLight(_x, _y, _z), averageValues
);
if (!lightRangePredicate.testGreenLight(lightValue)) {
return false;
}
}
if (lightRangePredicate.isTestBlueLightValue()) {
byte lightValue = getCachedAverageLightValue(
LightType.BlueLight, world, x, y, z, sunlightFactor, (_x, _y, _z, _chunk, _sunlightFactor) -> _chunk.getBlueBlockLight(_x, _y, _z), averageValues
);
return lightRangePredicate.testBlueLight(lightValue);
} else {
return true;
}
}
private static byte getCachedAverageLightValue(
LightType lightType,
@Nonnull World world,
int x,
int y,
int z,
double sunlightFactor,
@Nonnull TriIntObjectDoubleToByteFunction<BlockChunk> valueCalculator,
@Nonnull Object2ByteMap<LightType> averageValues
) {
byte cachedValue = averageValues.getByte(lightType);
if (cachedValue < 0) {
int counted = 0;
int total = 0;
for (int xOffset = x - 4; xOffset < x + 4; xOffset++) {
for (int zOffset = z - 4; zOffset < z + 4; zOffset++) {
WorldChunk chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(xOffset, zOffset));
if (chunk != null) {
BlockChunk blockChunk = chunk.getBlockChunk();
for (int yOffset = y; yOffset < y + 4; yOffset++) {
int blockId = chunk.getBlock(xOffset, yOffset, zOffset);
if (blockId == 0 || BlockType.getAssetMap().getAsset(blockId).getMaterial() != BlockMaterial.Solid) {
counted++;
total += valueCalculator.apply(xOffset, yOffset, zOffset, blockChunk, sunlightFactor);
}
}
}
}
}
cachedValue = counted > 0 ? (byte)((float)total / counted) : 0;
averageValues.put(lightType, cachedValue);
}
return cachedValue;
}
}