HyCodeYourTale
classpublicPriority 3

RoofConnectedBlockRuleSet

com.hypixel.hytale.server.core.universe.world.connectedblocks.builtin.RoofConnectedBlockRuleSet

extends ConnectedBlockRuleSet

implements StairLikeConnectedBlockRuleSet

5

Methods

5

Public Methods

5

Fields

1

Constructors

Constants

BuilderCodec<RoofConnectedBlockRuleSet>CODEC= BuilderCodec.builder(RoofConnectedBlockRuleSet.class, RoofConnectedBlockRuleSet::new) .appe...

Constructors

public
RoofConnectedBlockRuleSet()

Methods

Public Methods (5)

public
String getMaterialName()
@Nullable@Override
public
StairConnectedBlockRuleSet.StairType getStairType(int blockId)
@Override
public
boolean onlyUpdateOnPlacement()
@Override
public
com.hypixel.hytale.protocol.ConnectedBlockRuleSet toPacket(BlockTypeAssetMap<String, BlockType> assetMap)
@Nullable@Override
public
void updateCachedBlockTypes(BlockType baseBlockType, BlockTypeAssetMap<String, BlockType> assetMap)
@Override

Fields

Private/Package Fields (5)

privateStairConnectedBlockRuleSet hollow
privateString materialName
privateStairConnectedBlockRuleSet regular
privateConnectedBlockOutput topper
privateint width

Inheritance

Parent
Current
Interface
Child

Use mouse wheel to zoom, drag to pan. Click nodes to navigate.

Related Classes

Source Code

package com.hypixel.hytale.server.core.universe.world.connectedblocks.builtin;

import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.codec.codecs.simple.IntegerCodec;
import com.hypixel.hytale.codec.validation.Validators;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.protocol.ConnectedBlockRuleSetType;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockFace;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockFaceSupport;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.universe.world.connectedblocks.ConnectedBlockRuleSet;
import com.hypixel.hytale.server.core.universe.world.connectedblocks.ConnectedBlocksUtil;
import com.hypixel.hytale.server.core.util.FillerBlockUtil;
import it.unimi.dsi.fastutil.objects.ObjectIntPair;
import java.util.Map;
import java.util.Optional;
import javax.annotation.Nullable;

public class RoofConnectedBlockRuleSet extends ConnectedBlockRuleSet implements StairLikeConnectedBlockRuleSet {
   public static final BuilderCodec<RoofConnectedBlockRuleSet> CODEC = BuilderCodec.builder(RoofConnectedBlockRuleSet.class, RoofConnectedBlockRuleSet::new)
      .append(new KeyedCodec<>("Regular", StairConnectedBlockRuleSet.CODEC), (ruleSet, output) -> ruleSet.regular = output, ruleSet -> ruleSet.regular)
      .addValidator(Validators.nonNull())
      .add()
      .append(new KeyedCodec<>("Hollow", StairConnectedBlockRuleSet.CODEC), (ruleSet, output) -> ruleSet.hollow = output, ruleSet -> ruleSet.hollow)
      .add()
      .append(new KeyedCodec<>("Topper", ConnectedBlockOutput.CODEC), (ruleSet, output) -> ruleSet.topper = output, ruleSet -> ruleSet.topper)
      .add()
      .append(new KeyedCodec<>("Width", new IntegerCodec()), (ruleSet, output) -> ruleSet.width = output, ruleSet -> ruleSet.width)
      .add()
      .append(new KeyedCodec<>("MaterialName", Codec.STRING), (ruleSet, materialName) -> ruleSet.materialName = materialName, ruleSet -> ruleSet.materialName)
      .add()
      .build();
   private StairConnectedBlockRuleSet regular;
   private StairConnectedBlockRuleSet hollow;
   private ConnectedBlockOutput topper;
   private String materialName;
   private int width = 1;

   public RoofConnectedBlockRuleSet() {
   }

   private static StairConnectedBlockRuleSet.StairType getConnectedBlockStairType(
      World world, Vector3i coordinate, StairLikeConnectedBlockRuleSet currentRuleSet, int blockId, int rotation, int width
   ) {
      RotationTuple currentRotation = RotationTuple.get(rotation);
      Rotation currentYaw = currentRotation.yaw();
      Rotation currentPitch = currentRotation.pitch();
      boolean upsideDown = currentPitch != Rotation.None;
      if (upsideDown) {
         currentYaw = currentYaw.flip();
      }

      Vector3i mutablePos = new Vector3i();
      StairConnectedBlockRuleSet.StairType resultingStair = StairConnectedBlockRuleSet.StairType.STRAIGHT;
      StairConnectedBlockRuleSet.StairConnection frontConnection = StairConnectedBlockRuleSet.getInvertedCornerConnection(
         world, currentRuleSet, coordinate, mutablePos, currentYaw, upsideDown
      );
      if (frontConnection != null) {
         boolean valid = isWidthFulfilled(world, coordinate, mutablePos, frontConnection, currentYaw, blockId, rotation, width);
         if (valid) {
            resultingStair = frontConnection.getStairType(true);
         }
      }

      StairConnectedBlockRuleSet.StairConnection backConnection = StairConnectedBlockRuleSet.getCornerConnection(
         world, currentRuleSet, coordinate, mutablePos, rotation, currentYaw, upsideDown, width
      );
      if (backConnection != null) {
         boolean valid = isWidthFulfilled(world, coordinate, mutablePos, backConnection, currentYaw, blockId, rotation, width);
         if (valid) {
            resultingStair = backConnection.getStairType(false);
         }
      }

      if (resultingStair == StairConnectedBlockRuleSet.StairType.STRAIGHT) {
         Vector3i aboveCoordinate = new Vector3i(coordinate).add(0, 1, 0);
         StairConnectedBlockRuleSet.StairConnection resultingConnection = getValleyConnection(
            world, coordinate, aboveCoordinate, currentRuleSet, currentRotation, mutablePos, false, blockId, rotation, width
         );
         if (resultingConnection != null) {
            resultingStair = resultingConnection.getStairType(true);
         }
      }

      if (resultingStair == StairConnectedBlockRuleSet.StairType.STRAIGHT) {
         Vector3i belowCoordinate = new Vector3i(coordinate).add(0, -1, 0);
         StairConnectedBlockRuleSet.StairConnection resultingConnection = getValleyConnection(
            world, coordinate, belowCoordinate, currentRuleSet, currentRotation, mutablePos, true, blockId, rotation, width
         );
         if (resultingConnection != null) {
            resultingStair = resultingConnection.getStairType(false);
         }
      }

      if (upsideDown) {
         resultingStair = switch (resultingStair) {
            case CORNER_LEFT -> StairConnectedBlockRuleSet.StairType.CORNER_RIGHT;
            case CORNER_RIGHT -> StairConnectedBlockRuleSet.StairType.CORNER_LEFT;
            case INVERTED_CORNER_LEFT -> StairConnectedBlockRuleSet.StairType.INVERTED_CORNER_RIGHT;
            case INVERTED_CORNER_RIGHT -> StairConnectedBlockRuleSet.StairType.INVERTED_CORNER_LEFT;
            default -> resultingStair;
         };
      }

      return resultingStair;
   }

   private static boolean isWidthFulfilled(
      World world,
      Vector3i coordinate,
      Vector3i mutablePos,
      StairConnectedBlockRuleSet.StairConnection backConnection,
      Rotation currentYaw,
      int blockId,
      int rotation,
      int width
   ) {
      boolean valid = true;

      for (int i = 0; i < width - 1; i++) {
         mutablePos.assign(backConnection == StairConnectedBlockRuleSet.StairConnection.CORNER_LEFT ? Vector3i.WEST : Vector3i.EAST).scale(i + 1);
         currentYaw.rotateY(mutablePos, mutablePos);
         int requiredFiller = FillerBlockUtil.pack(mutablePos.x, mutablePos.y, mutablePos.z);
         mutablePos.add(coordinate.x, coordinate.y, coordinate.z);
         WorldChunk chunk = world.getChunkIfLoaded(ChunkUtil.indexChunkFromBlock(mutablePos.x, mutablePos.z));
         if (chunk != null) {
            int otherRotation = chunk.getRotationIndex(mutablePos.x, mutablePos.y, mutablePos.z);
            int otherFiller = chunk.getFiller(mutablePos.x, mutablePos.y, mutablePos.z);
            int otherBlockId = chunk.getBlock(mutablePos);
            if ((otherFiller != 0 || otherBlockId != blockId || otherRotation != rotation)
               && (otherFiller != requiredFiller || otherBlockId != blockId || otherRotation != rotation)) {
               valid = false;
               break;
            }
         }
      }

      return valid;
   }

   private static StairConnectedBlockRuleSet.StairConnection getValleyConnection(
      World world,
      Vector3i placementCoordinate,
      Vector3i checkCoordinate,
      StairLikeConnectedBlockRuleSet currentRuleSet,
      RotationTuple rotation,
      Vector3i mutablePos,
      boolean reverse,
      int blockId,
      int blockRotation,
      int width
   ) {
      Rotation yaw = rotation.yaw();
      mutablePos.assign(reverse ? Vector3i.SOUTH : Vector3i.NORTH).scale(width);
      yaw.rotateY(mutablePos, mutablePos);
      mutablePos.add(checkCoordinate.x, checkCoordinate.y, checkCoordinate.z);
      ObjectIntPair<StairConnectedBlockRuleSet.StairType> backStair = StairConnectedBlockRuleSet.getStairData(
         world, mutablePos, currentRuleSet.getMaterialName()
      );
      if (backStair == null) {
         return null;
      } else {
         boolean backConnection = reverse
            ? isTopperConnectionCompatible(rotation, backStair, Rotation.None)
            : isValleyConnectionCompatible(rotation, backStair, Rotation.None, false);
         if (!backConnection) {
            return null;
         } else {
            mutablePos.assign(reverse ? Vector3i.EAST : Vector3i.WEST).scale(width);
            yaw.rotateY(mutablePos, mutablePos);
            mutablePos.add(checkCoordinate.x, checkCoordinate.y, checkCoordinate.z);
            ObjectIntPair<StairConnectedBlockRuleSet.StairType> leftStair = StairConnectedBlockRuleSet.getStairData(
               world, mutablePos, currentRuleSet.getMaterialName()
            );
            mutablePos.assign(reverse ? Vector3i.WEST : Vector3i.EAST).scale(width);
            yaw.rotateY(mutablePos, mutablePos);
            mutablePos.add(checkCoordinate.x, checkCoordinate.y, checkCoordinate.z);
            ObjectIntPair<StairConnectedBlockRuleSet.StairType> rightStair = StairConnectedBlockRuleSet.getStairData(
               world, mutablePos, currentRuleSet.getMaterialName()
            );
            boolean leftConnection = reverse
               ? isTopperConnectionCompatible(rotation, leftStair, Rotation.Ninety)
               : isValleyConnectionCompatible(rotation, leftStair, Rotation.Ninety, false);
            boolean rightConnection = reverse
               ? isTopperConnectionCompatible(rotation, rightStair, Rotation.TwoSeventy)
               : isValleyConnectionCompatible(rotation, rightStair, Rotation.TwoSeventy, false);
            if (leftConnection == rightConnection) {
               return null;
            } else {
               StairConnectedBlockRuleSet.StairConnection connection = leftConnection
                  ? StairConnectedBlockRuleSet.StairConnection.CORNER_LEFT
                  : StairConnectedBlockRuleSet.StairConnection.CORNER_RIGHT;
               return !isWidthFulfilled(world, placementCoordinate, mutablePos, connection, yaw, blockId, blockRotation, width) ? null : connection;
            }
         }
      }
   }

   private static boolean isTopperConnectionCompatible(
      RotationTuple rotation, ObjectIntPair<StairConnectedBlockRuleSet.StairType> otherStair, Rotation yawOffset
   ) {
      return isValleyConnectionCompatible(rotation, otherStair, yawOffset, true);
   }

   private static boolean canBeTopper(
      World world, Vector3i coordinate, StairLikeConnectedBlockRuleSet currentRuleSet, RotationTuple rotation, Vector3i mutablePos
   ) {
      Rotation yaw = rotation.yaw();
      Vector3i[] directions = new Vector3i[]{Vector3i.NORTH, Vector3i.SOUTH, Vector3i.EAST, Vector3i.WEST};
      Rotation[] yawOffsets = new Rotation[]{Rotation.OneEighty, Rotation.None, Rotation.Ninety, Rotation.TwoSeventy};

      for (int i = 0; i < directions.length; i++) {
         mutablePos.assign(directions[i]);
         yaw.rotateY(mutablePos, mutablePos);
         mutablePos.add(coordinate.x, coordinate.y, coordinate.z);
         ObjectIntPair<StairConnectedBlockRuleSet.StairType> stair = StairConnectedBlockRuleSet.getStairData(
            world, mutablePos, currentRuleSet.getMaterialName()
         );
         if (stair == null || !isTopperConnectionCompatible(rotation, stair, yawOffsets[i])) {
            return false;
         }
      }

      return true;
   }

   private static boolean isValleyConnectionCompatible(
      RotationTuple rotation, ObjectIntPair<StairConnectedBlockRuleSet.StairType> otherStair, Rotation yawOffset, boolean inverted
   ) {
      Rotation targetYaw = rotation.yaw().add(yawOffset);
      if (otherStair == null) {
         return false;
      } else {
         RotationTuple stairRotation = RotationTuple.get(otherStair.rightInt());
         StairConnectedBlockRuleSet.StairType otherStairType = (StairConnectedBlockRuleSet.StairType)otherStair.first();
         if (stairRotation.pitch() != rotation.pitch()) {
            return false;
         } else if (inverted && otherStairType.isCorner()) {
            return false;
         } else {
            return !inverted && otherStairType.isInvertedCorner()
               ? false
               : stairRotation.yaw() == targetYaw
                  || otherStairType == StairConnectedBlockRuleSet.StairConnection.CORNER_RIGHT.getStairType(inverted)
                     && stairRotation.yaw() == targetYaw.add(Rotation.Ninety)
                  || otherStairType == StairConnectedBlockRuleSet.StairConnection.CORNER_LEFT.getStairType(inverted)
                     && stairRotation.yaw() == targetYaw.add(Rotation.TwoSeventy);
         }
      }
   }

   @Override
   public boolean onlyUpdateOnPlacement() {
      return false;
   }

   @Override
   public Optional<ConnectedBlocksUtil.ConnectedBlockResult> getConnectedBlockType(
      World world, Vector3i coordinate, BlockType blockType, int rotation, Vector3i placementNormal, boolean isPlacement
   ) {
      WorldChunk chunk = world.getChunkIfLoaded(ChunkUtil.indexChunkFromBlock(coordinate.x, coordinate.z));
      if (chunk == null) {
         return Optional.empty();
      } else {
         int belowBlockId = chunk.getBlock(coordinate.x, coordinate.y - 1, coordinate.z);
         BlockType belowBlockType = BlockType.getAssetMap().getAsset(belowBlockId);
         int belowBlockRotation = chunk.getRotationIndex(coordinate.x, coordinate.y - 1, coordinate.z);
         boolean hollow = true;
         if (belowBlockType != null) {
            Map<BlockFace, BlockFaceSupport[]> supporting = belowBlockType.getSupporting(belowBlockRotation);
            if (supporting != null) {
               BlockFaceSupport[] support = supporting.get(BlockFace.UP);
               hollow = support == null;
            }
         }

         int blockId = BlockType.getAssetMap().getIndex(blockType.getId());
         StairConnectedBlockRuleSet.StairType stairType = getConnectedBlockStairType(world, coordinate, this, blockId, rotation, this.width);
         if (this.topper != null && stairType == StairConnectedBlockRuleSet.StairType.STRAIGHT) {
            Vector3i belowCoordinate = new Vector3i(coordinate).add(0, -1, 0);
            RotationTuple currentRotation = RotationTuple.get(rotation);
            currentRotation = RotationTuple.of(Rotation.None, currentRotation.pitch(), currentRotation.roll());
            Vector3i mutablePos = new Vector3i();
            boolean topper = canBeTopper(world, belowCoordinate, this, currentRotation, mutablePos);
            if (topper) {
               BlockType topperBlockType = BlockType.getAssetMap().getAsset(this.topper.blockTypeKey);
               if (topperBlockType != null) {
                  return Optional.of(new ConnectedBlocksUtil.ConnectedBlockResult(topperBlockType.getId(), rotation));
               }
            }
         }

         if (this.hollow != null && hollow) {
            BlockType hollowBlockType = this.hollow.getStairBlockType(stairType);
            if (hollowBlockType != null) {
               return Optional.of(new ConnectedBlocksUtil.ConnectedBlockResult(hollowBlockType.getId(), rotation));
            }
         }

         BlockType regularBlockType = this.regular.getStairBlockType(stairType);
         if (regularBlockType != null) {
            ConnectedBlocksUtil.ConnectedBlockResult result = new ConnectedBlocksUtil.ConnectedBlockResult(regularBlockType.getId(), rotation);
            if (this.regular != null && this.width > 0) {
               StairConnectedBlockRuleSet.StairType existingStairType = this.regular.getStairType(BlockType.getAssetMap().getIndex(blockType.getId()));
               if (existingStairType != null && existingStairType != StairConnectedBlockRuleSet.StairType.STRAIGHT) {
                  int previousWidth = existingStairType.isLeft() ? -(this.width - 1) : (existingStairType.isRight() ? this.width - 1 : 0);
                  int newWidth = stairType.isLeft() ? -(this.width - 1) : (stairType.isRight() ? this.width - 1 : 0);
                  if (newWidth != previousWidth) {
                     Vector3i mutablePos = new Vector3i();
                     Rotation currentYaw = RotationTuple.get(rotation).yaw();
                     mutablePos.assign(Vector3i.EAST).scale(previousWidth);
                     currentYaw.rotateY(mutablePos, mutablePos);
                     result.addAdditionalBlock(mutablePos, regularBlockType.getId(), rotation);
                  }
               }
            }

            return Optional.of(result);
         } else {
            return Optional.empty();
         }
      }
   }

   @Override
   public void updateCachedBlockTypes(BlockType baseBlockType, BlockTypeAssetMap<String, BlockType> assetMap) {
      if (this.regular != null) {
         this.regular.updateCachedBlockTypes(baseBlockType, assetMap);
      }

      if (this.hollow != null) {
         this.hollow.updateCachedBlockTypes(baseBlockType, assetMap);
      }

      if (this.topper != null) {
         this.topper.resolve(baseBlockType, assetMap);
      }
   }

   @Override
   public StairConnectedBlockRuleSet.StairType getStairType(int blockId) {
      StairConnectedBlockRuleSet.StairType regularStairType = this.regular.getStairType(blockId);
      if (regularStairType != null) {
         return regularStairType;
      } else {
         return this.hollow != null ? this.hollow.getStairType(blockId) : null;
      }
   }

   @Nullable
   @Override
   public String getMaterialName() {
      return this.materialName;
   }

   @Nullable
   @Override
   public com.hypixel.hytale.protocol.ConnectedBlockRuleSet toPacket(BlockTypeAssetMap<String, BlockType> assetMap) {
      com.hypixel.hytale.protocol.ConnectedBlockRuleSet packet = new com.hypixel.hytale.protocol.ConnectedBlockRuleSet();
      packet.type = ConnectedBlockRuleSetType.Roof;
      com.hypixel.hytale.protocol.RoofConnectedBlockRuleSet roofPacket = new com.hypixel.hytale.protocol.RoofConnectedBlockRuleSet();
      if (this.regular != null) {
         roofPacket.regular = this.regular.toProtocol(assetMap);
      }

      if (this.hollow != null) {
         roofPacket.hollow = this.hollow.toProtocol(assetMap);
      }

      if (this.topper != null) {
         roofPacket.topperBlockId = assetMap.getIndex(this.topper.blockTypeKey);
      } else {
         roofPacket.topperBlockId = -1;
      }

      roofPacket.width = this.width;
      roofPacket.materialName = this.materialName;
      packet.roof = roofPacket;
      return packet;
   }
}