2026.02.11-255364b8e

This commit is contained in:
Intege-rs
2026-02-11 18:06:52 -05:00
parent 8988b1dc65
commit 4dcc723c9c
550 changed files with 12847 additions and 5414 deletions

View File

@@ -2,7 +2,7 @@ package com.hypixel.hytale.builtin.adventure.camera.asset.camerashake;
import com.hypixel.hytale.assetstore.map.IndexedAssetMap; import com.hypixel.hytale.assetstore.map.IndexedAssetMap;
import com.hypixel.hytale.protocol.CachedPacket; import com.hypixel.hytale.protocol.CachedPacket;
import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket;
import com.hypixel.hytale.protocol.UpdateType; import com.hypixel.hytale.protocol.UpdateType;
import com.hypixel.hytale.protocol.packets.assets.UpdateCameraShake; import com.hypixel.hytale.protocol.packets.assets.UpdateCameraShake;
import com.hypixel.hytale.server.core.asset.packet.SimpleAssetPacketGenerator; import com.hypixel.hytale.server.core.asset.packet.SimpleAssetPacketGenerator;
@@ -14,17 +14,17 @@ import javax.annotation.Nonnull;
public class CameraShakePacketGenerator extends SimpleAssetPacketGenerator<String, CameraShake, IndexedAssetMap<String, CameraShake>> { public class CameraShakePacketGenerator extends SimpleAssetPacketGenerator<String, CameraShake, IndexedAssetMap<String, CameraShake>> {
@Nonnull @Nonnull
public Packet generateInitPacket(@Nonnull IndexedAssetMap<String, CameraShake> assetMap, @Nonnull Map<String, CameraShake> assets) { public ToClientPacket generateInitPacket(@Nonnull IndexedAssetMap<String, CameraShake> assetMap, @Nonnull Map<String, CameraShake> assets) {
return toCachedPacket(UpdateType.Init, assetMap, assets); return toCachedPacket(UpdateType.Init, assetMap, assets);
} }
@Nonnull @Nonnull
protected Packet generateUpdatePacket(@Nonnull IndexedAssetMap<String, CameraShake> assetMap, @Nonnull Map<String, CameraShake> loadedAssets) { protected ToClientPacket generateUpdatePacket(@Nonnull IndexedAssetMap<String, CameraShake> assetMap, @Nonnull Map<String, CameraShake> loadedAssets) {
return toCachedPacket(UpdateType.AddOrUpdate, assetMap, loadedAssets); return toCachedPacket(UpdateType.AddOrUpdate, assetMap, loadedAssets);
} }
@Nonnull @Nonnull
protected Packet generateRemovePacket(@Nonnull IndexedAssetMap<String, CameraShake> assetMap, @Nonnull Set<String> removed) { protected ToClientPacket generateRemovePacket(@Nonnull IndexedAssetMap<String, CameraShake> assetMap, @Nonnull Set<String> removed) {
Int2ObjectOpenHashMap<com.hypixel.hytale.protocol.CameraShake> profiles = new Int2ObjectOpenHashMap<>(); Int2ObjectOpenHashMap<com.hypixel.hytale.protocol.CameraShake> profiles = new Int2ObjectOpenHashMap<>();
for (String key : removed) { for (String key : removed) {
@@ -39,7 +39,7 @@ public class CameraShakePacketGenerator extends SimpleAssetPacketGenerator<Strin
} }
@Nonnull @Nonnull
protected static Packet toCachedPacket( protected static ToClientPacket toCachedPacket(
@Nonnull UpdateType type, @Nonnull IndexedAssetMap<String, CameraShake> assetMap, @Nonnull Map<String, CameraShake> assets @Nonnull UpdateType type, @Nonnull IndexedAssetMap<String, CameraShake> assetMap, @Nonnull Map<String, CameraShake> assets
) { ) {
Int2ObjectOpenHashMap<com.hypixel.hytale.protocol.CameraShake> profiles = new Int2ObjectOpenHashMap<>(); Int2ObjectOpenHashMap<com.hypixel.hytale.protocol.CameraShake> profiles = new Int2ObjectOpenHashMap<>();

View File

@@ -3,7 +3,7 @@ package com.hypixel.hytale.builtin.adventure.camera.asset.viewbobbing;
import com.hypixel.hytale.assetstore.AssetMap; import com.hypixel.hytale.assetstore.AssetMap;
import com.hypixel.hytale.protocol.CachedPacket; import com.hypixel.hytale.protocol.CachedPacket;
import com.hypixel.hytale.protocol.MovementType; import com.hypixel.hytale.protocol.MovementType;
import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket;
import com.hypixel.hytale.protocol.UpdateType; import com.hypixel.hytale.protocol.UpdateType;
import com.hypixel.hytale.protocol.packets.assets.UpdateViewBobbing; import com.hypixel.hytale.protocol.packets.assets.UpdateViewBobbing;
import com.hypixel.hytale.server.core.asset.packet.SimpleAssetPacketGenerator; import com.hypixel.hytale.server.core.asset.packet.SimpleAssetPacketGenerator;
@@ -16,19 +16,19 @@ import javax.annotation.Nonnull;
public class ViewBobbingPacketGenerator extends SimpleAssetPacketGenerator<MovementType, ViewBobbing, AssetMap<MovementType, ViewBobbing>> { public class ViewBobbingPacketGenerator extends SimpleAssetPacketGenerator<MovementType, ViewBobbing, AssetMap<MovementType, ViewBobbing>> {
@Nonnull @Nonnull
@Override @Override
public Packet generateInitPacket(AssetMap<MovementType, ViewBobbing> assetMap, @Nonnull Map<MovementType, ViewBobbing> assets) { public ToClientPacket generateInitPacket(AssetMap<MovementType, ViewBobbing> assetMap, @Nonnull Map<MovementType, ViewBobbing> assets) {
return toCachedPacket(UpdateType.Init, assets); return toCachedPacket(UpdateType.Init, assets);
} }
@Nonnull @Nonnull
@Override @Override
protected Packet generateUpdatePacket(AssetMap<MovementType, ViewBobbing> assetMap, @Nonnull Map<MovementType, ViewBobbing> loadedAssets) { protected ToClientPacket generateUpdatePacket(AssetMap<MovementType, ViewBobbing> assetMap, @Nonnull Map<MovementType, ViewBobbing> loadedAssets) {
return toCachedPacket(UpdateType.AddOrUpdate, loadedAssets); return toCachedPacket(UpdateType.AddOrUpdate, loadedAssets);
} }
@Nonnull @Nonnull
@Override @Override
protected Packet generateRemovePacket(AssetMap<MovementType, ViewBobbing> assetMap, @Nonnull Set<MovementType> removed) { protected ToClientPacket generateRemovePacket(AssetMap<MovementType, ViewBobbing> assetMap, @Nonnull Set<MovementType> removed) {
UpdateViewBobbing packet = new UpdateViewBobbing(); UpdateViewBobbing packet = new UpdateViewBobbing();
packet.type = UpdateType.Remove; packet.type = UpdateType.Remove;
packet.profiles = new EnumMap<>(MovementType.class); packet.profiles = new EnumMap<>(MovementType.class);
@@ -41,7 +41,7 @@ public class ViewBobbingPacketGenerator extends SimpleAssetPacketGenerator<Movem
} }
@Nonnull @Nonnull
protected static Packet toCachedPacket(@Nonnull UpdateType type, @Nonnull Map<MovementType, ViewBobbing> assets) { protected static ToClientPacket toCachedPacket(@Nonnull UpdateType type, @Nonnull Map<MovementType, ViewBobbing> assets) {
UpdateViewBobbing packet = new UpdateViewBobbing(); UpdateViewBobbing packet = new UpdateViewBobbing();
packet.type = type; packet.type = type;
packet.profiles = new EnumMap<>(MovementType.class); packet.profiles = new EnumMap<>(MovementType.class);

View File

@@ -172,7 +172,7 @@ public class FarmingUtil {
} }
} }
public static void harvest( public static boolean harvest(
@Nonnull World world, @Nonnull World world,
@Nonnull ComponentAccessor<EntityStore> componentAccessor, @Nonnull ComponentAccessor<EntityStore> componentAccessor,
@Nonnull Ref<EntityStore> ref, @Nonnull Ref<EntityStore> ref,
@@ -180,9 +180,9 @@ public class FarmingUtil {
int rotationIndex, int rotationIndex,
@Nonnull Vector3i blockPosition @Nonnull Vector3i blockPosition
) { ) {
if (world.getGameplayConfig().getWorldConfig().isBlockGatheringAllowed()) { return world.getGameplayConfig().getWorldConfig().isBlockGatheringAllowed()
harvest0(componentAccessor, ref, blockType, rotationIndex, blockPosition); ? harvest0(componentAccessor, ref, blockType, rotationIndex, blockPosition)
} : false;
} }
@Nullable @Nullable
@@ -212,24 +212,19 @@ public class FarmingUtil {
@Nonnull Vector3i blockPosition @Nonnull Vector3i blockPosition
) { ) {
FarmingData farmingConfig = blockType.getFarming(); FarmingData farmingConfig = blockType.getFarming();
boolean isFarmable = true;
if (farmingConfig == null || farmingConfig.getStages() == null) { if (farmingConfig == null || farmingConfig.getStages() == null) {
return false; isFarmable = false;
} else if (blockType.getGathering().getHarvest() == null) { }
if (blockType.getGathering().getHarvest() == null) {
return false; return false;
} else { } else {
World world = store.getExternalData().getWorld(); World world = store.getExternalData().getWorld();
Vector3d centerPosition = new Vector3d(); Vector3d centerPosition = new Vector3d();
blockType.getBlockCenter(rotationIndex, centerPosition); blockType.getBlockCenter(rotationIndex, centerPosition);
centerPosition.add(blockPosition); centerPosition.add(blockPosition);
if (farmingConfig.getStageSetAfterHarvest() == null) { if (isFarmable && farmingConfig.getStageSetAfterHarvest() != null) {
giveDrops(store, ref, centerPosition, blockType);
WorldChunk chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(blockPosition.x, blockPosition.z));
if (chunk != null) {
chunk.breakBlock(blockPosition.x, blockPosition.y, blockPosition.z);
}
return true;
} else {
giveDrops(store, ref, centerPosition, blockType); giveDrops(store, ref, centerPosition, blockType);
Map<String, FarmingStageData[]> stageSets = farmingConfig.getStages(); Map<String, FarmingStageData[]> stageSets = farmingConfig.getStages();
FarmingStageData[] stages = stageSets.get(farmingConfig.getStartingStageSet()); FarmingStageData[] stages = stageSets.get(farmingConfig.getStartingStageSet());
@@ -307,6 +302,14 @@ public class FarmingUtil {
return false; return false;
} }
} }
} else {
giveDrops(store, ref, centerPosition, blockType);
WorldChunk chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(blockPosition.x, blockPosition.z));
if (chunk != null) {
chunk.breakBlock(blockPosition.x, blockPosition.y, blockPosition.z);
}
return true;
} }
} }
} }

View File

@@ -1,11 +1,14 @@
package com.hypixel.hytale.builtin.adventure.farming.interactions; package com.hypixel.hytale.builtin.adventure.farming.interactions;
import com.hypixel.hytale.builtin.adventure.farming.FarmingUtil; import com.hypixel.hytale.builtin.adventure.farming.FarmingUtil;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.protocol.InteractionState;
import com.hypixel.hytale.protocol.InteractionType; import com.hypixel.hytale.protocol.InteractionType;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.entity.InteractionContext; import com.hypixel.hytale.server.core.entity.InteractionContext;
@@ -27,7 +30,16 @@ public class HarvestCropInteraction extends SimpleBlockInteraction {
HarvestCropInteraction.class, HarvestCropInteraction::new, SimpleBlockInteraction.CODEC HarvestCropInteraction.class, HarvestCropInteraction::new, SimpleBlockInteraction.CODEC
) )
.documentation("Harvests the resources from the target farmable block.") .documentation("Harvests the resources from the target farmable block.")
.<Boolean>appendInherited(
new KeyedCodec<>("RequireNotBroken", Codec.BOOLEAN),
(interaction, s) -> interaction.requireNotBroken = s,
interaction -> interaction.requireNotBroken,
(interaction, parent) -> interaction.requireNotBroken = parent.requireNotBroken
)
.documentation("If true, the interaction will fail if the held item is broken (durability = 0).")
.add()
.build(); .build();
protected boolean requireNotBroken = false;
@Override @Override
protected void interactWithBlock( protected void interactWithBlock(
@@ -39,25 +51,31 @@ public class HarvestCropInteraction extends SimpleBlockInteraction {
@Nonnull Vector3i targetBlock, @Nonnull Vector3i targetBlock,
@Nonnull CooldownHandler cooldownHandler @Nonnull CooldownHandler cooldownHandler
) { ) {
Ref<EntityStore> ref = context.getEntity(); if (this.requireNotBroken && itemInHand != null && itemInHand.isBroken()) {
ChunkStore chunkStore = world.getChunkStore(); context.getState().state = InteractionState.Failed;
long chunkIndex = ChunkUtil.indexChunkFromBlock(targetBlock.x, targetBlock.z); } else {
Ref<ChunkStore> chunkRef = chunkStore.getChunkReference(chunkIndex); Ref<EntityStore> ref = context.getEntity();
if (chunkRef != null && chunkRef.isValid()) { ChunkStore chunkStore = world.getChunkStore();
BlockChunk blockChunkComponent = chunkStore.getStore().getComponent(chunkRef, BlockChunk.getComponentType()); long chunkIndex = ChunkUtil.indexChunkFromBlock(targetBlock.x, targetBlock.z);
Ref<ChunkStore> chunkRef = chunkStore.getChunkReference(chunkIndex);
if (chunkRef != null && chunkRef.isValid()) {
BlockChunk blockChunkComponent = chunkStore.getStore().getComponent(chunkRef, BlockChunk.getComponentType());
assert blockChunkComponent != null; assert blockChunkComponent != null;
BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(targetBlock.y); BlockSection blockSection = blockChunkComponent.getSectionAtBlockY(targetBlock.y);
if (blockSection != null) { if (blockSection != null) {
WorldChunk worldChunkComponent = chunkStore.getStore().getComponent(chunkRef, WorldChunk.getComponentType()); WorldChunk worldChunkComponent = chunkStore.getStore().getComponent(chunkRef, WorldChunk.getComponentType());
assert worldChunkComponent != null; assert worldChunkComponent != null;
BlockType blockType = worldChunkComponent.getBlockType(targetBlock); BlockType blockType = worldChunkComponent.getBlockType(targetBlock);
if (blockType != null) { if (blockType != null) {
int rotationIndex = blockSection.getRotationIndex(targetBlock.x, targetBlock.y, targetBlock.z); int rotationIndex = blockSection.getRotationIndex(targetBlock.x, targetBlock.y, targetBlock.z);
FarmingUtil.harvest(world, commandBuffer, ref, blockType, rotationIndex, targetBlock); if (!FarmingUtil.harvest(world, commandBuffer, ref, blockType, rotationIndex, targetBlock)) {
context.getState().state = InteractionState.Failed;
}
}
} }
} }
} }

View File

@@ -35,7 +35,6 @@ import com.hypixel.hytale.server.core.modules.entity.component.TransformComponen
import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent; import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent;
import com.hypixel.hytale.server.core.modules.entity.item.PickupItemComponent; import com.hypixel.hytale.server.core.modules.entity.item.PickupItemComponent;
import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId; import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId;
import com.hypixel.hytale.server.core.modules.i18n.I18nModule;
import com.hypixel.hytale.server.core.universe.PlayerRef; 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.World;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
@@ -62,12 +61,6 @@ public class NPCMemory extends Memory {
.add() .add()
.append(new KeyedCodec<>("TranslationKey", Codec.STRING), (npcMemory, s) -> npcMemory.memoryTitleKey = s, npcMemory -> npcMemory.memoryTitleKey) .append(new KeyedCodec<>("TranslationKey", Codec.STRING), (npcMemory, s) -> npcMemory.memoryTitleKey = s, npcMemory -> npcMemory.memoryTitleKey)
.add() .add()
.append(
new KeyedCodec<>("IsMemoriesNameOverridden", Codec.BOOLEAN),
(npcMemory, aBoolean) -> npcMemory.isMemoriesNameOverridden = aBoolean,
npcMemory -> npcMemory.isMemoriesNameOverridden
)
.add()
.append( .append(
new KeyedCodec<>("CapturedTimestamp", Codec.LONG), new KeyedCodec<>("CapturedTimestamp", Codec.LONG),
(npcMemory, aDouble) -> npcMemory.capturedTimestamp = aDouble, (npcMemory, aDouble) -> npcMemory.capturedTimestamp = aDouble,
@@ -86,10 +79,8 @@ public class NPCMemory extends Memory {
npcMemory -> npcMemory.foundLocationGeneralNameKey npcMemory -> npcMemory.foundLocationGeneralNameKey
) )
.add() .add()
.afterDecode(NPCMemory::processConfig)
.build(); .build();
private String npcRole; private String npcRole;
private boolean isMemoriesNameOverridden;
private long capturedTimestamp; private long capturedTimestamp;
private String foundLocationZoneNameKey; private String foundLocationZoneNameKey;
private String foundLocationGeneralNameKey; private String foundLocationGeneralNameKey;
@@ -98,11 +89,9 @@ public class NPCMemory extends Memory {
private NPCMemory() { private NPCMemory() {
} }
public NPCMemory(@Nonnull String npcRole, @Nonnull String nameTranslationKey, boolean isMemoriesNameOverridden) { public NPCMemory(@Nonnull String npcRole, @Nonnull String nameTranslationKey) {
this.npcRole = npcRole; this.npcRole = npcRole;
this.memoryTitleKey = nameTranslationKey; this.memoryTitleKey = nameTranslationKey;
this.isMemoriesNameOverridden = isMemoriesNameOverridden;
this.processConfig();
} }
@Override @Override
@@ -128,19 +117,6 @@ public class NPCMemory extends Memory {
return "UI/Custom/Pages/Memories/npcs/" + this.npcRole + ".png"; return "UI/Custom/Pages/Memories/npcs/" + this.npcRole + ".png";
} }
public void processConfig() {
if (this.isMemoriesNameOverridden) {
this.memoryTitleKey = "server.npcRoles." + this.npcRole + ".name";
if (I18nModule.get().getMessage("en-US", this.memoryTitleKey) == null) {
this.memoryTitleKey = "server.memories.names." + this.npcRole;
}
}
if (this.memoryTitleKey == null || this.memoryTitleKey.isEmpty()) {
this.memoryTitleKey = "server.npcRoles." + this.npcRole + ".name";
}
}
@Nonnull @Nonnull
@Override @Override
public Message getUndiscoveredTooltipText() { public Message getUndiscoveredTooltipText() {
@@ -177,15 +153,14 @@ public class NPCMemory extends Memory {
return false; return false;
} else { } else {
NPCMemory npcMemory = (NPCMemory)o; NPCMemory npcMemory = (NPCMemory)o;
return this.isMemoriesNameOverridden == npcMemory.isMemoriesNameOverridden && Objects.equals(this.npcRole, npcMemory.npcRole); return Objects.equals(this.npcRole, npcMemory.npcRole);
} }
} }
@Override @Override
public int hashCode() { public int hashCode() {
int result = super.hashCode(); int result = super.hashCode();
result = 31 * result + Objects.hashCode(this.npcRole); return 31 * result + Objects.hashCode(this.npcRole);
return 31 * result + Boolean.hashCode(this.isMemoriesNameOverridden);
} }
@Nonnull @Nonnull
@@ -193,8 +168,6 @@ public class NPCMemory extends Memory {
public String toString() { public String toString() {
return "NPCMemory{npcRole='" return "NPCMemory{npcRole='"
+ this.npcRole + this.npcRole
+ "', isMemoriesNameOverride="
+ this.isMemoriesNameOverridden
+ "', capturedTimestamp=" + "', capturedTimestamp="
+ this.capturedTimestamp + this.capturedTimestamp
+ "', foundLocationZoneNameKey='" + "', foundLocationZoneNameKey='"
@@ -271,46 +244,41 @@ public class NPCMemory extends Memory {
if (npcComponent != null) { if (npcComponent != null) {
Role role = npcComponent.getRole(); Role role = npcComponent.getRole();
if (role != null && role.isMemory()) { if (role != null && role.isMemory()) {
temp.isMemoriesNameOverridden = role.isMemoriesNameOverriden(); String memoriesNameOverride = role.getMemoriesNameOverride();
temp.npcRole = temp.isMemoriesNameOverridden ? role.getMemoriesNameOverride() : npcComponent.getRoleName(); temp.npcRole = memoriesNameOverride != null && !memoriesNameOverride.isEmpty() ? memoriesNameOverride : npcComponent.getRoleName();
temp.memoryTitleKey = role.getNameTranslationKey(); temp.memoryTitleKey = role.getNameTranslationKey();
temp.capturedTimestamp = System.currentTimeMillis(); temp.capturedTimestamp = System.currentTimeMillis();
temp.foundLocationGeneralNameKey = foundLocationZoneNameKey; temp.foundLocationGeneralNameKey = foundLocationZoneNameKey;
if (!memoriesPlugin.hasRecordedMemory(temp)) { if (!memoriesPlugin.hasRecordedMemory(temp) && playerMemoriesComponent.recordMemory(temp)) {
temp.processConfig(); NotificationUtil.sendNotification(
if (playerMemoriesComponent.recordMemory(temp)) { playerRefComponent.getPacketHandler(),
NotificationUtil.sendNotification( Message.translation("server.memories.general.collected").param("memoryTitle", Message.translation(temp.getTitle())),
playerRefComponent.getPacketHandler(), null,
Message.translation("server.memories.general.collected").param("memoryTitle", Message.translation(temp.getTitle())), "NotificationIcons/MemoriesIcon.png"
null, );
"NotificationIcons/MemoriesIcon.png" temp = new NPCMemory();
); TransformComponent npcTransformComponent = commandBuffer.getComponent(npcRef, TransformComponent.getComponentType());
temp = new NPCMemory(); if (npcTransformComponent != null) {
TransformComponent npcTransformComponent = commandBuffer.getComponent(npcRef, TransformComponent.getComponentType()); MemoriesGameplayConfig memoriesGameplayConfig = MemoriesGameplayConfig.get(store.getExternalData().getWorld().getGameplayConfig());
if (npcTransformComponent != null) { if (memoriesGameplayConfig != null) {
MemoriesGameplayConfig memoriesGameplayConfig = MemoriesGameplayConfig.get( ItemStack memoryItemStack = new ItemStack(memoriesGameplayConfig.getMemoriesCatchItemId());
store.getExternalData().getWorld().getGameplayConfig() Vector3d memoryItemHolderPosition = npcTransformComponent.getPosition().clone();
); BoundingBox boundingBoxComponent = commandBuffer.getComponent(npcRef, BoundingBox.getComponentType());
if (memoriesGameplayConfig != null) { if (boundingBoxComponent != null) {
ItemStack memoryItemStack = new ItemStack(memoriesGameplayConfig.getMemoriesCatchItemId()); memoryItemHolderPosition.y = memoryItemHolderPosition.y + boundingBoxComponent.getBoundingBox().middleY();
Vector3d memoryItemHolderPosition = npcTransformComponent.getPosition().clone();
BoundingBox boundingBoxComponent = commandBuffer.getComponent(npcRef, BoundingBox.getComponentType());
if (boundingBoxComponent != null) {
memoryItemHolderPosition.y = memoryItemHolderPosition.y + boundingBoxComponent.getBoundingBox().middleY();
}
Holder<EntityStore> memoryItemHolder = ItemComponent.generatePickedUpItem(
memoryItemStack, memoryItemHolderPosition, commandBuffer, ref
);
float memoryCatchItemLifetimeS = 0.62F;
PickupItemComponent pickupItemComponent = memoryItemHolder.getComponent(PickupItemComponent.getComponentType());
assert pickupItemComponent != null;
pickupItemComponent.setInitialLifeTime(0.62F);
commandBuffer.addEntity(memoryItemHolder, AddReason.SPAWN);
displayCatchEntityParticles(memoriesGameplayConfig, memoryItemHolderPosition, npcRef, commandBuffer);
} }
Holder<EntityStore> memoryItemHolder = ItemComponent.generatePickedUpItem(
memoryItemStack, memoryItemHolderPosition, commandBuffer, ref
);
float memoryCatchItemLifetimeS = 0.62F;
PickupItemComponent pickupItemComponent = memoryItemHolder.getComponent(PickupItemComponent.getComponentType());
assert pickupItemComponent != null;
pickupItemComponent.setInitialLifeTime(0.62F);
commandBuffer.addEntity(memoryItemHolder, AddReason.SPAWN);
displayCatchEntityParticles(memoriesGameplayConfig, memoryItemHolderPosition, npcRef, commandBuffer);
} }
} }
} }

View File

@@ -42,9 +42,9 @@ public class NPCMemoryProvider extends MemoryProvider<NPCMemory> {
String translationKey = getNPCNameTranslationKey(builder); String translationKey = getNPCNameTranslationKey(builder);
NPCMemory memory; NPCMemory memory;
if (memoriesNameOverride != null && !memoriesNameOverride.isEmpty()) { if (memoriesNameOverride != null && !memoriesNameOverride.isEmpty()) {
memory = new NPCMemory(memoriesNameOverride, translationKey, true); memory = new NPCMemory(memoriesNameOverride, translationKey);
} else { } else {
memory = new NPCMemory(builderInfo.getKeyName(), translationKey, false); memory = new NPCMemory(builderInfo.getKeyName(), translationKey);
} }
allMemories.computeIfAbsent(category, s -> new HashSet<>()).add(memory); allMemories.computeIfAbsent(category, s -> new HashSet<>()).add(memory);

View File

@@ -101,11 +101,11 @@ public class MemoriesPage extends InteractiveCustomUIPage<MemoriesPage.PageEvent
commandBuilder.set(selector + "#TotalMemoryCountComplete.Text", String.valueOf(memoriesInCategory.size())); commandBuilder.set(selector + "#TotalMemoryCountComplete.Text", String.valueOf(memoriesInCategory.size()));
boolean isCategoryComplete = recordedMemoriesCount == memoriesInCategory.size(); boolean isCategoryComplete = recordedMemoriesCount == memoriesInCategory.size();
if (isCategoryComplete) { if (isCategoryComplete) {
commandBuilder.set(selector + "#CategoryIcon.Background", "Pages/Memories/categories/" + category + "Complete.png"); commandBuilder.set(selector + "#CategoryIcon.AssetPath", "UI/Custom/Pages/Memories/categories/" + category + "Complete.png");
commandBuilder.set(selector + "#CompleteCategoryBackground.Visible", true); commandBuilder.set(selector + "#CompleteCategoryBackground.Visible", true);
commandBuilder.set(selector + "#CompleteCategoryCounter.Visible", true); commandBuilder.set(selector + "#CompleteCategoryCounter.Visible", true);
} else { } else {
commandBuilder.set(selector + "#CategoryIcon.Background", "Pages/Memories/categories/" + category + ".png"); commandBuilder.set(selector + "#CategoryIcon.AssetPath", "UI/Custom/Pages/Memories/categories/" + category + ".png");
commandBuilder.set(selector + "#NotCompleteCategoryCounter.Visible", true); commandBuilder.set(selector + "#NotCompleteCategoryCounter.Visible", true);
} }

View File

@@ -854,6 +854,8 @@ public class ObjectivePlugin extends JavaPlugin {
oldModel.getGradientId(), oldModel.getGradientId(),
oldModel.getEyeHeight(), oldModel.getEyeHeight(),
oldModel.getCrouchOffset(), oldModel.getCrouchOffset(),
oldModel.getSittingOffset(),
oldModel.getSleepingOffset(),
oldModel.getAnimationSetMap(), oldModel.getAnimationSetMap(),
oldModel.getCamera(), oldModel.getCamera(),
oldModel.getLight(), oldModel.getLight(),

View File

@@ -177,6 +177,8 @@ public class ObjectiveLocationMarkerSystems {
model.getGradientId(), model.getGradientId(),
model.getEyeHeight(), model.getEyeHeight(),
model.getCrouchOffset(), model.getCrouchOffset(),
model.getSittingOffset(),
model.getSleepingOffset(),
model.getAnimationSetMap(), model.getAnimationSetMap(),
model.getCamera(), model.getCamera(),
model.getLight(), model.getLight(),

View File

@@ -242,7 +242,7 @@ public abstract class ObjectiveTask implements NetworkSerializer<Objective, com.
private void shutdownEventRegistry() { private void shutdownEventRegistry() {
if (this.eventRegistry != null) { if (this.eventRegistry != null) {
this.eventRegistry.shutdown(); this.eventRegistry.shutdownAndCleanup(true);
this.eventRegistry = null; this.eventRegistry = null;
} }
} }

View File

@@ -6,6 +6,7 @@ import com.hypixel.hytale.builtin.adventure.teleporter.interaction.server.UsedTe
import com.hypixel.hytale.builtin.adventure.teleporter.page.TeleporterSettingsPageSupplier; import com.hypixel.hytale.builtin.adventure.teleporter.page.TeleporterSettingsPageSupplier;
import com.hypixel.hytale.builtin.adventure.teleporter.system.ClearUsedTeleporterSystem; import com.hypixel.hytale.builtin.adventure.teleporter.system.ClearUsedTeleporterSystem;
import com.hypixel.hytale.builtin.adventure.teleporter.system.CreateWarpWhenTeleporterPlacedSystem; import com.hypixel.hytale.builtin.adventure.teleporter.system.CreateWarpWhenTeleporterPlacedSystem;
import com.hypixel.hytale.builtin.adventure.teleporter.system.TurnOffTeleportersSystem;
import com.hypixel.hytale.builtin.teleport.TeleportPlugin; import com.hypixel.hytale.builtin.teleport.TeleportPlugin;
import com.hypixel.hytale.component.AddReason; import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.CommandBuffer;
@@ -61,11 +62,13 @@ public class TeleporterPlugin extends JavaPlugin {
ComponentType<EntityStore, PendingTeleport> pendingTeleportComponentType = PendingTeleport.getComponentType(); ComponentType<EntityStore, PendingTeleport> pendingTeleportComponentType = PendingTeleport.getComponentType();
chunkStoreRegistry.registerSystem(new TeleporterPlugin.TeleporterOwnedWarpRefChangeSystem(this.teleporterComponentType)); chunkStoreRegistry.registerSystem(new TeleporterPlugin.TeleporterOwnedWarpRefChangeSystem(this.teleporterComponentType));
chunkStoreRegistry.registerSystem(new TeleporterPlugin.TeleporterOwnedWarpRefSystem(this.teleporterComponentType)); chunkStoreRegistry.registerSystem(new TeleporterPlugin.TeleporterOwnedWarpRefSystem(this.teleporterComponentType));
chunkStoreRegistry.registerSystem( chunkStoreRegistry.registerSystem(new TurnOffTeleportersSystem());
new CreateWarpWhenTeleporterPlacedSystem( this.getChunkStoreRegistry()
placedByInteractionComponentType, this.teleporterComponentType, blockStateInfoComponentType, playerRefComponentType .registerSystem(
) new CreateWarpWhenTeleporterPlacedSystem(
); placedByInteractionComponentType, this.teleporterComponentType, blockStateInfoComponentType, playerRefComponentType
)
);
this.usedTeleporterComponentType = entityStoreRegistry.registerComponent(UsedTeleporter.class, UsedTeleporter::new); this.usedTeleporterComponentType = entityStoreRegistry.registerComponent(UsedTeleporter.class, UsedTeleporter::new);
entityStoreRegistry.registerSystem( entityStoreRegistry.registerSystem(
new ClearUsedTeleporterSystem( new ClearUsedTeleporterSystem(
@@ -151,18 +154,6 @@ public class TeleporterPlugin extends JavaPlugin {
public void onEntityAdded( public void onEntityAdded(
@Nonnull Ref<ChunkStore> ref, @Nonnull AddReason reason, @Nonnull Store<ChunkStore> store, @Nonnull CommandBuffer<ChunkStore> commandBuffer @Nonnull Ref<ChunkStore> ref, @Nonnull AddReason reason, @Nonnull Store<ChunkStore> store, @Nonnull CommandBuffer<ChunkStore> commandBuffer
) { ) {
switch (reason) {
case LOAD:
Teleporter teleporterComponent = commandBuffer.getComponent(ref, this.teleporterComponentType);
if (teleporterComponent == null) {
return;
} else {
String ownedWarp = teleporterComponent.getOwnedWarp();
if (ownedWarp != null && !ownedWarp.isEmpty() && !TeleportPlugin.get().getWarps().containsKey(ownedWarp.toLowerCase())) {
}
}
case SPAWN:
}
} }
@Override @Override

View File

@@ -44,6 +44,8 @@ public class Teleporter implements Component<ChunkStore> {
.documentation("The ID of the Word list to select default warp names from") .documentation("The ID of the Word list to select default warp names from")
.add() .add()
.build(); .build();
public static final String ACTIVATE_STATE = "Active";
public static final String INACTIVE_STATE = "default";
@Nullable @Nullable
private UUID worldUuid; private UUID worldUuid;
@Nullable @Nullable

View File

@@ -2,6 +2,7 @@ package com.hypixel.hytale.builtin.adventure.teleporter.page;
import com.hypixel.hytale.builtin.adventure.teleporter.component.Teleporter; import com.hypixel.hytale.builtin.adventure.teleporter.component.Teleporter;
import com.hypixel.hytale.builtin.adventure.teleporter.system.CreateWarpWhenTeleporterPlacedSystem; import com.hypixel.hytale.builtin.adventure.teleporter.system.CreateWarpWhenTeleporterPlacedSystem;
import com.hypixel.hytale.builtin.adventure.teleporter.system.TurnOffTeleportersSystem;
import com.hypixel.hytale.builtin.adventure.teleporter.util.CannedWarpNames; import com.hypixel.hytale.builtin.adventure.teleporter.util.CannedWarpNames;
import com.hypixel.hytale.builtin.teleport.TeleportPlugin; import com.hypixel.hytale.builtin.teleport.TeleportPlugin;
import com.hypixel.hytale.builtin.teleport.Warp; import com.hypixel.hytale.builtin.teleport.Warp;
@@ -11,13 +12,11 @@ import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.codec.codecs.EnumCodec; import com.hypixel.hytale.codec.codecs.EnumCodec;
import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store; import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.math.vector.Transform; import com.hypixel.hytale.math.vector.Transform;
import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime; import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime;
import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType; import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType;
import com.hypixel.hytale.protocol.packets.interface_.Page; import com.hypixel.hytale.protocol.packets.interface_.Page;
import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.entity.entities.player.pages.InteractiveCustomUIPage; import com.hypixel.hytale.server.core.entity.entities.player.pages.InteractiveCustomUIPage;
import com.hypixel.hytale.server.core.modules.block.BlockModule; import com.hypixel.hytale.server.core.modules.block.BlockModule;
@@ -34,6 +33,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -43,16 +43,11 @@ public class TeleporterSettingsPage extends InteractiveCustomUIPage<TeleporterSe
@Nonnull @Nonnull
private final Ref<ChunkStore> blockRef; private final Ref<ChunkStore> blockRef;
private final TeleporterSettingsPage.Mode mode; private final TeleporterSettingsPage.Mode mode;
@Nullable
private final String activeState;
public TeleporterSettingsPage( public TeleporterSettingsPage(@Nonnull PlayerRef playerRef, @Nonnull Ref<ChunkStore> blockRef, TeleporterSettingsPage.Mode mode) {
@Nonnull PlayerRef playerRef, @Nonnull Ref<ChunkStore> blockRef, TeleporterSettingsPage.Mode mode, @Nullable String activeState
) {
super(playerRef, CustomPageLifetime.CanDismissOrCloseThroughInteraction, TeleporterSettingsPage.PageEventData.CODEC); super(playerRef, CustomPageLifetime.CanDismissOrCloseThroughInteraction, TeleporterSettingsPage.PageEventData.CODEC);
this.blockRef = blockRef; this.blockRef = blockRef;
this.mode = mode; this.mode = mode;
this.activeState = activeState;
} }
@Override @Override
@@ -182,10 +177,6 @@ public class TeleporterSettingsPage extends InteractiveCustomUIPage<TeleporterSe
assert worldChunkComponent != null; assert worldChunkComponent != null;
int index = blockStateInfo.getIndex();
int targetX = ChunkUtil.xFromBlockInColumn(index);
int targetY = ChunkUtil.yFromBlockInColumn(index);
int targetZ = ChunkUtil.zFromBlockInColumn(index);
Teleporter teleporterComponent = this.blockRef.getStore().getComponent(this.blockRef, Teleporter.getComponentType()); Teleporter teleporterComponent = this.blockRef.getStore().getComponent(this.blockRef, Teleporter.getComponentType());
if (teleporterComponent == null) { if (teleporterComponent == null) {
playerComponent.getPageManager().setPage(ref, store, Page.None); playerComponent.getPageManager().setPage(ref, store, Page.None);
@@ -227,6 +218,8 @@ public class TeleporterSettingsPage extends InteractiveCustomUIPage<TeleporterSe
} }
playerComponent.getPageManager().setPage(ref, store, Page.None); playerComponent.getPageManager().setPage(ref, store, Page.None);
String ownedWarpBefore = teleporterComponent.getOwnedWarp();
String destinationWarpBefore = teleporterComponent.getWarp();
CreateWarpWhenTeleporterPlacedSystem.createWarp(worldChunkComponent, blockStateInfo, data.warpName); CreateWarpWhenTeleporterPlacedSystem.createWarp(worldChunkComponent, blockStateInfo, data.warpName);
teleporterComponent.setOwnedWarp(data.warpName); teleporterComponent.setOwnedWarp(data.warpName);
teleporterComponent.setIsCustomName(customName); teleporterComponent.setIsCustomName(customName);
@@ -252,34 +245,20 @@ public class TeleporterSettingsPage extends InteractiveCustomUIPage<TeleporterSe
| (data.isBlockRelative ? 64 : 0) | (data.isBlockRelative ? 64 : 0)
) )
); );
teleporterComponent.setWarp(data.warp != null && !data.warp.isEmpty() ? data.warp : null); teleporterComponent.setWarp(data.destinationWarp != null && !data.destinationWarp.isEmpty() ? data.destinationWarp : null);
break; break;
case WARP: case WARP:
teleporterComponent.setWorldUuid(null); teleporterComponent.setWorldUuid(null);
teleporterComponent.setTransform(null); teleporterComponent.setTransform(null);
teleporterComponent.setWarp(data.warp != null && !data.warp.isEmpty() ? data.warp : null); teleporterComponent.setWarp(data.destinationWarp != null && !data.destinationWarp.isEmpty() ? data.destinationWarp : null);
} }
String newState = "default"; boolean ownChanged = !Objects.equals(ownedWarpBefore, teleporterComponent.getOwnedWarp());
if (teleporterComponent.isValid()) { boolean destinationChanged = !Objects.equals(destinationWarpBefore, teleporterComponent.getWarp());
newState = this.activeState != null ? this.activeState : "default"; if (ownChanged || destinationChanged) {
World world = store.getExternalData().getWorld();
TurnOffTeleportersSystem.updatePortalBlocksInWorld(world);
} }
boolean isDifferentState = false;
BlockType blockType = worldChunkComponent.getBlockType(targetX, targetY, targetZ);
if (blockType != null) {
String currentState = blockType.getStateForBlock(blockType);
isDifferentState = !newState.equals(currentState);
}
if (isDifferentState) {
BlockType variantBlockType = blockType.getBlockForState(newState);
if (variantBlockType != null) {
worldChunkComponent.setBlockInteractionState(targetX, targetY, targetZ, variantBlockType, newState, true);
}
}
blockStateInfo.markNeedsSaving();
} }
} }
} }
@@ -381,7 +360,9 @@ public class TeleporterSettingsPage extends InteractiveCustomUIPage<TeleporterSe
.add() .add()
.append(new KeyedCodec<>("@World", Codec.STRING), (pageEventData, o) -> pageEventData.world = o, pageEventData -> pageEventData.world) .append(new KeyedCodec<>("@World", Codec.STRING), (pageEventData, o) -> pageEventData.world = o, pageEventData -> pageEventData.world)
.add() .add()
.append(new KeyedCodec<>("@Warp", Codec.STRING), (pageEventData, o) -> pageEventData.warp = o, pageEventData -> pageEventData.warp) .append(
new KeyedCodec<>("@Warp", Codec.STRING), (pageEventData, o) -> pageEventData.destinationWarp = o, pageEventData -> pageEventData.destinationWarp
)
.add() .add()
.append(new KeyedCodec<>("@NewWarp", Codec.STRING), (pageEventData, o) -> pageEventData.warpName = o, pageEventData -> pageEventData.warpName) .append(new KeyedCodec<>("@NewWarp", Codec.STRING), (pageEventData, o) -> pageEventData.warpName = o, pageEventData -> pageEventData.warpName)
.add() .add()
@@ -400,7 +381,7 @@ public class TeleporterSettingsPage extends InteractiveCustomUIPage<TeleporterSe
public boolean pitchIsRelative; public boolean pitchIsRelative;
public boolean rollIsRelative; public boolean rollIsRelative;
public String world; public String world;
public String warp; public String destinationWarp;
@Nullable @Nullable
public String warpName; public String warpName;
} }

View File

@@ -42,18 +42,9 @@ public class TeleporterSettingsPageSupplier implements OpenCustomUIInteraction.C
(supplier, parent) -> supplier.mode = parent.mode (supplier, parent) -> supplier.mode = parent.mode
) )
.add() .add()
.appendInherited(
new KeyedCodec<>("ActiveState", Codec.STRING),
(supplier, o) -> supplier.activeState = o,
supplier -> supplier.activeState,
(supplier, parent) -> supplier.activeState = parent.activeState
)
.add()
.build(); .build();
private boolean create = true; private boolean create = true;
private TeleporterSettingsPage.Mode mode = TeleporterSettingsPage.Mode.FULL; private TeleporterSettingsPage.Mode mode = TeleporterSettingsPage.Mode.FULL;
@Nullable
private String activeState;
@Nullable @Nullable
@Override @Override
@@ -91,7 +82,7 @@ public class TeleporterSettingsPageSupplier implements OpenCustomUIInteraction.C
blockRef = chunkComponentStore.addEntity(holder, AddReason.SPAWN); blockRef = chunkComponentStore.addEntity(holder, AddReason.SPAWN);
} }
return blockRef != null && blockRef.isValid() ? new TeleporterSettingsPage(playerRef, blockRef, this.mode, this.activeState) : null; return blockRef != null && blockRef.isValid() ? new TeleporterSettingsPage(playerRef, blockRef, this.mode) : null;
} }
} }
} }

View File

@@ -0,0 +1,98 @@
package com.hypixel.hytale.builtin.adventure.teleporter.system;
import com.hypixel.hytale.builtin.adventure.teleporter.component.Teleporter;
import com.hypixel.hytale.builtin.teleport.TeleportPlugin;
import com.hypixel.hytale.builtin.teleport.Warp;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.query.AndQuery;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.RefSystem;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.modules.block.BlockModule;
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.storage.ChunkStore;
import javax.annotation.Nonnull;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;
public class TurnOffTeleportersSystem extends RefSystem<ChunkStore> {
public static final Query<ChunkStore> QUERY = Query.and(Teleporter.getComponentType(), BlockModule.BlockStateInfo.getComponentType());
@Override
public void onEntityAdded(
@Nonnull Ref<ChunkStore> ref, @Nonnull AddReason reason, @Nonnull Store<ChunkStore> store, @Nonnull CommandBuffer<ChunkStore> commandBuffer
) {
if (reason == AddReason.LOAD) {
updatePortalBlocksInWorld(store.getExternalData().getWorld());
}
}
@Override
public void onEntityRemove(
@Nonnull Ref<ChunkStore> ref, @Nonnull RemoveReason reason, @Nonnull Store<ChunkStore> store, @Nonnull CommandBuffer<ChunkStore> commandBuffer
) {
if (reason == RemoveReason.REMOVE) {
updatePortalBlocksInWorld(store.getExternalData().getWorld());
}
}
public static void updatePortalBlocksInWorld(World world) {
Store<ChunkStore> store = world.getChunkStore().getStore();
AndQuery<ChunkStore> entityQuery = Query.and(Teleporter.getComponentType(), BlockModule.BlockStateInfo.getComponentType());
store.forEachChunk(entityQuery, (archetypeChunk, commandBuffer) -> {
for (int i = 0; i < archetypeChunk.size(); i++) {
Ref<ChunkStore> ref = archetypeChunk.getReferenceTo(i);
updatePortalBlockInWorld(ref, commandBuffer);
}
});
}
private static void updatePortalBlockInWorld(Ref<ChunkStore> ref, ComponentAccessor<ChunkStore> store) {
if (ref.isValid()) {
Teleporter teleporter = store.getComponent(ref, Teleporter.getComponentType());
BlockModule.BlockStateInfo blockState = store.getComponent(ref, BlockModule.BlockStateInfo.getComponentType());
updatePortalBlockInWorld(store, teleporter, blockState);
}
}
public static void updatePortalBlockInWorld(ComponentAccessor<ChunkStore> store, Teleporter teleporter, BlockModule.BlockStateInfo blockStateInfo) {
Ref<ChunkStore> chunkRef = blockStateInfo.getChunkRef();
if (chunkRef.isValid()) {
WorldChunk worldChunkComponent = store.getComponent(chunkRef, WorldChunk.getComponentType());
if (worldChunkComponent != null) {
int index = blockStateInfo.getIndex();
int x = ChunkUtil.xFromBlockInColumn(index);
int y = ChunkUtil.yFromBlockInColumn(index);
int z = ChunkUtil.zFromBlockInColumn(index);
BlockType blockType = worldChunkComponent.getBlockType(x, y, z);
if (blockType != null) {
String warpId = teleporter.getWarp();
Warp destinationWarp = warpId == null ? null : TeleportPlugin.get().getWarps().get(warpId);
String currentState = blockType.getStateForBlock(blockType);
String desiredState = destinationWarp == null ? "default" : "Active";
if (!desiredState.equals(currentState)) {
worldChunkComponent.setBlockInteractionState(x, y, z, blockType, desiredState, false);
blockStateInfo.markNeedsSaving(store);
}
if (destinationWarp == null) {
teleporter.setWarp(null);
blockStateInfo.markNeedsSaving(store);
}
}
}
}
}
@NullableDecl
@Override
public Query<ChunkStore> getQuery() {
return QUERY;
}
}

View File

@@ -101,7 +101,7 @@ public class AssetEditorPacketHandler extends GenericPacketHandler {
@Nonnull @Nonnull
@Override @Override
public String getIdentifier() { public String getIdentifier() {
return "{Editor(" + NettyUtil.formatRemoteAddress(this.channel) + "), " + this.editorClient.getUuid() + ", " + this.editorClient.getUsername() + "}"; return "{Editor(" + NettyUtil.formatRemoteAddress(this.getChannel()) + "), " + this.editorClient.getUuid() + ", " + this.editorClient.getUsername() + "}";
} }
@Override @Override
@@ -137,6 +137,7 @@ public class AssetEditorPacketHandler extends GenericPacketHandler {
this.registerHandler(316, p -> this.handle((AssetEditorCreateAssetPack)p)); this.registerHandler(316, p -> this.handle((AssetEditorCreateAssetPack)p));
this.registerHandler(315, p -> this.handle((AssetEditorUpdateAssetPack)p)); this.registerHandler(315, p -> this.handle((AssetEditorUpdateAssetPack)p));
this.registerHandler(317, p -> this.handle((AssetEditorDeleteAssetPack)p)); this.registerHandler(317, p -> this.handle((AssetEditorDeleteAssetPack)p));
this.registerHandler(232, p -> this.handle((UpdateLanguage)p));
} }
public void handle(@Nonnull AssetEditorSubscribeModifiedAssetsChanges packet) { public void handle(@Nonnull AssetEditorSubscribeModifiedAssetsChanges packet) {
@@ -400,11 +401,11 @@ public class AssetEditorPacketHandler extends GenericPacketHandler {
"%s - %s at %s left with reason: %s - %s", "%s - %s at %s left with reason: %s - %s",
this.editorClient.getUuid(), this.editorClient.getUuid(),
this.editorClient.getUsername(), this.editorClient.getUsername(),
NettyUtil.formatRemoteAddress(this.channel), NettyUtil.formatRemoteAddress(this.getChannel()),
packet.type.name(), packet.type.name(),
packet.reason packet.reason
); );
this.channel.close(); this.getChannel().close();
} }
private boolean lacksPermission(int token) { private boolean lacksPermission(int token) {

View File

@@ -38,7 +38,7 @@ import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.event.EventRegistry; import com.hypixel.hytale.event.EventRegistry;
import com.hypixel.hytale.event.IEventDispatcher; import com.hypixel.hytale.event.IEventDispatcher;
import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket;
import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorAsset; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorAsset;
import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorAssetListUpdate; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorAssetListUpdate;
import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorAssetPackSetup; import com.hypixel.hytale.protocol.packets.asseteditor.AssetEditorAssetPackSetup;
@@ -78,6 +78,7 @@ import com.hypixel.hytale.server.core.asset.AssetPackRegisterEvent;
import com.hypixel.hytale.server.core.asset.AssetPackUnregisterEvent; import com.hypixel.hytale.server.core.asset.AssetPackUnregisterEvent;
import com.hypixel.hytale.server.core.asset.AssetRegistryLoader; import com.hypixel.hytale.server.core.asset.AssetRegistryLoader;
import com.hypixel.hytale.server.core.asset.common.events.CommonAssetMonitorEvent; import com.hypixel.hytale.server.core.asset.common.events.CommonAssetMonitorEvent;
import com.hypixel.hytale.server.core.config.ModConfig;
import com.hypixel.hytale.server.core.io.PacketHandler; import com.hypixel.hytale.server.core.io.PacketHandler;
import com.hypixel.hytale.server.core.io.ServerManager; import com.hypixel.hytale.server.core.io.ServerManager;
import com.hypixel.hytale.server.core.io.handlers.InitialPacketHandler; import com.hypixel.hytale.server.core.io.handlers.InitialPacketHandler;
@@ -806,8 +807,8 @@ public class AssetEditorPlugin extends JavaPlugin {
String newPackId = newPackIdentifier.toString(); String newPackId = newPackIdentifier.toString();
Path packPath = dataSource.getRootPath(); Path packPath = dataSource.getRootPath();
HytaleServerConfig serverConfig = HytaleServer.get().getConfig(); HytaleServerConfig serverConfig = HytaleServer.get().getConfig();
HytaleServerConfig.ModConfig.setBoot(serverConfig, newPackIdentifier, true); HytaleServerConfig.setBoot(serverConfig, newPackIdentifier, true);
Map<PluginIdentifier, HytaleServerConfig.ModConfig> modConfig = serverConfig.getModConfig(); Map<PluginIdentifier, ModConfig> modConfig = serverConfig.getModConfig();
modConfig.remove(PluginIdentifier.fromString(packId)); modConfig.remove(PluginIdentifier.fromString(packId));
serverConfig.markChanged(); serverConfig.markChanged();
if (serverConfig.consumeHasChanged()) { if (serverConfig.consumeHasChanged()) {
@@ -816,7 +817,7 @@ public class AssetEditorPlugin extends JavaPlugin {
AssetModule assetModule = AssetModule.get(); AssetModule assetModule = AssetModule.get();
assetModule.unregisterPack(packId); assetModule.unregisterPack(packId);
assetModule.registerPack(newPackId, packPath, manifest); assetModule.registerPack(newPackId, packPath, manifest, false);
} }
} }
} }
@@ -886,13 +887,13 @@ public class AssetEditorPlugin extends JavaPlugin {
Path manifestPath = packPath.resolve("manifest.json"); Path manifestPath = packPath.resolve("manifest.json");
BsonUtil.writeSync(manifestPath, PluginManifest.CODEC, manifest, this.getLogger()); BsonUtil.writeSync(manifestPath, PluginManifest.CODEC, manifest, this.getLogger());
HytaleServerConfig serverConfig = HytaleServer.get().getConfig(); HytaleServerConfig serverConfig = HytaleServer.get().getConfig();
HytaleServerConfig.ModConfig.setBoot(serverConfig, new PluginIdentifier(manifest), true); HytaleServerConfig.setBoot(serverConfig, new PluginIdentifier(manifest), true);
serverConfig.markChanged(); serverConfig.markChanged();
if (serverConfig.consumeHasChanged()) { if (serverConfig.consumeHasChanged()) {
HytaleServerConfig.save(serverConfig).join(); HytaleServerConfig.save(serverConfig).join();
} }
AssetModule.get().registerPack(packId, packPath, manifest); AssetModule.get().registerPack(packId, packPath, manifest, false);
editorClient.sendSuccessReply(requestToken, Messages.PACK_CREATED); editorClient.sendSuccessReply(requestToken, Messages.PACK_CREATED);
this.getLogger().at(Level.INFO).log("Created new pack: %s at %s", packId, packPath); this.getLogger().at(Level.INFO).log("Created new pack: %s at %s", packId, packPath);
} catch (IOException var12) { } catch (IOException var12) {
@@ -965,7 +966,7 @@ public class AssetEditorPlugin extends JavaPlugin {
editorClient.getPacketHandler().write(new AssetEditorExportAssetInitialize(new AssetEditorAsset(null, assetPath.toPacket()), null, 0, false)); editorClient.getPacketHandler().write(new AssetEditorExportAssetInitialize(new AssetEditorAsset(null, assetPath.toPacket()), null, 0, false));
} else { } else {
byte[][] parts = ArrayUtil.split(bytes, 2621440); byte[][] parts = ArrayUtil.split(bytes, 2621440);
Packet[] packets = new Packet[2 + parts.length]; ToClientPacket[] packets = new ToClientPacket[2 + parts.length];
packets[0] = new AssetEditorExportAssetInitialize(new AssetEditorAsset(null, assetPath.toPacket()), null, bytes.length, false); packets[0] = new AssetEditorExportAssetInitialize(new AssetEditorAsset(null, assetPath.toPacket()), null, bytes.length, false);
for (int partIndex = 0; partIndex < parts.length; partIndex++) { for (int partIndex = 0; partIndex < parts.length; partIndex++) {
@@ -1421,12 +1422,13 @@ public class AssetEditorPlugin extends JavaPlugin {
editorClient.sendPopupNotification(AssetEditorPopupNotificationType.Error, Messages.REQUEST_CHILD_IDS_ASSET_TYPE_MISSING); editorClient.sendPopupNotification(AssetEditorPopupNotificationType.Error, Messages.REQUEST_CHILD_IDS_ASSET_TYPE_MISSING);
} else { } else {
AssetStore assetStore = assetStoreTypeHandler.getAssetStore(); AssetStore assetStore = assetStoreTypeHandler.getAssetStore();
AssetMap assetMap = assetStore.getAssetMap();
Object key = assetStore.decodeFilePathKey(assetPath.path()); Object key = assetStore.decodeFilePathKey(assetPath.path());
Set children = assetStore.getAssetMap().getChildren(key); Set children = assetMap.getChildren(key);
HashSet childrenIds = new HashSet(); HashSet childrenIds = new HashSet();
if (children != null) { if (children != null) {
for (Object child : children) { for (Object child : children) {
if (assetStore.getAssetMap().getPath(child) != null) { if (assetMap.getPath(child) != null) {
childrenIds.add(child.toString()); childrenIds.add(child.toString());
} }
} }
@@ -1796,13 +1798,13 @@ public class AssetEditorPlugin extends JavaPlugin {
} }
} }
private void sendPacketToAllEditorUsers(@Nonnull Packet packet) { private void sendPacketToAllEditorUsers(@Nonnull ToClientPacket packet) {
for (EditorClient editorClient : this.clientOpenAssetPathMapping.keySet()) { for (EditorClient editorClient : this.clientOpenAssetPathMapping.keySet()) {
editorClient.getPacketHandler().write(packet); editorClient.getPacketHandler().write(packet);
} }
} }
private void sendPacketToAllEditorUsersExcept(@Nonnull Packet packet, EditorClient ignoreEditorClient) { private void sendPacketToAllEditorUsersExcept(@Nonnull ToClientPacket packet, EditorClient ignoreEditorClient) {
for (EditorClient editorClient : this.clientOpenAssetPathMapping.keySet()) { for (EditorClient editorClient : this.clientOpenAssetPathMapping.keySet()) {
if (!editorClient.equals(ignoreEditorClient)) { if (!editorClient.equals(ignoreEditorClient)) {
editorClient.getPacketHandler().write(packet); editorClient.getPacketHandler().write(packet);

View File

@@ -86,7 +86,10 @@ public class StandardDataSource implements DataSource {
@Override @Override
public void shutdown() { public void shutdown() {
this.saveSchedule.cancel(false); if (this.saveSchedule != null) {
this.saveSchedule.cancel(false);
}
this.saveRecentModifications(); this.saveRecentModifications();
} }

View File

@@ -129,6 +129,7 @@ import com.hypixel.hytale.codec.codecs.array.ArrayCodec;
import com.hypixel.hytale.codec.validation.Validators; import com.hypixel.hytale.codec.validation.Validators;
import com.hypixel.hytale.common.util.CompletableFutureUtil; import com.hypixel.hytale.common.util.CompletableFutureUtil;
import com.hypixel.hytale.common.util.PathUtil; import com.hypixel.hytale.common.util.PathUtil;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.Archetype; import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentAccessor;
@@ -147,13 +148,11 @@ import com.hypixel.hytale.math.block.BlockCubeUtil;
import com.hypixel.hytale.math.block.BlockSphereUtil; import com.hypixel.hytale.math.block.BlockSphereUtil;
import com.hypixel.hytale.math.block.BlockUtil; import com.hypixel.hytale.math.block.BlockUtil;
import com.hypixel.hytale.math.iterator.LineIterator; import com.hypixel.hytale.math.iterator.LineIterator;
import com.hypixel.hytale.math.matrix.Matrix4d;
import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.math.util.MathUtil; import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.math.vector.Vector4d;
import com.hypixel.hytale.math.vector.VectorBoxUtil; import com.hypixel.hytale.math.vector.VectorBoxUtil;
import com.hypixel.hytale.metrics.MetricProvider; import com.hypixel.hytale.metrics.MetricProvider;
import com.hypixel.hytale.metrics.MetricResults; import com.hypixel.hytale.metrics.MetricResults;
@@ -195,13 +194,17 @@ import com.hypixel.hytale.server.core.command.system.CommandManager;
import com.hypixel.hytale.server.core.command.system.CommandRegistry; import com.hypixel.hytale.server.core.command.system.CommandRegistry;
import com.hypixel.hytale.server.core.command.system.CommandSender; import com.hypixel.hytale.server.core.command.system.CommandSender;
import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.entity.UUIDComponent;
import com.hypixel.hytale.server.core.entity.entities.BlockEntity;
import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent; import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent;
import com.hypixel.hytale.server.core.event.events.player.PlayerDisconnectEvent; import com.hypixel.hytale.server.core.event.events.player.PlayerDisconnectEvent;
import com.hypixel.hytale.server.core.inventory.ItemStack; import com.hypixel.hytale.server.core.inventory.ItemStack;
import com.hypixel.hytale.server.core.inventory.container.ItemContainer; import com.hypixel.hytale.server.core.inventory.container.ItemContainer;
import com.hypixel.hytale.server.core.io.ServerManager; import com.hypixel.hytale.server.core.io.ServerManager;
import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent; import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.modules.entity.tracker.EntityTrackerSystems;
import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.RootInteraction;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.server.OpenCustomUIInteraction; import com.hypixel.hytale.server.core.modules.interaction.interaction.config.server.OpenCustomUIInteraction;
@@ -223,7 +226,6 @@ import com.hypixel.hytale.server.core.prefab.selection.standard.FeedbackConsumer
import com.hypixel.hytale.server.core.universe.PlayerRef; import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.world.SoundUtil; import com.hypixel.hytale.server.core.universe.world.SoundUtil;
import com.hypixel.hytale.server.core.universe.world.World; import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.accessor.BlockAccessor;
import com.hypixel.hytale.server.core.universe.world.accessor.ChunkAccessor; import com.hypixel.hytale.server.core.universe.world.accessor.ChunkAccessor;
import com.hypixel.hytale.server.core.universe.world.accessor.LocalCachedChunkAccessor; import com.hypixel.hytale.server.core.universe.world.accessor.LocalCachedChunkAccessor;
import com.hypixel.hytale.server.core.universe.world.accessor.OverridableChunkAccessor; import com.hypixel.hytale.server.core.universe.world.accessor.OverridableChunkAccessor;
@@ -280,6 +282,7 @@ import java.util.function.Predicate;
import java.util.logging.Level; import java.util.logging.Level;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.joml.Quaterniond;
public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, MetricProvider { public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider, MetricProvider {
public static final String EDITOR_BLOCK = "Editor_Block"; public static final String EDITOR_BLOCK = "Editor_Block";
@@ -912,15 +915,47 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider,
@Nonnull @Nonnull
public BuilderToolsPlugin.ActionEntry restore(Ref<EntityStore> ref, Player player, World world, ComponentAccessor<EntityStore> componentAccessor) { public BuilderToolsPlugin.ActionEntry restore(Ref<EntityStore> ref, Player player, World world, ComponentAccessor<EntityStore> componentAccessor) {
List<SelectionSnapshot<?>> collector = Collections.emptyList(); List<SelectionSnapshot<?>> collector = Collections.emptyList();
List<Ref<EntityStore>> recreatedEntityRefs = null;
if (this.action == BuilderToolsPlugin.Action.ROTATE) {
PrototypePlayerBuilderToolSettings protoSettings = ToolOperation.getOrCreatePrototypeSettings(player.getUuid());
List<Ref<EntityStore>> currentRefs = protoSettings.getLastTransformEntityRefs();
if (currentRefs != null) {
Store<EntityStore> entityStore = world.getEntityStore().getStore();
for (Ref<EntityStore> currentRef : currentRefs) {
if (currentRef.isValid()) {
collector = (List<SelectionSnapshot<?>>)(collector.isEmpty() ? new ObjectArrayList<>() : collector);
collector.add(new EntityRemoveSnapshot(currentRef));
entityStore.removeEntity(currentRef, RemoveReason.UNLOAD);
}
}
protoSettings.setLastTransformEntityRefs(null);
}
}
for (SelectionSnapshot<?> snapshot : this.snapshots) { for (SelectionSnapshot<?> snapshot : this.snapshots) {
SelectionSnapshot<?> nextSnapshot = snapshot.restore(ref, player, world, componentAccessor); if (this.action != BuilderToolsPlugin.Action.ROTATE || !(snapshot instanceof EntityAddSnapshot)) {
if (nextSnapshot != null) { SelectionSnapshot<?> nextSnapshot = snapshot.restore(ref, player, world, componentAccessor);
collector = (List<SelectionSnapshot<?>>)(collector.isEmpty() ? new ObjectArrayList<>() : collector); if (nextSnapshot != null) {
collector.add(nextSnapshot); collector = (List<SelectionSnapshot<?>>)(collector.isEmpty() ? new ObjectArrayList<>() : collector);
collector.add(nextSnapshot);
if (nextSnapshot instanceof EntityAddSnapshot entityAddSnapshot) {
if (recreatedEntityRefs == null) {
recreatedEntityRefs = new ArrayList<>();
}
recreatedEntityRefs.add(entityAddSnapshot.getEntityRef());
}
}
} }
} }
if (this.action == BuilderToolsPlugin.Action.ROTATE && recreatedEntityRefs != null && !recreatedEntityRefs.isEmpty()) {
PrototypePlayerBuilderToolSettings prototypeSettings = ToolOperation.getOrCreatePrototypeSettings(player.getUuid());
prototypeSettings.setLastTransformEntityRefs(recreatedEntityRefs);
}
return new BuilderToolsPlugin.ActionEntry(this.action, collector); return new BuilderToolsPlugin.ActionEntry(this.action, collector);
} }
} }
@@ -1925,88 +1960,94 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider,
this.pushHistory(BuilderToolsPlugin.Action.EXTRUDE, new BlockSelectionSnapshot(before)); this.pushHistory(BuilderToolsPlugin.Action.EXTRUDE, new BlockSelectionSnapshot(before));
BlockSelection after = new BlockSelection(totalBlocks, 0); BlockSelection after = new BlockSelection(totalBlocks, 0);
after.copyPropertiesFrom(before); after.copyPropertiesFrom(before);
this.extendFaceFindBlocks( if (x >= min.getX() && x <= max.getX()) {
accessor, BlockType.getAssetMap(), before, after, x + normalX, y + normalY, z + normalZ, normalX, normalY, normalZ, extrudeDepth, blockId, min, max if (y >= min.getY() && y <= max.getY()) {
); if (z >= min.getZ() && z <= max.getZ()) {
Vector3i offset = new Vector3i(0, 0, 0); int testBlock = accessor.getBlock(x - normalX, y - normalY, z - normalZ);
BlockType testBlockType = BlockType.getAssetMap().getAsset(testBlock);
for (int i = 0; i < extrudeDepth; i++) { if (testBlockType != null && (testBlockType.getDrawType() == DrawType.Cube || testBlockType.getDrawType() == DrawType.CubeWithModel)) {
offset.x = normalX * i; int xMod = Math.abs(normalX) == 1 ? 0 : 1;
offset.y = normalY * i; int yMod = Math.abs(normalY) == 1 ? 0 : 1;
offset.z = normalZ * i; int zMod = Math.abs(normalZ) == 1 ? 0 : 1;
after.placeNoReturn("Set", this.player, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, offset, null, componentAccessor); Vector3i surfaceMin = new Vector3i(x - radiusAllowed * xMod, y - radiusAllowed * yMod, z - radiusAllowed * zMod);
Vector3i surfaceMax = new Vector3i(x + radiusAllowed * xMod, y + radiusAllowed * yMod, z + radiusAllowed * zMod);
this.extendFaceFindBlocks(accessor, before, after, normalX, normalY, normalZ, extrudeDepth, blockId, min, max, surfaceMin, surfaceMax);
after.placeNoReturn("Set", this.player, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor);
BuilderToolsPlugin.invalidateWorldMapForSelection(after, world);
long end = System.nanoTime();
long diff = end - start;
BuilderToolsPlugin.get()
.getLogger()
.at(Level.FINE)
.log("Took: %dns (%dms) to execute set of %d blocks", diff, TimeUnit.NANOSECONDS.toMillis(diff), after.getBlockCount());
this.sendUpdate();
this.sendArea();
}
}
}
} }
BuilderToolsPlugin.invalidateWorldMapForSelection(after, world);
long end = System.nanoTime();
long diff = end - start;
BuilderToolsPlugin.get()
.getLogger()
.at(Level.FINE)
.log("Took: %dns (%dms) to execute set of %d blocks", diff, TimeUnit.NANOSECONDS.toMillis(diff), after.getBlockCount());
this.sendUpdate();
this.sendArea();
} }
private void extendFaceFindBlocks( private void extendFaceFindBlocks(
@Nonnull ChunkAccessor accessor, @Nonnull LocalCachedChunkAccessor accessor,
@Nonnull BlockTypeAssetMap<String, BlockType> assetMap,
@Nonnull BlockSelection before, @Nonnull BlockSelection before,
@Nonnull BlockSelection after, @Nonnull BlockSelection after,
int x,
int y,
int z,
int normalX, int normalX,
int normalY, int normalY,
int normalZ, int normalZ,
int extrudeDepth, int extrudeDepth,
int blockId, int blockId,
@Nonnull Vector3i min, @Nonnull Vector3i min,
@Nonnull Vector3i max @Nonnull Vector3i max,
@Nonnull Vector3i surfaceMin,
@Nonnull Vector3i surfaceMax
) { ) {
if (x >= min.getX() && x <= max.getX()) { int xMin = surfaceMin.getX();
if (y >= min.getY() && y <= max.getY()) { int yMin = surfaceMin.getY();
if (z >= min.getZ() && z <= max.getZ()) { int zMin = surfaceMin.getZ();
int block = accessor.getBlock(x, y, z); int xMax = surfaceMax.getX();
int testBlock = accessor.getBlock(x - normalX, y - normalY, z - normalZ); int yMax = surfaceMax.getY();
BlockType testBlockType = assetMap.getAsset(testBlock); int zMax = surfaceMax.getZ();
if (testBlockType != null && (testBlockType.getDrawType() == DrawType.Cube || testBlockType.getDrawType() == DrawType.CubeWithModel)) {
if (!before.hasBlockAtWorldPos(x, y, z)) {
BlockAccessor blocks = accessor.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(x, z));
if (blocks != null) {
before.addBlockAtWorldPos(
x,
y,
z,
block,
blocks.getRotationIndex(x, y, z),
blocks.getFiller(x, y, z),
blocks.getSupportValue(x, y, z),
blocks.getBlockComponentHolder(x, y, z)
);
after.addBlockAtWorldPos(x, y, z, blockId, 0, 0, 0);
for (Vector3i side : Vector3i.BLOCK_SIDES) { for (int x = xMin; x <= xMax; x++) {
if ((normalX == 0 || side.getX() == 0) && (normalY == 0 || side.getY() == 0) && (normalZ == 0 || side.getZ() == 0)) { for (int z = zMin; z <= zMax; z++) {
this.extendFaceFindBlocks( WorldChunk chunk = accessor.getChunk(ChunkUtil.indexChunkFromBlock(x, z));
accessor,
assetMap, for (int y = yMax; y >= yMin; y--) {
before, int currentBlock = chunk.getBlock(x, y, z);
after, int currentFluid = chunk.getFluidId(x, y, z);
x + side.getX(), if (currentBlock > 0) {
y + side.getY(), int xRes = x + normalX;
z + side.getZ(), int yRes = y + normalY;
normalX, int zRes = z + normalZ;
normalY, currentBlock = chunk.getBlock(xRes, yRes, zRes);
normalZ, before.addBlockAtWorldPos(
extrudeDepth, xRes,
blockId, yRes,
min, zRes,
max currentBlock,
); chunk.getRotationIndex(xRes, yRes, zRes),
} chunk.getFiller(xRes, yRes, zRes),
} chunk.getSupportValue(xRes, yRes, zRes),
} chunk.getBlockComponentHolder(xRes, yRes, zRes)
);
after.addBlockAtWorldPos(xRes, yRes, zRes, blockId, 0, 0, 0);
for (int i = 0; i < extrudeDepth; i++) {
int extrudedBlockX = xRes + normalX * i;
int extrudedBlockY = yRes + normalY * i;
int extrudedBlockZ = zRes + normalZ * i;
before.addBlockAtWorldPos(
extrudedBlockX,
extrudedBlockY,
extrudedBlockZ,
currentBlock,
chunk.getRotationIndex(extrudedBlockX, extrudedBlockY, extrudedBlockZ),
chunk.getFiller(extrudedBlockX, extrudedBlockY, extrudedBlockZ),
chunk.getSupportValue(extrudedBlockX, extrudedBlockY, extrudedBlockZ),
chunk.getBlockComponentHolder(extrudedBlockX, extrudedBlockY, extrudedBlockZ)
);
after.addBlockAtWorldPos(extrudedBlockX, extrudedBlockY, extrudedBlockZ, blockId, 0, 0, 0);
} }
} }
} }
@@ -2165,7 +2206,7 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider,
int settings, int settings,
@Nonnull ComponentAccessor<EntityStore> componentAccessor @Nonnull ComponentAccessor<EntityStore> componentAccessor
) throws PrefabCopyException { ) throws PrefabCopyException {
return this.copyOrCut(ref, xMin, yMin, zMin, xMax, yMax, zMax, settings, null, componentAccessor); return this.copyOrCut(ref, xMin, yMin, zMin, xMax, yMax, zMax, settings, null, null, componentAccessor);
} }
public int copyOrCut( public int copyOrCut(
@@ -2179,6 +2220,22 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider,
int settings, int settings,
@Nullable Vector3i playerAnchor, @Nullable Vector3i playerAnchor,
@Nonnull ComponentAccessor<EntityStore> componentAccessor @Nonnull ComponentAccessor<EntityStore> componentAccessor
) throws PrefabCopyException {
return this.copyOrCut(ref, xMin, yMin, zMin, xMax, yMax, zMax, settings, playerAnchor, null, componentAccessor);
}
public int copyOrCut(
@Nonnull Ref<EntityStore> ref,
int xMin,
int yMin,
int zMin,
int xMax,
int yMax,
int zMax,
int settings,
@Nullable Vector3i playerAnchor,
@Nullable Set<Ref<EntityStore>> skipEntityRemoveSnapshotFor,
@Nonnull ComponentAccessor<EntityStore> componentAccessor
) throws PrefabCopyException { ) throws PrefabCopyException {
World world = componentAccessor.getExternalData().getWorld(); World world = componentAccessor.getExternalData().getWorld();
long start = System.nanoTime(); long start = System.nanoTime();
@@ -2316,8 +2373,11 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider,
Holder<EntityStore> holder = store.copyEntity(e); Holder<EntityStore> holder = store.copyEntity(e);
this.selection.addEntityFromWorld(holder); this.selection.addEntityFromWorld(holder);
if (cut) { if (cut) {
snapshots.add(new EntityRemoveSnapshot(e)); boolean shouldSkip = skipEntityRemoveSnapshotFor != null && skipEntityRemoveSnapshotFor.contains(e);
entitiesToRemove.add(e); if (!shouldSkip) {
snapshots.add(new EntityRemoveSnapshot(e));
entitiesToRemove.add(e);
}
} }
}); });
if (cut && entitiesToRemove != null) { if (cut && entitiesToRemove != null) {
@@ -2433,36 +2493,48 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider,
return size; return size;
} }
public static RotationTuple transformRotation(RotationTuple prevRot, Matrix4d transformationMatrix) { public static RotationTuple transformRotation(RotationTuple prevRot, Quaterniond rotation) {
Vector3f forwardVec = new Vector3f(1.0F, 0.0F, 0.0F); Vector3f forwardVec = new Vector3f(1.0F, 0.0F, 0.0F);
Vector3f upVec = new Vector3f(0.0F, 1.0F, 0.0F); Vector3f upVec = new Vector3f(0.0F, 1.0F, 0.0F);
forwardVec = Rotation.rotate(forwardVec, prevRot.yaw(), prevRot.pitch(), prevRot.roll()); forwardVec = Rotation.rotate(forwardVec, prevRot.yaw(), prevRot.pitch(), prevRot.roll());
upVec = Rotation.rotate(upVec, prevRot.yaw(), prevRot.pitch(), prevRot.roll()); upVec = Rotation.rotate(upVec, prevRot.yaw(), prevRot.pitch(), prevRot.roll());
Vector3f newForward = transformationMatrix.multiplyDirection(forwardVec.toVector3d()).toVector3f(); org.joml.Vector3d fwd = rotation.transform(new org.joml.Vector3d(forwardVec.x, forwardVec.y, forwardVec.z));
Vector3f newUp = transformationMatrix.multiplyDirection(upVec.toVector3d()).toVector3f(); org.joml.Vector3d up = rotation.transform(new org.joml.Vector3d(upVec.x, upVec.y, upVec.z));
Vector3f newForward = new Vector3f((float)fwd.x, (float)fwd.y, (float)fwd.z);
Vector3f newUp = new Vector3f((float)up.x, (float)up.y, (float)up.z);
float bestScore = Float.MIN_VALUE; float bestScore = Float.MIN_VALUE;
RotationTuple bestRot = prevRot; RotationTuple bestRot = prevRot;
for (RotationTuple rotation : RotationTuple.VALUES) { for (RotationTuple rot : RotationTuple.VALUES) {
Vector3f rotForward = Rotation.rotate(new Vector3f(1.0F, 0.0F, 0.0F), rotation.yaw(), rotation.pitch(), rotation.roll()); Vector3f rotForward = Rotation.rotate(new Vector3f(1.0F, 0.0F, 0.0F), rot.yaw(), rot.pitch(), rot.roll());
Vector3f rotUp = Rotation.rotate(new Vector3f(0.0F, 1.0F, 0.0F), rotation.yaw(), rotation.pitch(), rotation.roll()); Vector3f rotUp = Rotation.rotate(new Vector3f(0.0F, 1.0F, 0.0F), rot.yaw(), rot.pitch(), rot.roll());
float score = rotForward.dot(newForward) + rotUp.dot(newUp); float score = rotForward.dot(newForward) + rotUp.dot(newUp);
if (score > bestScore) { if (score > bestScore) {
bestScore = score; bestScore = score;
bestRot = rotation; bestRot = rot;
} }
} }
return bestRot; return bestRot;
} }
private void transformEntityRotation(Vector3f rotation, Quaterniond deltaQuat) {
Quaterniond originalQuat = new Quaterniond().rotationYXZ(rotation.y, rotation.x, rotation.z);
Quaterniond resultQuat = deltaQuat.mul(originalQuat, new Quaterniond());
org.joml.Vector3d eulerAngles = resultQuat.getEulerAnglesYXZ(new org.joml.Vector3d());
rotation.assign((float)eulerAngles.x, (float)eulerAngles.y, (float)eulerAngles.z);
}
public void transformThenPasteClipboard( public void transformThenPasteClipboard(
@Nonnull BlockChange[] blockChanges, @Nonnull BlockChange[] blockChanges,
@Nullable PrototypePlayerBuilderToolSettings.FluidChange[] fluidChanges, @Nullable PrototypePlayerBuilderToolSettings.FluidChange[] fluidChanges,
@Nonnull Matrix4d transformationMatrix, @Nullable PrototypePlayerBuilderToolSettings.EntityChange[] entityChanges,
@Nonnull Quaterniond rotation,
@Nonnull Vector3i translationOffset,
@Nonnull Vector3f rotationOrigin, @Nonnull Vector3f rotationOrigin,
@Nonnull Vector3i initialPastePoint, @Nonnull Vector3i initialPastePoint,
boolean keepEmptyBlocks, boolean keepEmptyBlocks,
@Nonnull PrototypePlayerBuilderToolSettings prototypeSettings,
ComponentAccessor<EntityStore> componentAccessor ComponentAccessor<EntityStore> componentAccessor
) { ) {
World world = componentAccessor.getExternalData().getWorld(); World world = componentAccessor.getExternalData().getWorld();
@@ -2477,35 +2549,33 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider,
} }
} }
Vector4d translationEndResult = new Vector4d(0.0, 0.0, 0.0, 1.0); int centerX = translationOffset.x + (int)rotationOrigin.x;
transformationMatrix.multiply(translationEndResult); int centerY = translationOffset.y + (int)rotationOrigin.y;
translationEndResult.x = translationEndResult.x + rotationOrigin.x; int centerZ = translationOffset.z + (int)rotationOrigin.z;
translationEndResult.y = translationEndResult.y + rotationOrigin.y;
translationEndResult.z = translationEndResult.z + rotationOrigin.z;
BlockSelection before = new BlockSelection(); BlockSelection before = new BlockSelection();
before.setPosition((int)translationEndResult.x, (int)translationEndResult.y, (int)translationEndResult.z); before.setPosition(centerX, centerY, centerZ);
BlockSelection after = new BlockSelection(before); BlockSelection after = new BlockSelection(before);
LocalCachedChunkAccessor accessor = LocalCachedChunkAccessor.atWorldCoords(world, (int)translationEndResult.x, (int)translationEndResult.z, 50); LocalCachedChunkAccessor accessor = LocalCachedChunkAccessor.atWorldCoords(world, centerX, centerZ, 50);
int minX = Integer.MAX_VALUE; int minX = Integer.MAX_VALUE;
int minY = Integer.MAX_VALUE; int minY = Integer.MAX_VALUE;
int minZ = Integer.MAX_VALUE; int minZ = Integer.MAX_VALUE;
int maxX = Integer.MIN_VALUE; int maxX = Integer.MIN_VALUE;
int maxY = Integer.MIN_VALUE; int maxY = Integer.MIN_VALUE;
int maxZ = Integer.MIN_VALUE; int maxZ = Integer.MIN_VALUE;
Vector4d mutable4d = new Vector4d(0.0, 0.0, 0.0, 1.0); org.joml.Vector3d mutableVec = new org.joml.Vector3d();
for (BlockChange blockChangex : blockChanges) { for (BlockChange blockChangex : blockChanges) {
mutable4d.assign( mutableVec.set(
blockChangex.x - rotationOrigin.x + initialPastePoint.x + 0.5, blockChangex.x - rotationOrigin.x + initialPastePoint.x + 0.5,
blockChangex.y - rotationOrigin.y + initialPastePoint.y + 0.5 + yOffsetOutOfGround, blockChangex.y - rotationOrigin.y + initialPastePoint.y + 0.5 + yOffsetOutOfGround,
blockChangex.z - rotationOrigin.z + initialPastePoint.z + 0.5, blockChangex.z - rotationOrigin.z + initialPastePoint.z + 0.5
1.0
); );
transformationMatrix.multiply(mutable4d); rotation.transform(mutableVec);
mutableVec.add(translationOffset.x, translationOffset.y, translationOffset.z);
Vector3i rotatedLocation = new Vector3i( Vector3i rotatedLocation = new Vector3i(
(int)Math.floor(mutable4d.x + 0.1 + rotationOrigin.x - 0.5), (int)Math.floor(mutableVec.x + 0.1 + rotationOrigin.x - 0.5),
(int)Math.floor(mutable4d.y + 0.1 + rotationOrigin.y - 0.5), (int)Math.floor(mutableVec.y + 0.1 + rotationOrigin.y - 0.5),
(int)Math.floor(mutable4d.z + 0.1 + rotationOrigin.z - 0.5) (int)Math.floor(mutableVec.z + 0.1 + rotationOrigin.z - 0.5)
); );
minX = Math.min(minX, rotatedLocation.x); minX = Math.min(minX, rotatedLocation.x);
minY = Math.min(minY, rotatedLocation.y); minY = Math.min(minY, rotatedLocation.y);
@@ -2517,12 +2587,12 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider,
Holder<ChunkStore> holder = currentChunk.getBlockComponentHolder(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z); Holder<ChunkStore> holder = currentChunk.getBlockComponentHolder(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z);
int blockIdInRotatedLocation = currentChunk.getBlock(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z); int blockIdInRotatedLocation = currentChunk.getBlock(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z);
int filler = currentChunk.getFiller(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z); int filler = currentChunk.getFiller(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z);
int rotation = currentChunk.getRotationIndex(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z); int blockRotation = currentChunk.getRotationIndex(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z);
before.addBlockAtWorldPos(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z, blockIdInRotatedLocation, rotation, filler, 0, holder); before.addBlockAtWorldPos(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z, blockIdInRotatedLocation, blockRotation, filler, 0, holder);
int originalFluidId = currentChunk.getFluidId(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z); int originalFluidId = currentChunk.getFluidId(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z);
byte originalFluidLevel = currentChunk.getFluidLevel(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z); byte originalFluidLevel = currentChunk.getFluidLevel(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z);
before.addFluidAtWorldPos(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z, originalFluidId, originalFluidLevel); before.addFluidAtWorldPos(rotatedLocation.x, rotatedLocation.y, rotatedLocation.z, originalFluidId, originalFluidLevel);
int newRotation = transformRotation(RotationTuple.get(blockChangex.rotation), transformationMatrix).index(); int newRotation = transformRotation(RotationTuple.get(blockChangex.rotation), rotation).index();
int blockIdToPlace = blockChangex.block; int blockIdToPlace = blockChangex.block;
if (blockChangex.block == 0 && keepEmptyBlocks) { if (blockChangex.block == 0 && keepEmptyBlocks) {
blockIdToPlace = editorBlockPrefabAir; blockIdToPlace = editorBlockPrefabAir;
@@ -2557,27 +2627,92 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider,
int finalYOffsetOutOfGround = yOffsetOutOfGround; int finalYOffsetOutOfGround = yOffsetOutOfGround;
if (fluidChanges != null) { if (fluidChanges != null) {
for (PrototypePlayerBuilderToolSettings.FluidChange fluidChange : fluidChanges) { for (PrototypePlayerBuilderToolSettings.FluidChange fluidChange : fluidChanges) {
mutable4d.assign( mutableVec.set(
fluidChange.x() - rotationOrigin.x + initialPastePoint.x + 0.5, fluidChange.x() - rotationOrigin.x + initialPastePoint.x + 0.5,
fluidChange.y() - rotationOrigin.y + initialPastePoint.y + 0.5 + finalYOffsetOutOfGround, fluidChange.y() - rotationOrigin.y + initialPastePoint.y + 0.5 + finalYOffsetOutOfGround,
fluidChange.z() - rotationOrigin.z + initialPastePoint.z + 0.5, fluidChange.z() - rotationOrigin.z + initialPastePoint.z + 0.5
1.0
); );
transformationMatrix.multiply(mutable4d); rotation.transform(mutableVec);
mutableVec.add(translationOffset.x, translationOffset.y, translationOffset.z);
Vector3i rotatedLocationx = new Vector3i( Vector3i rotatedLocationx = new Vector3i(
(int)Math.floor(mutable4d.x + 0.1 + rotationOrigin.x - 0.5), (int)Math.floor(mutableVec.x + 0.1 + rotationOrigin.x - 0.5),
(int)Math.floor(mutable4d.y + 0.1 + rotationOrigin.y - 0.5), (int)Math.floor(mutableVec.y + 0.1 + rotationOrigin.y - 0.5),
(int)Math.floor(mutable4d.z + 0.1 + rotationOrigin.z - 0.5) (int)Math.floor(mutableVec.z + 0.1 + rotationOrigin.z - 0.5)
); );
after.addFluidAtWorldPos(rotatedLocationx.x, rotatedLocationx.y, rotatedLocationx.z, fluidChange.fluidId(), fluidChange.fluidLevel()); after.addFluidAtWorldPos(rotatedLocationx.x, rotatedLocationx.y, rotatedLocationx.z, fluidChange.fluidId(), fluidChange.fluidLevel());
} }
} }
List<Ref<EntityStore>> previousEntityRefs = prototypeSettings.getLastTransformEntityRefs();
List<EntityRemoveSnapshot> previousEntitySnapshots = new ArrayList<>();
if (previousEntityRefs != null) {
Store<EntityStore> entityStore = world.getEntityStore().getStore();
for (Ref<EntityStore> ref : previousEntityRefs) {
if (ref.isValid()) {
previousEntitySnapshots.add(new EntityRemoveSnapshot(ref));
entityStore.removeEntity(ref, RemoveReason.UNLOAD);
}
}
}
List<Ref<EntityStore>> addedEntityRefs = new ArrayList<>();
if (entityChanges != null && entityChanges.length > 0) {
org.joml.Vector3d mutableEntityPos = new org.joml.Vector3d();
for (PrototypePlayerBuilderToolSettings.EntityChange entityChange : entityChanges) {
boolean isBlockEntity = entityChange.entityHolder().getComponent(BlockEntity.getComponentType()) != null;
double blockCenterOffset = isBlockEntity ? 0.5 : 0.0;
mutableEntityPos.set(
entityChange.x() - rotationOrigin.x,
entityChange.y() + blockCenterOffset - rotationOrigin.y + finalYOffsetOutOfGround,
entityChange.z() - rotationOrigin.z
);
rotation.transform(mutableEntityPos);
mutableEntityPos.add(translationOffset.x, translationOffset.y, translationOffset.z);
double newX = mutableEntityPos.x + rotationOrigin.x;
double newY = mutableEntityPos.y + rotationOrigin.y - blockCenterOffset;
double newZ = mutableEntityPos.z + rotationOrigin.z;
Holder<EntityStore> clonedHolder = entityChange.entityHolder().clone();
TransformComponent transformComponent = clonedHolder.getComponent(TransformComponent.getComponentType());
if (transformComponent != null && transformComponent.getPosition() != null) {
transformComponent.getPosition().assign(newX, newY, newZ);
Vector3f entityRotation = transformComponent.getRotation();
if (entityRotation != null) {
this.transformEntityRotation(entityRotation, rotation);
}
}
HeadRotation headRotation = clonedHolder.getComponent(HeadRotation.getComponentType());
if (headRotation != null && headRotation.getRotation() != null) {
this.transformEntityRotation(headRotation.getRotation(), rotation);
}
clonedHolder.putComponent(UUIDComponent.getComponentType(), new UUIDComponent(UUID.randomUUID()));
clonedHolder.removeComponent(EntityTrackerSystems.Visible.getComponentType());
clonedHolder.removeComponent(NetworkId.getComponentType());
Ref<EntityStore> entityRef = componentAccessor.addEntity(clonedHolder, AddReason.LOAD);
addedEntityRefs.add(entityRef);
}
}
if (minX != Integer.MAX_VALUE) { if (minX != Integer.MAX_VALUE) {
before.setSelectionArea(new Vector3i(minX, minY, minZ), new Vector3i(maxX, maxY, maxZ)); before.setSelectionArea(new Vector3i(minX, minY, minZ), new Vector3i(maxX, maxY, maxZ));
} }
this.pushHistory(BuilderToolsPlugin.Action.ROTATE, new BlockSelectionSnapshot(before)); prototypeSettings.setLastTransformEntityRefs(new ArrayList<>(addedEntityRefs));
List<SelectionSnapshot<?>> snapshots = new ObjectArrayList<>(addedEntityRefs.size() + previousEntitySnapshots.size() + 1);
for (Ref<EntityStore> entityRef : addedEntityRefs) {
snapshots.add(new EntityAddSnapshot(entityRef));
}
for (EntityRemoveSnapshot snapshot : previousEntitySnapshots) {
snapshots.add(snapshot);
}
snapshots.add(new BlockSelectionSnapshot(before));
this.pushHistory(BuilderToolsPlugin.Action.ROTATE, snapshots);
after.placeNoReturn("Transform 1/1", this.player, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor); after.placeNoReturn("Transform 1/1", this.player, BuilderToolsPlugin.FEEDBACK_CONSUMER, world, componentAccessor);
BuilderToolsPlugin.invalidateWorldMapForSelection(after, world); BuilderToolsPlugin.invalidateWorldMapForSelection(after, world);
long end = System.nanoTime(); long end = System.nanoTime();
@@ -2590,24 +2725,27 @@ public class BuilderToolsPlugin extends JavaPlugin implements SelectionProvider,
this.sendArea(); this.sendArea();
} }
public void transformSelectionPoints(@Nonnull Matrix4d transformationMatrix, @Nonnull Vector3f rotationOrigin) { public void transformSelectionPoints(@Nonnull Quaterniond rotation, @Nonnull Vector3i translationOffset, @Nonnull Vector3f rotationOrigin) {
Vector3i newMin = this.transformBlockLocation(this.selection.getSelectionMin(), transformationMatrix, rotationOrigin); Vector3i newMin = this.transformBlockLocation(this.selection.getSelectionMin(), rotation, translationOffset, rotationOrigin);
Vector3i newMax = this.transformBlockLocation(this.selection.getSelectionMax(), transformationMatrix, rotationOrigin); Vector3i newMax = this.transformBlockLocation(this.selection.getSelectionMax(), rotation, translationOffset, rotationOrigin);
this.selection.setSelectionArea(Vector3i.min(newMin, newMax), Vector3i.max(newMin, newMax)); this.selection.setSelectionArea(Vector3i.min(newMin, newMax), Vector3i.max(newMin, newMax));
this.sendUpdate(); this.sendUpdate();
this.sendArea(); this.sendArea();
} }
@Nonnull @Nonnull
public Vector3i transformBlockLocation(@Nonnull Vector3i blockLocation, @Nonnull Matrix4d transformationMatrix, @Nonnull Vector3f rotationOrigin) { public Vector3i transformBlockLocation(
Vector4d relativeOffset = new Vector4d( @Nonnull Vector3i blockLocation, @Nonnull Quaterniond rotation, @Nonnull Vector3i translationOffset, @Nonnull Vector3f rotationOrigin
blockLocation.x - rotationOrigin.x + 0.5, blockLocation.y - rotationOrigin.y + 0.5, blockLocation.z - rotationOrigin.z + 0.5, 1.0 ) {
org.joml.Vector3d relative = new org.joml.Vector3d(
blockLocation.x - rotationOrigin.x + 0.5, blockLocation.y - rotationOrigin.y + 0.5, blockLocation.z - rotationOrigin.z + 0.5
); );
transformationMatrix.multiply(relativeOffset); rotation.transform(relative);
relative.add(translationOffset.x, translationOffset.y, translationOffset.z);
return new Vector3i( return new Vector3i(
(int)Math.floor(relativeOffset.x + rotationOrigin.x - 0.5 + 0.1), (int)Math.floor(relative.x + rotationOrigin.x - 0.5 + 0.1),
(int)Math.floor(relativeOffset.y + rotationOrigin.y - 0.5 + 0.1), (int)Math.floor(relative.y + rotationOrigin.y - 0.5 + 0.1),
(int)Math.floor(relativeOffset.z + rotationOrigin.z - 0.5 + 0.1) (int)Math.floor(relative.z + rotationOrigin.z - 0.5 + 0.1)
); );
} }

View File

@@ -4,6 +4,7 @@ import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.BrushConfig;
import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.BrushConfigCommandExecutor; import com.hypixel.hytale.builtin.buildertools.scriptedbrushes.BrushConfigCommandExecutor;
import com.hypixel.hytale.builtin.buildertools.tooloperations.ToolOperation; import com.hypixel.hytale.builtin.buildertools.tooloperations.ToolOperation;
import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.math.block.BlockUtil; import com.hypixel.hytale.math.block.BlockUtil;
import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.math.vector.Vector3i;
@@ -14,6 +15,7 @@ import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -38,9 +40,13 @@ public class PrototypePlayerBuilderToolSettings {
@Nullable @Nullable
private PrototypePlayerBuilderToolSettings.FluidChange[] fluidChangesForPlaySelectionToolPasteMode = null; private PrototypePlayerBuilderToolSettings.FluidChange[] fluidChangesForPlaySelectionToolPasteMode = null;
@Nullable @Nullable
private PrototypePlayerBuilderToolSettings.EntityChange[] entityChangesForPlaySelectionToolPasteMode = null;
@Nullable
private Vector3i lastBrushPosition = null; private Vector3i lastBrushPosition = null;
@Nullable @Nullable
private Vector3i blockChangeOffsetOrigin = null; private Vector3i blockChangeOffsetOrigin = null;
@Nullable
private List<Ref<EntityStore>> lastTransformEntityRefs = null;
public PrototypePlayerBuilderToolSettings(UUID player) { public PrototypePlayerBuilderToolSettings(UUID player) {
this.player = player; this.player = player;
@@ -60,6 +66,7 @@ public class PrototypePlayerBuilderToolSettings {
if (!this.isInSelectionTransformationMode) { if (!this.isInSelectionTransformationMode) {
this.blockChangesForPlaySelectionToolPasteMode = null; this.blockChangesForPlaySelectionToolPasteMode = null;
this.fluidChangesForPlaySelectionToolPasteMode = null; this.fluidChangesForPlaySelectionToolPasteMode = null;
this.entityChangesForPlaySelectionToolPasteMode = null;
this.blockChangeOffsetOrigin = null; this.blockChangeOffsetOrigin = null;
} }
} }
@@ -98,6 +105,15 @@ public class PrototypePlayerBuilderToolSettings {
return this.fluidChangesForPlaySelectionToolPasteMode; return this.fluidChangesForPlaySelectionToolPasteMode;
} }
public void setEntityChangesForPlaySelectionToolPasteMode(@Nullable PrototypePlayerBuilderToolSettings.EntityChange[] entityChanges) {
this.entityChangesForPlaySelectionToolPasteMode = entityChanges;
}
@Nullable
public PrototypePlayerBuilderToolSettings.EntityChange[] getEntityChangesForPlaySelectionToolPasteMode() {
return this.entityChangesForPlaySelectionToolPasteMode;
}
public void setBlockChangeOffsetOrigin(@Nullable Vector3i blockChangeOffsetOrigin) { public void setBlockChangeOffsetOrigin(@Nullable Vector3i blockChangeOffsetOrigin) {
this.blockChangeOffsetOrigin = blockChangeOffsetOrigin; this.blockChangeOffsetOrigin = blockChangeOffsetOrigin;
} }
@@ -107,6 +123,19 @@ public class PrototypePlayerBuilderToolSettings {
return this.blockChangeOffsetOrigin; return this.blockChangeOffsetOrigin;
} }
public void setLastTransformEntityRefs(@Nullable List<Ref<EntityStore>> refs) {
this.lastTransformEntityRefs = refs;
}
@Nullable
public List<Ref<EntityStore>> getLastTransformEntityRefs() {
return this.lastTransformEntityRefs;
}
public void clearLastTransformEntityRefs() {
this.lastTransformEntityRefs = null;
}
@Nonnull @Nonnull
public LongOpenHashSet addIgnoredPaintOperation() { public LongOpenHashSet addIgnoredPaintOperation() {
LongOpenHashSet longs = new LongOpenHashSet(); LongOpenHashSet longs = new LongOpenHashSet();
@@ -202,6 +231,9 @@ public class PrototypePlayerBuilderToolSettings {
} }
} }
public record EntityChange(double x, double y, double z, Holder<EntityStore> entityHolder) {
}
public record FluidChange(int x, int y, int z, int fluidId, byte fluidLevel) { public record FluidChange(int x, int y, int z, int fluidId, byte fluidLevel) {
} }
} }

View File

@@ -25,7 +25,9 @@ public class ContractSelectionCommand extends AbstractPlayerCommand {
@Nonnull @Nonnull
private final RequiredArg<Integer> distanceArg = this.withRequiredArg("distance", "server.commands.contract.arg.distance.desc", ArgTypes.INTEGER); private final RequiredArg<Integer> distanceArg = this.withRequiredArg("distance", "server.commands.contract.arg.distance.desc", ArgTypes.INTEGER);
@Nonnull @Nonnull
private final OptionalArg<List<Axis>> axisArg = this.withListOptionalArg("axis", "command.contract.arg.axis.desc", ArgTypes.forEnum("Axis", Axis.class)); private final OptionalArg<List<Axis>> axisArg = this.withListOptionalArg(
"axis", "server.commands.contract.arg.axis.desc", ArgTypes.forEnum("Axis", Axis.class)
);
public ContractSelectionCommand() { public ContractSelectionCommand() {
super("contractSelection", "server.commands.contract.desc"); super("contractSelection", "server.commands.contract.desc");

View File

@@ -89,7 +89,7 @@ public class BrushConfig {
this.executionErrorMessage = null; this.executionErrorMessage = null;
this.originOffset = new Vector3i(0, 0, 0); this.originOffset = new Vector3i(0, 0, 0);
this.originAfterOffset = null; this.originAfterOffset = null;
this.shape = BrushShape.Cube; this.shape = BrushShape.Sphere;
this.shapeWidth = 5; this.shapeWidth = 5;
this.shapeHeight = 5; this.shapeHeight = 5;
this.shapeThickness = 0; this.shapeThickness = 0;

View File

@@ -20,7 +20,7 @@ public class ShapeOperation extends SequenceBrushOperation {
.documentation("Changes the shape of the brush editing area") .documentation("Changes the shape of the brush editing area")
.build(); .build();
@Nonnull @Nonnull
public BrushShape brushShapeArg = BrushShape.Cube; public BrushShape brushShapeArg = BrushShape.Sphere;
public ShapeOperation() { public ShapeOperation() {
super("Shape", "Changes the shape of the brush editing area", false); super("Shape", "Changes the shape of the brush editing area", false);

View File

@@ -4,6 +4,8 @@ import com.hypixel.hytale.builtin.creativehub.command.HubCommand;
import com.hypixel.hytale.builtin.creativehub.config.CreativeHubEntityConfig; import com.hypixel.hytale.builtin.creativehub.config.CreativeHubEntityConfig;
import com.hypixel.hytale.builtin.creativehub.config.CreativeHubWorldConfig; import com.hypixel.hytale.builtin.creativehub.config.CreativeHubWorldConfig;
import com.hypixel.hytale.builtin.creativehub.interactions.HubPortalInteraction; import com.hypixel.hytale.builtin.creativehub.interactions.HubPortalInteraction;
import com.hypixel.hytale.builtin.creativehub.systems.ReturnToHubButtonSystem;
import com.hypixel.hytale.builtin.creativehub.ui.ReturnToHubButtonUI;
import com.hypixel.hytale.builtin.instances.InstancesPlugin; import com.hypixel.hytale.builtin.instances.InstancesPlugin;
import com.hypixel.hytale.builtin.instances.config.InstanceEntityConfig; import com.hypixel.hytale.builtin.instances.config.InstanceEntityConfig;
import com.hypixel.hytale.builtin.instances.config.InstanceWorldConfig; import com.hypixel.hytale.builtin.instances.config.InstanceWorldConfig;
@@ -100,7 +102,10 @@ public class CreativeHubPlugin extends JavaPlugin {
config -> { config -> {
config.setUuid(UUID.randomUUID()); config.setUuid(UUID.randomUUID());
config.setDeleteOnRemove(false); config.setDeleteOnRemove(false);
config.setDisplayName(WorldConfig.formatDisplayName(instanceAssetName)); if (config.getDisplayName() == null) {
config.setDisplayName(WorldConfig.formatDisplayName(instanceAssetName));
}
config.getPluginConfig().remove(InstanceWorldConfig.class); config.getPluginConfig().remove(InstanceWorldConfig.class);
config.markChanged(); config.markChanged();
long start = System.nanoTime(); long start = System.nanoTime();
@@ -148,9 +153,11 @@ public class CreativeHubPlugin extends JavaPlugin {
this.getCodecRegistry(WorldConfig.PLUGIN_CODEC).register(CreativeHubWorldConfig.class, "CreativeHub", CreativeHubWorldConfig.CODEC); this.getCodecRegistry(WorldConfig.PLUGIN_CODEC).register(CreativeHubWorldConfig.class, "CreativeHub", CreativeHubWorldConfig.CODEC);
this.creativeHubEntityConfigComponentType = this.getEntityStoreRegistry() this.creativeHubEntityConfigComponentType = this.getEntityStoreRegistry()
.registerComponent(CreativeHubEntityConfig.class, "CreativeHub", CreativeHubEntityConfig.CODEC); .registerComponent(CreativeHubEntityConfig.class, "CreativeHub", CreativeHubEntityConfig.CODEC);
this.getEntityStoreRegistry().registerSystem(new ReturnToHubButtonSystem());
this.getEventRegistry().registerGlobal(PlayerConnectEvent.class, CreativeHubPlugin::onPlayerConnect); this.getEventRegistry().registerGlobal(PlayerConnectEvent.class, CreativeHubPlugin::onPlayerConnect);
this.getEventRegistry().registerGlobal(RemoveWorldEvent.class, CreativeHubPlugin::onWorldRemove); this.getEventRegistry().registerGlobal(RemoveWorldEvent.class, CreativeHubPlugin::onWorldRemove);
this.getEventRegistry().registerGlobal(AddPlayerToWorldEvent.class, CreativeHubPlugin::onPlayerAddToWorld); this.getEventRegistry().registerGlobal(AddPlayerToWorldEvent.class, CreativeHubPlugin::onPlayerAddToWorld);
ReturnToHubButtonUI.register();
} }
private static void onWorldRemove(@Nonnull RemoveWorldEvent event) { private static void onWorldRemove(@Nonnull RemoveWorldEvent event) {
@@ -212,9 +219,11 @@ public class CreativeHubPlugin extends JavaPlugin {
World parentWorld = Universe.get().getWorld(hubEntityConfig.getParentHubWorldUuid()); World parentWorld = Universe.get().getWorld(hubEntityConfig.getParentHubWorldUuid());
if (parentWorld != null) { if (parentWorld != null) {
World hubInstance = get().getActiveHubInstance(parentWorld); World hubInstance = get().getActiveHubInstance(parentWorld);
if (!world.equals(hubInstance)) { boolean isInHubInstance = world.equals(hubInstance);
PlayerRef playerRef = holder.getComponent(PlayerRef.getComponentType()); PlayerRef playerRef = holder.getComponent(PlayerRef.getComponentType());
if (playerRef != null) { if (playerRef != null) {
ReturnToHubButtonUI.send(playerRef, isInHubInstance);
if (!isInHubInstance) {
world.execute(() -> playerRef.sendMessage(MESSAGE_HUB_RETURN_HINT)); world.execute(() -> playerRef.sendMessage(MESSAGE_HUB_RETURN_HINT));
} }
} }

View File

@@ -27,7 +27,7 @@ public class HubCommand extends AbstractPlayerCommand {
public HubCommand() { public HubCommand() {
super("hub", "server.commands.hub.desc"); super("hub", "server.commands.hub.desc");
this.addAliases("converge", "convergence"); this.addAliases("cosmos", "crossroads");
this.setPermissionGroup(GameMode.Creative); this.setPermissionGroup(GameMode.Creative);
} }

View File

@@ -159,47 +159,49 @@ public class HubPortalInteraction extends SimpleInstantInteraction {
} else { } else {
UUID playerUUID = uuidComponent.getUuid(); UUID playerUUID = uuidComponent.getUuid();
CreativeHubEntityConfig hubEntityConfig = componentAccessor.getComponent(playerRef, CreativeHubEntityConfig.getComponentType()); CreativeHubEntityConfig hubEntityConfig = componentAccessor.getComponent(playerRef, CreativeHubEntityConfig.getComponentType());
originalWorld.execute(playerRefComponent::removeFromStore); CompletableFuture.runAsync(playerRefComponent::removeFromStore, originalWorld)
worldFuture.orTimeout(1L, TimeUnit.MINUTES).thenCompose(world -> { .thenCombine(worldFuture.orTimeout(1L, TimeUnit.MINUTES), (v, world) -> (World)world)
PlayerWorldData worldData = perWorldData.get(world.getName()); .thenCompose(world -> {
if (worldData != null && worldData.getLastPosition() != null) { PlayerWorldData worldData = perWorldData.get(world.getName());
return world.addPlayer(playerRefComponent, worldData.getLastPosition(), Boolean.TRUE, Boolean.FALSE); if (worldData != null && worldData.getLastPosition() != null) {
} else { return world.addPlayer(playerRefComponent, worldData.getLastPosition(), Boolean.TRUE, Boolean.FALSE);
ISpawnProvider spawnProvider = world.getWorldConfig().getSpawnProvider();
Transform spawnPoint = spawnProvider != null ? spawnProvider.getSpawnPoint(world, playerUUID) : null;
return world.addPlayer(playerRefComponent, spawnPoint, Boolean.TRUE, Boolean.FALSE);
}
}).whenComplete((ret, ex) -> {
if (ex != null) {
LOGGER.at(Level.SEVERE).withCause(ex).log("Failed to teleport %s to permanent world", playerRefComponent.getUsername());
}
if (ret == null) {
if (originalWorld.isAlive()) {
originalWorld.addPlayer(playerRefComponent, originalPosition, Boolean.TRUE, Boolean.FALSE);
} else { } else {
if (hubEntityConfig != null && hubEntityConfig.getParentHubWorldUuid() != null) { ISpawnProvider spawnProvider = world.getWorldConfig().getSpawnProvider();
World parentWorld = Universe.get().getWorld(hubEntityConfig.getParentHubWorldUuid()); Transform spawnPoint = spawnProvider != null ? spawnProvider.getSpawnPoint(world, playerUUID) : null;
if (parentWorld != null) { return world.addPlayer(playerRefComponent, spawnPoint, Boolean.TRUE, Boolean.FALSE);
CreativeHubWorldConfig parentHubConfig = CreativeHubWorldConfig.get(parentWorld.getWorldConfig()); }
if (parentHubConfig != null && parentHubConfig.getStartupInstance() != null) { })
World hubInstance = CreativeHubPlugin.get().getOrSpawnHubInstance(parentWorld, parentHubConfig, new Transform()); .whenComplete((ret, ex) -> {
hubInstance.addPlayer(playerRefComponent, null, Boolean.TRUE, Boolean.FALSE); if (ex != null) {
return; LOGGER.at(Level.SEVERE).withCause(ex).log("Failed to teleport %s to permanent world", playerRefComponent.getUsername());
}
if (ret == null) {
if (originalWorld.isAlive()) {
originalWorld.addPlayer(playerRefComponent, originalPosition, Boolean.TRUE, Boolean.FALSE);
} else {
if (hubEntityConfig != null && hubEntityConfig.getParentHubWorldUuid() != null) {
World parentWorld = Universe.get().getWorld(hubEntityConfig.getParentHubWorldUuid());
if (parentWorld != null) {
CreativeHubWorldConfig parentHubConfig = CreativeHubWorldConfig.get(parentWorld.getWorldConfig());
if (parentHubConfig != null && parentHubConfig.getStartupInstance() != null) {
World hubInstance = CreativeHubPlugin.get().getOrSpawnHubInstance(parentWorld, parentHubConfig, new Transform());
hubInstance.addPlayer(playerRefComponent, null, Boolean.TRUE, Boolean.FALSE);
return;
}
} }
} }
}
World defaultWorld = Universe.get().getDefaultWorld(); World defaultWorld = Universe.get().getDefaultWorld();
if (defaultWorld != null) { if (defaultWorld != null) {
defaultWorld.addPlayer(playerRefComponent, null, Boolean.TRUE, Boolean.FALSE); defaultWorld.addPlayer(playerRefComponent, null, Boolean.TRUE, Boolean.FALSE);
} else { } else {
LOGGER.at(Level.SEVERE).log("No fallback world available for %s, disconnecting", playerRefComponent.getUsername()); LOGGER.at(Level.SEVERE).log("No fallback world available for %s, disconnecting", playerRefComponent.getUsername());
playerRefComponent.getPacketHandler().disconnect("Failed to teleport - no world available"); playerRefComponent.getPacketHandler().disconnect("Failed to teleport - no world available");
}
} }
} }
} });
});
} }
} }
} }

View File

@@ -0,0 +1,66 @@
package com.hypixel.hytale.builtin.creativehub.systems;
import com.hypixel.hytale.builtin.creativehub.CreativeHubPlugin;
import com.hypixel.hytale.builtin.creativehub.config.CreativeHubEntityConfig;
import com.hypixel.hytale.builtin.creativehub.ui.ReturnToHubButtonUI;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.RefSystem;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import java.util.Optional;
import javax.annotation.Nonnull;
public class ReturnToHubButtonSystem extends RefSystem<EntityStore> {
@Override
public void onEntityAdded(
@Nonnull Ref<EntityStore> ref, @Nonnull AddReason reason, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer
) {
Optional<Boolean> isInChildWorld = this.getCreativeHubWorldStatus(store, commandBuffer, ref);
if (!isInChildWorld.isEmpty()) {
PlayerRef playerRef = commandBuffer.getComponent(ref, PlayerRef.getComponentType());
boolean disabled = !isInChildWorld.get();
ReturnToHubButtonUI.send(playerRef, disabled);
}
}
@Override
public void onEntityRemove(
@Nonnull Ref<EntityStore> ref, @Nonnull RemoveReason reason, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer
) {
if (!this.getCreativeHubWorldStatus(store, commandBuffer, ref).isEmpty()) {
PlayerRef playerRef = commandBuffer.getComponent(ref, PlayerRef.getComponentType());
ReturnToHubButtonUI.clear(playerRef);
}
}
@Override
public Query<EntityStore> getQuery() {
return Query.and(Player.getComponentType(), PlayerRef.getComponentType());
}
private Optional<Boolean> getCreativeHubWorldStatus(
@Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer, @Nonnull Ref<EntityStore> ref
) {
CreativeHubEntityConfig hubEntityConfig = commandBuffer.getComponent(ref, CreativeHubEntityConfig.getComponentType());
if (hubEntityConfig != null && hubEntityConfig.getParentHubWorldUuid() != null) {
World parentWorld = Universe.get().getWorld(hubEntityConfig.getParentHubWorldUuid());
if (parentWorld == null) {
return Optional.empty();
} else {
World currentWorld = store.getExternalData().getWorld();
World hubInstance = CreativeHubPlugin.get().getActiveHubInstance(parentWorld);
return Optional.of(!currentWorld.equals(hubInstance));
}
} else {
return Optional.empty();
}
}
}

View File

@@ -0,0 +1,87 @@
package com.hypixel.hytale.builtin.creativehub.ui;
import com.hypixel.hytale.builtin.creativehub.CreativeHubPlugin;
import com.hypixel.hytale.builtin.creativehub.config.CreativeHubEntityConfig;
import com.hypixel.hytale.builtin.creativehub.config.CreativeHubWorldConfig;
import com.hypixel.hytale.builtin.instances.InstancesPlugin;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.math.vector.Transform;
import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType;
import com.hypixel.hytale.protocol.packets.interface_.UpdateAnchorUI;
import com.hypixel.hytale.server.core.modules.anchoraction.AnchorActionModule;
import com.hypixel.hytale.server.core.ui.builder.EventData;
import com.hypixel.hytale.server.core.ui.builder.UICommandBuilder;
import com.hypixel.hytale.server.core.ui.builder.UIEventBuilder;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.spawn.ISpawnProvider;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public final class ReturnToHubButtonUI {
public static final String ANCHOR_ID = "MapServerContent";
public static final String ACTION_RETURN_TO_HUB = "returnToHub";
private ReturnToHubButtonUI() {
}
public static void register() {
AnchorActionModule.get().register("returnToHub", (playerRef, ref, store, data) -> executeReturnToHub(playerRef, ref, store));
}
public static void send(@Nonnull PlayerRef playerRef) {
send(playerRef, false);
}
public static void send(@Nonnull PlayerRef playerRef, boolean disabled) {
UICommandBuilder commandBuilder = new UICommandBuilder();
commandBuilder.append("Hud/ReturnToHubButton.ui");
commandBuilder.set("#ReturnToHubButton.Disabled", disabled);
UIEventBuilder eventBuilder = new UIEventBuilder();
if (!disabled) {
eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, "#ReturnToHubButton", EventData.of("action", "returnToHub"), false);
}
playerRef.getPacketHandler().writeNoCache(new UpdateAnchorUI("MapServerContent", true, commandBuilder.getCommands(), eventBuilder.getEvents()));
}
public static void clear(@Nonnull PlayerRef playerRef) {
playerRef.getPacketHandler().writeNoCache(new UpdateAnchorUI("MapServerContent", true, null, null));
}
public static void executeReturnToHub(@Nonnull PlayerRef playerRef, @Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store) {
World parentWorld = findParentHubWorld(store, ref);
if (parentWorld != null) {
CreativeHubWorldConfig hubConfig = CreativeHubWorldConfig.get(parentWorld.getWorldConfig());
if (hubConfig != null && hubConfig.getStartupInstance() != null) {
World world = store.getExternalData().getWorld();
World currentHub = CreativeHubPlugin.get().getActiveHubInstance(parentWorld);
if (!world.equals(currentHub)) {
ISpawnProvider spawnProvider = parentWorld.getWorldConfig().getSpawnProvider();
Transform returnPoint = spawnProvider != null ? spawnProvider.getSpawnPoint(parentWorld, playerRef.getUuid()) : new Transform();
World hubInstance = CreativeHubPlugin.get().getOrSpawnHubInstance(parentWorld, hubConfig, returnPoint);
InstancesPlugin.teleportPlayerToInstance(ref, store, hubInstance, null);
}
}
}
}
@Nullable
private static World findParentHubWorld(@Nonnull Store<EntityStore> store, @Nonnull Ref<EntityStore> ref) {
CreativeHubEntityConfig hubEntityConfig = store.getComponent(ref, CreativeHubEntityConfig.getComponentType());
if (hubEntityConfig != null && hubEntityConfig.getParentHubWorldUuid() != null) {
World parentWorld = Universe.get().getWorld(hubEntityConfig.getParentHubWorldUuid());
if (parentWorld != null) {
CreativeHubWorldConfig hubConfig = CreativeHubWorldConfig.get(parentWorld.getWorldConfig());
if (hubConfig != null && hubConfig.getStartupInstance() != null) {
return parentWorld;
}
}
}
return null;
}
}

View File

@@ -108,21 +108,21 @@ public class FluidPlugin extends JavaPlugin {
int y = ChunkUtil.minBlock(fluidSectionComponent.getY()) + ChunkUtil.yFromIndex(idx); int y = ChunkUtil.minBlock(fluidSectionComponent.getY()) + ChunkUtil.yFromIndex(idx);
int z = ChunkUtil.minBlock(fluidSectionComponent.getZ()) + ChunkUtil.zFromIndex(idx); int z = ChunkUtil.minBlock(fluidSectionComponent.getZ()) + ChunkUtil.zFromIndex(idx);
boolean canSpread = ChunkUtil.isBorderBlock(x, z) boolean canSpread = ChunkUtil.isBorderBlock(x, z)
|| fluidSectionComponent.getFluidId(x - 1, y, z) == 0 || fluidSectionComponent.getFluidId(x - 1, y, z) != fluidId
&& !FluidTicker.isSolid(blockMap.getAsset(blockSectionComponent.get(x - 1, y, z))) && !FluidTicker.isSolid(blockMap.getAsset(blockSectionComponent.get(x - 1, y, z)))
|| fluidSectionComponent.getFluidId(x + 1, y, z) == 0 || fluidSectionComponent.getFluidId(x + 1, y, z) != fluidId
&& !FluidTicker.isSolid(blockMap.getAsset(blockSectionComponent.get(x + 1, y, z))) && !FluidTicker.isSolid(blockMap.getAsset(blockSectionComponent.get(x + 1, y, z)))
|| fluidSectionComponent.getFluidId(x, y, z - 1) == 0 || fluidSectionComponent.getFluidId(x, y, z - 1) != fluidId
&& !FluidTicker.isSolid(blockMap.getAsset(blockSectionComponent.get(x, y, z - 1))) && !FluidTicker.isSolid(blockMap.getAsset(blockSectionComponent.get(x, y, z - 1)))
|| fluidSectionComponent.getFluidId(x, y, z + 1) == 0 || fluidSectionComponent.getFluidId(x, y, z + 1) != fluidId
&& !FluidTicker.isSolid(blockMap.getAsset(blockSectionComponent.get(x, y, z + 1))); && !FluidTicker.isSolid(blockMap.getAsset(blockSectionComponent.get(x, y, z + 1)));
if (y > 0) { if (y > 0) {
if (ChunkUtil.chunkCoordinate(y) == ChunkUtil.chunkCoordinate(y - 1)) { if (ChunkUtil.chunkCoordinate(y) == ChunkUtil.chunkCoordinate(y - 1)) {
canSpread |= fluidSectionComponent.getFluidId(x, y - 1, z) == 0 canSpread |= fluidSectionComponent.getFluidId(x, y - 1, z) != fluidId
&& !FluidTicker.isSolid(blockMap.getAsset(blockSectionComponent.get(x, y - 1, z))); && !FluidTicker.isSolid(blockMap.getAsset(blockSectionComponent.get(x, y - 1, z)));
} else { } else {
FluidSection fluidSection2 = sections[i - 1].getComponent(FluidSection.getComponentType()); FluidSection fluidSection2 = sections[i - 1].getComponent(FluidSection.getComponentType());
canSpread |= fluidSection2.getFluidId(x, y - 1, z) == 0 canSpread |= fluidSection2.getFluidId(x, y - 1, z) != fluidId
&& !FluidTicker.isSolid(blockMap.getAsset(blockChunkComponent.getBlock(x, y - 1, z))); && !FluidTicker.isSolid(blockMap.getAsset(blockChunkComponent.getBlock(x, y - 1, z)));
} }
} }

View File

@@ -19,7 +19,7 @@ import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.component.system.tick.RunWhenPausedSystem; import com.hypixel.hytale.component.system.tick.RunWhenPausedSystem;
import com.hypixel.hytale.logger.HytaleLogger; import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.math.util.ChunkUtil; import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.ToClientPacket;
import com.hypixel.hytale.protocol.packets.world.ServerSetFluid; import com.hypixel.hytale.protocol.packets.world.ServerSetFluid;
import com.hypixel.hytale.protocol.packets.world.ServerSetFluids; import com.hypixel.hytale.protocol.packets.world.ServerSetFluids;
import com.hypixel.hytale.protocol.packets.world.SetFluidCmd; import com.hypixel.hytale.protocol.packets.world.SetFluidCmd;
@@ -109,7 +109,7 @@ public class FluidSystems {
Store<ChunkStore> store, Store<ChunkStore> store,
@Nonnull CommandBuffer<ChunkStore> commandBuffer, @Nonnull CommandBuffer<ChunkStore> commandBuffer,
PlayerRef query, PlayerRef query,
@Nonnull List<CompletableFuture<Packet>> results @Nonnull List<CompletableFuture<ToClientPacket>> results
) { ) {
ChunkColumn chunkColumnComponent = archetypeChunk.getComponent(index, this.chunkColumnComponentType); ChunkColumn chunkColumnComponent = archetypeChunk.getComponent(index, this.chunkColumnComponentType);

View File

@@ -43,7 +43,7 @@ public class MultiMixDensityAsset extends DensityAsset {
ArrayList<MultiMixDensity.Key> keys = new ArrayList<>(this.keyAssets.length); ArrayList<MultiMixDensity.Key> keys = new ArrayList<>(this.keyAssets.length);
for (MultiMixDensityAsset.KeyAsset keyAsset : this.keyAssets) { for (MultiMixDensityAsset.KeyAsset keyAsset : this.keyAssets) {
if (keyAsset.densityIndex <= 0) { if (keyAsset.densityIndex < 0) {
keys.add(new MultiMixDensity.Key(keyAsset.value, null)); keys.add(new MultiMixDensity.Key(keyAsset.value, null));
} else if (keyAsset.densityIndex >= densityInputs.size() - 1) { } else if (keyAsset.densityIndex >= densityInputs.size() - 1) {
LoggerUtil.getLogger() LoggerUtil.getLogger()

View File

@@ -1,7 +1,7 @@
package com.hypixel.hytale.builtin.hytalegenerator.assets.patterns; package com.hypixel.hytale.builtin.hytalegenerator.assets.patterns;
import com.hypixel.hytale.builtin.hytalegenerator.patterns.CeilingPattern;
import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern; import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern;
import com.hypixel.hytale.builtin.hytalegenerator.patterns.SurfacePattern;
import com.hypixel.hytale.codec.KeyedCodec; import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec; import com.hypixel.hytale.codec.builder.BuilderCodec;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@@ -27,7 +27,7 @@ public class CeilingPatternAsset extends PatternAsset {
} else { } else {
Pattern ceilingPattern = this.ceiling.build(argument); Pattern ceilingPattern = this.ceiling.build(argument);
Pattern originPattern = this.origin.build(argument); Pattern originPattern = this.origin.build(argument);
return new CeilingPattern(ceilingPattern, originPattern); return new SurfacePattern(ceilingPattern, originPattern, 0.0, 0.0, SurfacePattern.Facing.D, 0, 0);
} }
} }

View File

@@ -389,7 +389,6 @@ public class NTerrainStage implements NStage {
for (int y = this.top; y >= this.bottom; y--) { for (int y = this.top; y >= this.bottom; y--) {
position.y = y; position.y = y;
positionAbove.y = y + 1; positionAbove.y = y + 1;
positionBelow.y = y - 1;
int i = y - this.bottom; int i = y - this.bottom;
float density = this.densityBuffer.get(position); float density = this.densityBuffer.get(position);
boolean solidity = density > 0.0; boolean solidity = density > 0.0;
@@ -415,6 +414,8 @@ public class NTerrainStage implements NStage {
} }
for (int yx = this.bottom; yx <= this.top; yx++) { for (int yx = this.bottom; yx <= this.top; yx++) {
position.y = yx;
positionBelow.y = yx - 1;
int i = yx - this.bottom; int i = yx - this.bottom;
double density = this.densityBuffer.get(position); double density = this.densityBuffer.get(position);
boolean solidity = density > 0.0; boolean solidity = density > 0.0;

View File

@@ -1,46 +0,0 @@
package com.hypixel.hytale.builtin.hytalegenerator.patterns;
import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize;
import com.hypixel.hytale.math.vector.Vector3i;
import javax.annotation.Nonnull;
public class CeilingPattern extends Pattern {
@Nonnull
private final Pattern ceilingPattern;
@Nonnull
private final Pattern airPattern;
@Nonnull
private final SpaceSize readSpaceSize;
@Nonnull
private final Vector3i rCeilingPosition;
@Nonnull
private final Pattern.Context rCeilingContext;
public CeilingPattern(@Nonnull Pattern ceilingPattern, @Nonnull Pattern airPattern) {
this.ceilingPattern = ceilingPattern;
this.airPattern = airPattern;
SpaceSize ceilingSpace = ceilingPattern.readSpace();
ceilingSpace.moveBy(new Vector3i(0, 1, 0));
this.readSpaceSize = SpaceSize.merge(ceilingSpace, airPattern.readSpace());
this.rCeilingPosition = new Vector3i();
this.rCeilingContext = new Pattern.Context();
}
@Override
public boolean matches(@Nonnull Pattern.Context context) {
this.rCeilingPosition.assign(context.position);
if (context.materialSpace.isInsideSpace(context.position) && context.materialSpace.isInsideSpace(this.rCeilingPosition)) {
this.rCeilingContext.assign(context);
this.rCeilingContext.position = this.rCeilingPosition;
return this.airPattern.matches(context) && this.ceilingPattern.matches(this.rCeilingContext);
} else {
return false;
}
}
@Nonnull
@Override
public SpaceSize readSpace() {
return this.readSpaceSize.clone();
}
}

View File

@@ -1,6 +1,7 @@
package com.hypixel.hytale.builtin.hytalegenerator.patterns; package com.hypixel.hytale.builtin.hytalegenerator.patterns;
import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize; import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize;
import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.NullSpace;
import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace; import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace;
import com.hypixel.hytale.builtin.hytalegenerator.material.Material; import com.hypixel.hytale.builtin.hytalegenerator.material.Material;
import com.hypixel.hytale.math.vector.Vector3i; import com.hypixel.hytale.math.vector.Vector3i;
@@ -49,12 +50,12 @@ public abstract class Pattern {
public static class Context { public static class Context {
@Nonnull @Nonnull
public Vector3i position; public Vector3i position;
@Nullable @Nonnull
public VoxelSpace<Material> materialSpace; public VoxelSpace<Material> materialSpace;
public Context() { public Context() {
this.position = new Vector3i(); this.position = new Vector3i();
this.materialSpace = null; this.materialSpace = NullSpace.instance();
} }
public Context(@Nonnull Vector3i position, @Nullable VoxelSpace<Material> materialSpace) { public Context(@Nonnull Vector3i position, @Nullable VoxelSpace<Material> materialSpace) {

View File

@@ -38,6 +38,7 @@ public class FieldFunctionPositionProvider extends PositionProvider {
for (FieldFunctionPositionProvider.Delimiter d : this.delimiters) { for (FieldFunctionPositionProvider.Delimiter d : this.delimiters) {
if (d.isInside(value)) { if (d.isInside(value)) {
context.consumer.accept(p); context.consumer.accept(p);
return;
} }
} }
}; };

View File

@@ -59,7 +59,9 @@ public class QueueProp extends Prop {
public void place(@Nonnull Prop.Context context) { public void place(@Nonnull Prop.Context context) {
QueueProp.QueueScanResult conditionalScanResult = QueueProp.QueueScanResult.cast(context.scanResult); QueueProp.QueueScanResult conditionalScanResult = QueueProp.QueueScanResult.cast(context.scanResult);
if (!conditionalScanResult.isNegative()) { if (!conditionalScanResult.isNegative()) {
conditionalScanResult.prop.place(context); Prop.Context childContext = new Prop.Context(context);
childContext.scanResult = conditionalScanResult.propScanResult;
conditionalScanResult.prop.place(childContext);
} }
} }

View File

@@ -293,7 +293,9 @@ public class PrefabProp extends Prop {
Material worldMaterial = materialSpace.getContent(worldX, worldY, worldZ); Material worldMaterial = materialSpace.getContent(worldX, worldY, worldZ);
int worldMaterialHash = worldMaterial.hashMaterialIds(); int worldMaterialHash = worldMaterial.hashMaterialIds();
if (this.materialMask.canReplace(materialHash, worldMaterialHash)) { if (this.materialMask.canReplace(materialHash, worldMaterialHash)) {
materialSpace.set(material, worldX, worldY, worldZ); if (filler == 0) {
materialSpace.set(material, worldX, worldY, worldZ);
}
} }
} }
} }

View File

@@ -179,7 +179,10 @@ public class InstancesPlugin extends JavaPlugin {
SneakyThrow.sneakyFunction( SneakyThrow.sneakyFunction(
config -> { config -> {
config.setUuid(uuid); config.setUuid(uuid);
config.setDisplayName(WorldConfig.formatDisplayName(name)); if (config.getDisplayName() == null) {
config.setDisplayName(WorldConfig.formatDisplayName(name));
}
InstanceWorldConfig instanceConfig = InstanceWorldConfig.ensureAndGet(config); InstanceWorldConfig instanceConfig = InstanceWorldConfig.ensureAndGet(config);
instanceConfig.setReturnPoint( instanceConfig.setReturnPoint(
new WorldReturnPoint(forWorld.getWorldConfig().getUuid(), returnPoint, instanceConfig.shouldPreventReconnection()) new WorldReturnPoint(forWorld.getWorldConfig().getUuid(), returnPoint, instanceConfig.shouldPreventReconnection())
@@ -576,7 +579,7 @@ public class InstancesPlugin extends JavaPlugin {
Path instancePath = getInstanceAssetPath(name); Path instancePath = getInstanceAssetPath(name);
Universe universe = Universe.get(); Universe universe = Universe.get();
WorldConfig config = WorldConfig.load(instancePath.resolve("instance.bson")).join(); WorldConfig config = WorldConfig.load(instancePath.resolve("instance.bson")).join();
IChunkStorageProvider storage = config.getChunkStorageProvider(); IChunkStorageProvider<?> storage = config.getChunkStorageProvider();
config.setChunkStorageProvider(new MigrationChunkStorageProvider(new IChunkStorageProvider[]{storage}, EmptyChunkStorageProvider.INSTANCE)); config.setChunkStorageProvider(new MigrationChunkStorageProvider(new IChunkStorageProvider[]{storage}, EmptyChunkStorageProvider.INSTANCE));
config.setResourceStorageProvider(EmptyResourceStorageProvider.INSTANCE); config.setResourceStorageProvider(EmptyResourceStorageProvider.INSTANCE);
config.setUuid(UUID.randomUUID()); config.setUuid(UUID.randomUUID());

View File

@@ -26,7 +26,6 @@ import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.protocol.AnimationSlot; import com.hypixel.hytale.protocol.AnimationSlot;
import com.hypixel.hytale.protocol.BlockMount; import com.hypixel.hytale.protocol.BlockMount;
import com.hypixel.hytale.protocol.ComponentUpdate;
import com.hypixel.hytale.protocol.ComponentUpdateType; import com.hypixel.hytale.protocol.ComponentUpdateType;
import com.hypixel.hytale.protocol.GameMode; import com.hypixel.hytale.protocol.GameMode;
import com.hypixel.hytale.protocol.MountController; import com.hypixel.hytale.protocol.MountController;
@@ -855,8 +854,6 @@ public class MountSystems {
private static void queueUpdatesFor( private static void queueUpdatesFor(
@Nonnull Ref<EntityStore> ref, @Nonnull Map<Ref<EntityStore>, EntityTrackerSystems.EntityViewer> visibleTo, @Nonnull MountedComponent component @Nonnull Ref<EntityStore> ref, @Nonnull Map<Ref<EntityStore>, EntityTrackerSystems.EntityViewer> visibleTo, @Nonnull MountedComponent component
) { ) {
ComponentUpdate update = new ComponentUpdate();
update.type = ComponentUpdateType.Mounted;
Ref<EntityStore> mountedToEntity = component.getMountedToEntity(); Ref<EntityStore> mountedToEntity = component.getMountedToEntity();
Ref<ChunkStore> mountedToBlock = component.getMountedToBlock(); Ref<ChunkStore> mountedToBlock = component.getMountedToBlock();
Vector3f offset = component.getAttachmentOffset(); Vector3f offset = component.getAttachmentOffset();
@@ -897,10 +894,8 @@ public class MountSystems {
mountedUpdate = new MountedUpdate(0, netOffset, component.getControllerType(), blockMount); mountedUpdate = new MountedUpdate(0, netOffset, component.getControllerType(), blockMount);
} }
update.mounted = mountedUpdate;
for (EntityTrackerSystems.EntityViewer viewer : visibleTo.values()) { for (EntityTrackerSystems.EntityViewer viewer : visibleTo.values()) {
viewer.queueUpdate(ref, update); viewer.queueUpdate(ref, mountedUpdate);
} }
} }
} }

View File

@@ -10,6 +10,7 @@ import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.RefSystem; import com.hypixel.hytale.component.system.RefSystem;
import com.hypixel.hytale.protocol.packets.interaction.MountNPC; import com.hypixel.hytale.protocol.packets.interaction.MountNPC;
import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.modules.entity.component.Interactable;
import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent; import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent;
import com.hypixel.hytale.server.core.modules.entity.damage.DeathSystems; import com.hypixel.hytale.server.core.modules.entity.damage.DeathSystems;
import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId; import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId;
@@ -129,6 +130,7 @@ public class NPCMountSystems {
if (playerComponent != null) { if (playerComponent != null) {
playerComponent.setMountEntityId(networkId); playerComponent.setMountEntityId(networkId);
playerRef.getPacketHandler().write(packet); playerRef.getPacketHandler().write(packet);
commandBuffer.removeComponent(ref, Interactable.getComponentType());
} }
} }
} }
@@ -145,6 +147,7 @@ public class NPCMountSystems {
RoleChangeSystem.requestRoleChange(ref, npcComponent.getRole(), mountComponent.getOriginalRoleIndex(), false, "Idle", null, store); RoleChangeSystem.requestRoleChange(ref, npcComponent.getRole(), mountComponent.getOriginalRoleIndex(), false, "Idle", null, store);
commandBuffer.removeComponent(ref, this.mountComponentType); commandBuffer.removeComponent(ref, this.mountComponentType);
commandBuffer.ensureComponent(ref, Interactable.getComponentType());
} }
@Override @Override

View File

@@ -30,7 +30,6 @@ import com.hypixel.hytale.server.core.asset.type.item.config.Item;
import com.hypixel.hytale.server.core.asset.type.item.config.PortalKey; import com.hypixel.hytale.server.core.asset.type.item.config.PortalKey;
import com.hypixel.hytale.server.core.asset.type.portalworld.PillTag; import com.hypixel.hytale.server.core.asset.type.portalworld.PillTag;
import com.hypixel.hytale.server.core.asset.type.portalworld.PortalDescription; import com.hypixel.hytale.server.core.asset.type.portalworld.PortalDescription;
import com.hypixel.hytale.server.core.asset.type.portalworld.PortalSpawn;
import com.hypixel.hytale.server.core.asset.type.portalworld.PortalType; import com.hypixel.hytale.server.core.asset.type.portalworld.PortalType;
import com.hypixel.hytale.server.core.asset.util.ColorParseUtil; import com.hypixel.hytale.server.core.asset.util.ColorParseUtil;
import com.hypixel.hytale.server.core.entity.UUIDComponent; import com.hypixel.hytale.server.core.entity.UUIDComponent;
@@ -52,6 +51,8 @@ import com.hypixel.hytale.server.core.universe.world.spawn.ISpawnProvider;
import com.hypixel.hytale.server.core.universe.world.spawn.IndividualSpawnProvider; import com.hypixel.hytale.server.core.universe.world.spawn.IndividualSpawnProvider;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@@ -280,8 +281,7 @@ public class PortalDeviceSummonPage extends InteractiveCustomUIPage<PortalDevice
private static CompletableFuture<World> spawnReturnPortal( private static CompletableFuture<World> spawnReturnPortal(
@Nonnull World world, @Nonnull PortalWorld portalWorld, @Nonnull UUID sampleUuid, @Nonnull String portalBlockType @Nonnull World world, @Nonnull PortalWorld portalWorld, @Nonnull UUID sampleUuid, @Nonnull String portalBlockType
) { ) {
PortalSpawn portalSpawn = portalWorld.getPortalType().getPortalSpawn(); return getSpawnTransform(world, sampleUuid)
return getSpawnTransform(world, sampleUuid, portalSpawn)
.thenCompose( .thenCompose(
spawnTransform -> { spawnTransform -> {
Vector3d spawnPoint = spawnTransform.getPosition(); Vector3d spawnPoint = spawnTransform.getPosition();
@@ -312,21 +312,24 @@ public class PortalDeviceSummonPage extends InteractiveCustomUIPage<PortalDevice
} }
@Nonnull @Nonnull
private static CompletableFuture<Transform> getSpawnTransform(@Nonnull World world, @Nonnull UUID sampleUuid, @Nullable PortalSpawn portalSpawn) { private static CompletableFuture<Transform> getSpawnTransform(@Nonnull World world, @Nonnull UUID sampleUuid) {
return CompletableFuture.supplyAsync(() -> {
List<Vector3d> hintedSpawns = fetchHintedSpawns(world, sampleUuid);
return PortalSpawnFinder.computeSpawnTransform(world, hintedSpawns);
}, world);
}
private static List<Vector3d> fetchHintedSpawns(World world, UUID sampleUuid) {
ISpawnProvider spawnProvider = world.getWorldConfig().getSpawnProvider(); ISpawnProvider spawnProvider = world.getWorldConfig().getSpawnProvider();
if (spawnProvider == null) { if (spawnProvider == null) {
return CompletableFuture.completedFuture(null); return Collections.emptyList();
} else { } else {
Transform worldSpawnPoint = spawnProvider.getSpawnPoint(world, sampleUuid); Transform[] spawnTransforms = spawnProvider.getSpawnPoints();
if (portalSpawn == null) { if (spawnTransforms != null && spawnTransforms.length > 0) {
Transform uppedSpawnPoint = worldSpawnPoint.clone(); return Arrays.stream(spawnTransforms).map(Transform::getPosition).toList();
uppedSpawnPoint.getPosition().add(0.0, 0.5, 0.0);
return CompletableFuture.completedFuture(uppedSpawnPoint);
} else { } else {
return CompletableFuture.supplyAsync(() -> { Transform spawnPoint = spawnProvider.getSpawnPoint(world, sampleUuid);
Transform computedSpawn = PortalSpawnFinder.computeSpawnTransform(world, portalSpawn); return spawnPoint != null ? Collections.singletonList(spawnPoint.getPosition()) : Collections.emptyList();
return computedSpawn == null ? worldSpawnPoint : computedSpawn;
}, world);
} }
} }
} }

View File

@@ -1,6 +1,5 @@
package com.hypixel.hytale.builtin.portals.ui; package com.hypixel.hytale.builtin.portals.ui;
import com.hypixel.hytale.builtin.portals.utils.posqueries.generators.SearchCircular;
import com.hypixel.hytale.builtin.portals.utils.posqueries.predicates.FitsAPortal; import com.hypixel.hytale.builtin.portals.utils.posqueries.predicates.FitsAPortal;
import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Ref; import com.hypixel.hytale.component.Ref;
@@ -10,10 +9,7 @@ import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.math.vector.Transform; import com.hypixel.hytale.math.vector.Transform;
import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.vector.Vector3f; import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.protocol.BlockMaterial;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.asset.type.portalworld.PortalSpawn;
import com.hypixel.hytale.server.core.modules.collision.WorldUtil; import com.hypixel.hytale.server.core.modules.collision.WorldUtil;
import com.hypixel.hytale.server.core.universe.world.World; 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.BlockChunk;
@@ -21,22 +17,27 @@ import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk; import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection; import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore; import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import java.util.logging.Level;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public final class PortalSpawnFinder { public final class PortalSpawnFinder {
private static final int MAX_ATTEMPTS_PER_WORLD = 10;
private static final int QUALITY_ATTEMPTS = 2;
private static final int CHECKS_PER_CHUNK = 8;
private static final Vector3d FALLBACK_POSITION = Vector3d.ZERO;
@Nullable @Nullable
public static Transform computeSpawnTransform(@Nonnull World world, @Nonnull PortalSpawn config) { public static Transform computeSpawnTransform(@Nonnull World world, @Nonnull List<Vector3d> hintedSpawns) {
Vector3d spawn = findSpawnByThrowingDarts(world, config); Vector3d spawn = guesstimateFromHints(world, hintedSpawns);
if (spawn == null) { if (spawn == null) {
spawn = findFallbackPositionOnGround(world, config); spawn = findFallbackPositionOnGround(world);
HytaleLogger.getLogger().at(Level.INFO).log("Had to use fallback spawn for portal spawn"); HytaleLogger.getLogger().atWarning().log("Had to use fallback spawn for portal spawn (10 attempts)");
} }
if (spawn == null) { if (spawn == null) {
HytaleLogger.getLogger().at(Level.INFO).log("Both dart and fallback spawn finder failed for portal spawn"); HytaleLogger.getLogger().atWarning().log("Both dart and fallback spawn finder failed for portal spawn");
return null; return null;
} else { } else {
Vector3f direction = Vector3f.lookAt(spawn).scale(-1.0F); Vector3f direction = Vector3f.lookAt(spawn).scale(-1.0F);
@@ -47,27 +48,17 @@ public final class PortalSpawnFinder {
} }
@Nullable @Nullable
private static Vector3d findSpawnByThrowingDarts(@Nonnull World world, @Nonnull PortalSpawn config) { private static Vector3d guesstimateFromHints(World world, List<Vector3d> hintedSpawns) {
Vector3d center = config.getCenter().toVector3d(); for (int i = 0; i < Math.min(hintedSpawns.size(), 10); i++) {
center.setY(config.getCheckSpawnY()); Vector3d hintedSpawn = hintedSpawns.get(i);
int halfwayThrows = config.getChunkDartThrows() / 2; WorldChunk chunk = world.getChunk(ChunkUtil.indexChunkFromBlock(hintedSpawn.x, hintedSpawn.z));
if (chunk != null) {
for (int chunkDart = 0; chunkDart < config.getChunkDartThrows(); chunkDart++) { boolean quality = i < 2;
Vector3d pointd = new SearchCircular(config.getMinRadius(), config.getMaxRadius(), 1).execute(world, center).orElse(null); int scanHeight = quality ? (int)hintedSpawn.y : 319;
if (pointd != null) { Vector3d spawn = findGroundWithinChunk(chunk, scanHeight, quality);
Vector3i point = pointd.toVector3i(); if (spawn != null) {
WorldChunk chunk = world.getChunk(ChunkUtil.indexChunkFromBlock(point.x, point.z)); HytaleLogger.getLogger().atInfo().log("Found portal spawn " + spawn + " on attempt #" + (i + 1) + " quality=" + quality);
BlockType firstBlock = chunk.getBlockType(point.x, point.y, point.z); return spawn;
if (firstBlock != null) {
BlockMaterial firstBlockMat = firstBlock.getMaterial();
if (firstBlockMat != BlockMaterial.Solid) {
boolean checkIfPortalFitsNice = chunkDart < halfwayThrows;
Vector3d spawn = findGroundWithinChunk(chunk, config, checkIfPortalFitsNice);
if (spawn != null) {
HytaleLogger.getLogger().at(Level.INFO).log("Found fragment spawn at " + spawn + " after " + (chunkDart + 1) + " chunk scan(s)");
return spawn;
}
}
} }
} }
} }
@@ -76,15 +67,15 @@ public final class PortalSpawnFinder {
} }
@Nullable @Nullable
private static Vector3d findGroundWithinChunk(@Nonnull WorldChunk chunk, @Nonnull PortalSpawn config, boolean checkIfPortalFitsNice) { private static Vector3d findGroundWithinChunk(@Nonnull WorldChunk chunk, int scanHeight, boolean checkIfPortalFitsNice) {
int chunkBlockX = ChunkUtil.minBlock(chunk.getX()); int chunkBlockX = ChunkUtil.minBlock(chunk.getX());
int chunkBlockZ = ChunkUtil.minBlock(chunk.getZ()); int chunkBlockZ = ChunkUtil.minBlock(chunk.getZ());
ThreadLocalRandom random = ThreadLocalRandom.current(); ThreadLocalRandom random = ThreadLocalRandom.current();
for (int i = 0; i < config.getChecksPerChunk(); i++) { for (int i = 0; i < 8; i++) {
int x = chunkBlockX + random.nextInt(2, 14); int x = chunkBlockX + random.nextInt(2, 14);
int z = chunkBlockZ + random.nextInt(2, 14); int z = chunkBlockZ + random.nextInt(2, 14);
Vector3d point = findWithGroundBelow(chunk, x, config.getCheckSpawnY(), z, config.getScanHeight(), false); Vector3d point = findWithGroundBelow(chunk, x, scanHeight, z, scanHeight, false);
if (point != null && (!checkIfPortalFitsNice || FitsAPortal.check(chunk.getWorld(), point))) { if (point != null && (!checkIfPortalFitsNice || FitsAPortal.check(chunk.getWorld(), point))) {
return point; return point;
} }
@@ -160,8 +151,8 @@ public final class PortalSpawnFinder {
} }
@Nullable @Nullable
private static Vector3d findFallbackPositionOnGround(@Nonnull World world, @Nonnull PortalSpawn config) { private static Vector3d findFallbackPositionOnGround(@Nonnull World world) {
Vector3i center = config.getCenter(); Vector3d center = FALLBACK_POSITION.clone();
long chunkIndex = ChunkUtil.indexChunkFromBlock(center.x, center.z); long chunkIndex = ChunkUtil.indexChunkFromBlock(center.x, center.z);
WorldChunk centerChunk = world.getChunk(chunkIndex); WorldChunk centerChunk = world.getChunk(chunkIndex);
return centerChunk == null ? null : findWithGroundBelow(centerChunk, 0, 319, 0, 319, true); return centerChunk == null ? null : findWithGroundBelow(centerChunk, 0, 319, 0, 319, true);

View File

@@ -0,0 +1,35 @@
package com.hypixel.hytale.builtin.randomtick;
import com.hypixel.hytale.component.Resource;
import com.hypixel.hytale.component.ResourceType;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import java.util.Random;
import javax.annotation.Nullable;
public class RandomTick implements Resource<ChunkStore> {
private int blocksPerSectionPerTickStable = 1;
private int blocksPerSectionPerTickUnstable = 3;
private Random random = new Random();
public static ResourceType<ChunkStore, RandomTick> getResourceType() {
return RandomTickPlugin.get().getRandomTickResourceType();
}
public int getBlocksPerSectionPerTickStable() {
return this.blocksPerSectionPerTickStable;
}
public int getBlocksPerSectionPerTickUnstable() {
return this.blocksPerSectionPerTickUnstable;
}
public Random getRandom() {
return this.random;
}
@Nullable
@Override
public Resource<ChunkStore> clone() {
return new RandomTick();
}
}

View File

@@ -0,0 +1,36 @@
package com.hypixel.hytale.builtin.randomtick;
import com.hypixel.hytale.builtin.randomtick.procedures.ChangeIntoBlockProcedure;
import com.hypixel.hytale.builtin.randomtick.procedures.SpreadToProcedure;
import com.hypixel.hytale.component.ResourceType;
import com.hypixel.hytale.server.core.asset.type.blocktick.config.RandomTickProcedure;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import javax.annotation.Nonnull;
public class RandomTickPlugin extends JavaPlugin {
private static RandomTickPlugin INSTANCE;
private ResourceType<ChunkStore, RandomTick> randomTickResourceType;
public static RandomTickPlugin get() {
return INSTANCE;
}
public RandomTickPlugin(@Nonnull JavaPluginInit init) {
super(init);
INSTANCE = this;
}
@Override
protected void setup() {
this.randomTickResourceType = this.getChunkStoreRegistry().registerResource(RandomTick.class, RandomTick::new);
this.getChunkStoreRegistry().registerSystem(new RandomTickSystem());
RandomTickProcedure.CODEC.register("ChangeIntoBlock", ChangeIntoBlockProcedure.class, ChangeIntoBlockProcedure.CODEC);
RandomTickProcedure.CODEC.register("SpreadTo", SpreadToProcedure.class, SpreadToProcedure.CODEC);
}
public ResourceType<ChunkStore, RandomTick> getRandomTickResourceType() {
return this.randomTickResourceType;
}
}

View File

@@ -0,0 +1,114 @@
package com.hypixel.hytale.builtin.randomtick;
import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.math.util.HashUtil;
import com.hypixel.hytale.server.core.asset.type.blocktick.config.RandomTickProcedure;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import java.util.Random;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class RandomTickSystem extends EntityTickingSystem<ChunkStore> {
private final ComponentType<ChunkStore, BlockSection> blockSelectionComponentType = BlockSection.getComponentType();
private final ComponentType<ChunkStore, ChunkSection> chunkSectionComponentType = ChunkSection.getComponentType();
private final Query<ChunkStore> query = Query.and(this.blockSelectionComponentType, this.chunkSectionComponentType);
@Override
public void tick(
float dt,
int index,
@Nonnull ArchetypeChunk<ChunkStore> archetypeChunk,
@Nonnull Store<ChunkStore> store,
@Nonnull CommandBuffer<ChunkStore> commandBuffer
) {
BlockSection blockSection = archetypeChunk.getComponent(index, this.blockSelectionComponentType);
assert blockSection != null;
if (!blockSection.isSolidAir()) {
ChunkSection chunkSection = archetypeChunk.getComponent(index, this.chunkSectionComponentType);
assert chunkSection != null;
RandomTick config = commandBuffer.getResource(RandomTick.getResourceType());
World world = store.getExternalData().getWorld();
int interval = 32768 / config.getBlocksPerSectionPerTickStable();
long baseSeed = HashUtil.hash(world.getTick() / interval, chunkSection.getX(), chunkSection.getY(), chunkSection.getZ());
long randomSeed = (baseSeed << 1 | 1L) & 32767L;
long randomSeed2 = baseSeed >> 16 & 32767L;
long startIndex = world.getTick() % interval * config.getBlocksPerSectionPerTickStable();
BlockTypeAssetMap<String, BlockType> assetMap = BlockType.getAssetMap();
for (int i = 0; i < config.getBlocksPerSectionPerTickStable(); i++) {
int blockIndex = (int)((startIndex + i) * randomSeed + randomSeed2 & 32767L);
int localX = ChunkUtil.xFromIndex(blockIndex);
int localY = ChunkUtil.yFromIndex(blockIndex);
int localZ = ChunkUtil.zFromIndex(blockIndex);
int blockId = blockSection.get(blockIndex);
if (blockId != 0) {
BlockType blockType = assetMap.getAsset(blockId);
if (blockType != null) {
RandomTickProcedure randomTickProcedure = blockType.getRandomTickProcedure();
if (randomTickProcedure != null) {
randomTickProcedure.onRandomTick(
store,
commandBuffer,
blockSection,
ChunkUtil.worldCoordFromLocalCoord(chunkSection.getX(), localX),
ChunkUtil.worldCoordFromLocalCoord(chunkSection.getY(), localY),
ChunkUtil.worldCoordFromLocalCoord(chunkSection.getZ(), localZ),
blockId,
blockType
);
}
}
}
}
Random rng = config.getRandom();
for (int ix = 0; ix < config.getBlocksPerSectionPerTickUnstable(); ix++) {
int blockIndex = rng.nextInt(32768);
int localX = ChunkUtil.xFromIndex(blockIndex);
int localY = ChunkUtil.yFromIndex(blockIndex);
int localZ = ChunkUtil.zFromIndex(blockIndex);
int blockId = blockSection.get(blockIndex);
if (blockId != 0) {
BlockType blockType = assetMap.getAsset(blockId);
if (blockType != null) {
RandomTickProcedure randomTickProcedure = blockType.getRandomTickProcedure();
if (randomTickProcedure != null) {
randomTickProcedure.onRandomTick(
store,
commandBuffer,
blockSection,
ChunkUtil.worldCoordFromLocalCoord(chunkSection.getX(), localX),
ChunkUtil.worldCoordFromLocalCoord(chunkSection.getY(), localY),
ChunkUtil.worldCoordFromLocalCoord(chunkSection.getZ(), localZ),
blockId,
blockType
);
}
}
}
}
}
}
@Nullable
@Override
public Query<ChunkStore> getQuery() {
return this.query;
}
}

View File

@@ -0,0 +1,38 @@
package com.hypixel.hytale.builtin.randomtick.procedures;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.server.core.asset.type.blocktick.config.RandomTickProcedure;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
public class ChangeIntoBlockProcedure implements RandomTickProcedure {
public static final BuilderCodec<ChangeIntoBlockProcedure> CODEC = BuilderCodec.builder(ChangeIntoBlockProcedure.class, ChangeIntoBlockProcedure::new)
.appendInherited(new KeyedCodec<>("TargetBlock", Codec.STRING), (o, i) -> o.targetBlock = i, o -> o.targetBlock, (o, p) -> o.targetBlock = p.targetBlock)
.addValidatorLate(() -> BlockType.VALIDATOR_CACHE.getValidator().late())
.add()
.build();
private String targetBlock;
@Override
public void onRandomTick(
Store<ChunkStore> store,
CommandBuffer<ChunkStore> commandBuffer,
BlockSection blockSection,
int worldX,
int worldY,
int worldZ,
int blockId,
BlockType blockType
) {
int targetBlockId = BlockType.getAssetMap().getIndex(this.targetBlock);
if (targetBlockId != Integer.MIN_VALUE) {
blockSection.set(ChunkUtil.indexBlock(worldX, worldY, worldZ), targetBlockId, 0, 0);
}
}
}

View File

@@ -0,0 +1,197 @@
package com.hypixel.hytale.builtin.randomtick.procedures;
import com.hypixel.hytale.assetstore.AssetRegistry;
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.array.ArrayCodec;
import com.hypixel.hytale.codec.validation.Validators;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.protocol.BlockMaterial;
import com.hypixel.hytale.protocol.DrawType;
import com.hypixel.hytale.server.core.asset.type.blocktick.config.RandomTickProcedure;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import it.unimi.dsi.fastutil.ints.IntSet;
public class SpreadToProcedure implements RandomTickProcedure {
public static final BuilderCodec<SpreadToProcedure> CODEC = BuilderCodec.builder(SpreadToProcedure.class, SpreadToProcedure::new)
.appendInherited(
new KeyedCodec<>("SpreadDirections", new ArrayCodec<>(Vector3i.CODEC, Vector3i[]::new)),
(o, i) -> o.spreadDirections = i,
o -> o.spreadDirections,
(o, p) -> o.spreadDirections = p.spreadDirections
)
.documentation("The directions this block can spread in.")
.addValidator(Validators.nonNull())
.add()
.<Integer>appendInherited(new KeyedCodec<>("MinY", Codec.INTEGER), (o, i) -> o.minY = i, o -> o.minY, (o, p) -> o.minY = p.minY)
.documentation(
"The minimum Y level this block can spread to, relative to the current block. For example, a value of -1 means the block can spread to blocks one level below it."
)
.add()
.<Integer>appendInherited(new KeyedCodec<>("MaxY", Codec.INTEGER), (o, i) -> o.maxY = i, o -> o.maxY, (o, p) -> o.maxY = p.maxY)
.documentation(
"The maximum Y level this block can spread to, relative to the current block. For example, a value of 1 means the block can spread to blocks one level above it."
)
.add()
.<String>appendInherited(new KeyedCodec<>("AllowedTag", Codec.STRING), (o, i) -> {
o.allowedTag = i;
o.allowedTagIndex = AssetRegistry.getOrCreateTagIndex(i);
}, o -> o.allowedTag, (o, p) -> {
o.allowedTag = p.allowedTag;
o.allowedTagIndex = p.allowedTagIndex;
})
.documentation("The asset tag that the block can spread to.")
.addValidator(Validators.nonNull())
.add()
.<Boolean>appendInherited(
new KeyedCodec<>("RequireEmptyAboveTarget", Codec.BOOLEAN),
(o, i) -> o.requireEmptyAboveTarget = i,
o -> o.requireEmptyAboveTarget,
(o, p) -> o.requireEmptyAboveTarget = p.requireEmptyAboveTarget
)
.documentation("Whether the block requires an empty block above the target block to spread.")
.add()
.<Integer>appendInherited(
new KeyedCodec<>("RequiredLightLevel", Codec.INTEGER),
(o, i) -> o.requiredLightLevel = i,
o -> o.requiredLightLevel,
(o, p) -> o.requiredLightLevel = p.requiredLightLevel
)
.documentation("The minimum light level required for the block to spread.")
.addValidator(Validators.range(0, 15))
.add()
.<String>appendInherited(
new KeyedCodec<>("RevertBlock", Codec.STRING), (o, i) -> o.revertBlock = i, o -> o.revertBlock, (o, p) -> o.revertBlock = p.revertBlock
)
.documentation("If specified, the block will revert to this block if it is covered by another block.")
.addValidatorLate(() -> BlockType.VALIDATOR_CACHE.getValidator().late())
.add()
.build();
private Vector3i[] spreadDirections;
private int minY = 0;
private int maxY = 0;
private String allowedTag;
private int allowedTagIndex = Integer.MIN_VALUE;
private boolean requireEmptyAboveTarget = true;
private int requiredLightLevel = 6;
private String revertBlock;
@Override
public void onRandomTick(
Store<ChunkStore> store,
CommandBuffer<ChunkStore> commandBuffer,
BlockSection blockSection,
int worldX,
int worldY,
int worldZ,
int blockId,
BlockType blockType
) {
IntSet validSpreadTargets = BlockType.getAssetMap().getIndexesForTag(this.allowedTagIndex);
WorldTimeResource worldTimeResource = commandBuffer.getExternalData()
.getWorld()
.getEntityStore()
.getStore()
.getResource(WorldTimeResource.getResourceType());
double sunlightFactor = worldTimeResource.getSunlightFactor();
BlockSection aboveSection = blockSection;
if (!ChunkUtil.isSameChunkSection(worldX, worldY, worldZ, worldX, worldY + 1, worldZ)) {
Ref<ChunkStore> aboveChunk = store.getExternalData()
.getChunkSectionReference(
commandBuffer, ChunkUtil.chunkCoordinate(worldX), ChunkUtil.chunkCoordinate(worldY + 1), ChunkUtil.chunkCoordinate(worldZ)
);
if (aboveChunk == null) {
return;
}
BlockSection aboveBlockSection = commandBuffer.getComponent(aboveChunk, BlockSection.getComponentType());
if (aboveBlockSection == null) {
return;
}
aboveSection = aboveBlockSection;
}
int aboveIndex = ChunkUtil.indexBlock(worldX, worldY + 1, worldZ);
if (this.revertBlock != null) {
int blockAtAboveId = aboveSection.get(aboveIndex);
BlockType blockAtAbove = BlockType.getAssetMap().getAsset(blockAtAboveId);
if (blockAtAbove != null && (blockAtAbove.getDrawType() == DrawType.Cube || blockAtAbove.getDrawType() == DrawType.CubeWithModel)) {
int revert = BlockType.getAssetMap().getIndex(this.revertBlock);
if (revert != Integer.MIN_VALUE) {
blockSection.set(worldX, worldY, worldZ, revert, 0, 0);
return;
}
}
}
int skyLight = (int)(aboveSection.getLocalLight().getSkyLight(aboveIndex) * sunlightFactor);
int blockLevel = aboveSection.getLocalLight().getBlockLightIntensity(aboveIndex);
int lightLevel = Math.max(skyLight, blockLevel);
if (lightLevel >= this.requiredLightLevel) {
for (int y = this.minY; y <= this.maxY; y++) {
for (Vector3i direction : this.spreadDirections) {
int targetX = worldX + direction.x;
int targetY = worldY + direction.y + y;
int targetZ = worldZ + direction.z;
BlockSection targetBlockSection = blockSection;
if (!ChunkUtil.isSameChunkSection(worldX, worldY, worldZ, targetX, targetY, targetZ)) {
Ref<ChunkStore> otherChunk = store.getExternalData()
.getChunkSectionReference(
commandBuffer, ChunkUtil.chunkCoordinate(targetX), ChunkUtil.chunkCoordinate(targetY), ChunkUtil.chunkCoordinate(targetZ)
);
if (otherChunk == null) {
continue;
}
targetBlockSection = commandBuffer.getComponent(otherChunk, BlockSection.getComponentType());
if (targetBlockSection == null) {
continue;
}
}
int targetIndex = ChunkUtil.indexBlock(targetX, targetY, targetZ);
int targetBlockId = targetBlockSection.get(targetIndex);
if (validSpreadTargets.contains(targetBlockId)) {
if (this.requireEmptyAboveTarget) {
int aboveTargetBlockId;
if (ChunkUtil.isSameChunkSection(targetX, targetY, targetZ, targetX, targetY + 1, targetZ)) {
aboveTargetBlockId = targetBlockSection.get(ChunkUtil.indexBlock(targetX, targetY + 1, targetZ));
} else {
Ref<ChunkStore> aboveChunkx = store.getExternalData()
.getChunkSectionReference(
commandBuffer, ChunkUtil.chunkCoordinate(targetX), ChunkUtil.chunkCoordinate(targetY + 1), ChunkUtil.chunkCoordinate(targetZ)
);
if (aboveChunkx == null) {
continue;
}
BlockSection aboveBlockSection = commandBuffer.getComponent(aboveChunkx, BlockSection.getComponentType());
if (aboveBlockSection == null) {
continue;
}
aboveTargetBlockId = aboveBlockSection.get(ChunkUtil.indexBlock(targetX, targetY + 1, targetZ));
}
BlockType aboveBlockType = BlockType.getAssetMap().getAsset(aboveTargetBlockId);
if (aboveBlockType == null || aboveBlockType.getMaterial() != BlockMaterial.Empty) {
continue;
}
}
targetBlockSection.set(targetIndex, blockId, 0, 0);
}
}
}
}
}
}

View File

@@ -51,57 +51,69 @@ public class TeleportOtherToPlayerCommand extends CommandBase {
World targetWorld = targetStore.getExternalData().getWorld(); World targetWorld = targetStore.getExternalData().getWorld();
sourceWorld.execute( sourceWorld.execute(
() -> { () -> {
TransformComponent transformComponent = sourceStore.getComponent(sourceRef, TransformComponent.getComponentType()); if (!sourceRef.isValid()) {
context.sendMessage(MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_IN_WORLD);
} else if (!targetRef.isValid()) {
context.sendMessage(MESSAGE_COMMANDS_ERRORS_TARGET_NOT_IN_WORLD);
} else {
TransformComponent transformComponent = sourceStore.getComponent(sourceRef, TransformComponent.getComponentType());
assert transformComponent != null; assert transformComponent != null;
HeadRotation headRotationComponent = sourceStore.getComponent(sourceRef, HeadRotation.getComponentType()); HeadRotation headRotationComponent = sourceStore.getComponent(sourceRef, HeadRotation.getComponentType());
assert headRotationComponent != null; assert headRotationComponent != null;
Vector3d pos = transformComponent.getPosition().clone(); Vector3d pos = transformComponent.getPosition().clone();
Vector3f rotation = headRotationComponent.getRotation().clone(); Vector3f rotation = headRotationComponent.getRotation().clone();
targetWorld.execute( targetWorld.execute(
() -> { () -> {
TransformComponent targetTransformComponent = targetStore.getComponent(targetRef, TransformComponent.getComponentType()); TransformComponent targetTransformComponent = targetStore.getComponent(targetRef, TransformComponent.getComponentType());
assert targetTransformComponent != null; assert targetTransformComponent != null;
HeadRotation targetHeadRotationComponent = targetStore.getComponent(targetRef, HeadRotation.getComponentType()); HeadRotation targetHeadRotationComponent = targetStore.getComponent(targetRef, HeadRotation.getComponentType());
assert targetHeadRotationComponent != null; assert targetHeadRotationComponent != null;
Vector3d targetPosition = targetTransformComponent.getPosition().clone(); Vector3d targetPosition = targetTransformComponent.getPosition().clone();
Vector3f targetHeadRotation = targetHeadRotationComponent.getRotation().clone(); Vector3f targetHeadRotation = targetHeadRotationComponent.getRotation().clone();
Transform targetTransform = new Transform(targetPosition, targetHeadRotation); Transform targetTransform = new Transform(targetPosition, targetHeadRotation);
sourceWorld.execute( sourceWorld.execute(
() -> { () -> {
Teleport teleportComponent = Teleport.createForPlayer(targetWorld, targetTransform); if (!sourceRef.isValid()) {
sourceStore.addComponent(sourceRef, Teleport.getComponentType(), teleportComponent); context.sendMessage(MESSAGE_COMMANDS_ERRORS_PLAYER_NOT_IN_WORLD);
PlayerRef sourcePlayerRefComponent = sourceStore.getComponent(sourceRef, PlayerRef.getComponentType()); } else if (!targetRef.isValid()) {
context.sendMessage(MESSAGE_COMMANDS_ERRORS_TARGET_NOT_IN_WORLD);
} else {
Teleport teleportComponent = Teleport.createForPlayer(targetWorld, targetTransform);
sourceStore.addComponent(sourceRef, Teleport.getComponentType(), teleportComponent);
PlayerRef sourcePlayerRefComponent = sourceStore.getComponent(sourceRef, PlayerRef.getComponentType());
assert sourcePlayerRefComponent != null; assert sourcePlayerRefComponent != null;
PlayerRef targetPlayerRefComponent = targetStore.getComponent(targetRef, PlayerRef.getComponentType()); PlayerRef targetPlayerRefComponent = targetStore.getComponent(targetRef, PlayerRef.getComponentType());
assert targetPlayerRefComponent != null; assert targetPlayerRefComponent != null;
context.sendMessage( context.sendMessage(
Message.translation("server.commands.teleport.teleportedOtherToPlayer") Message.translation("server.commands.teleport.teleportedOtherToPlayer")
.param("targetName", sourcePlayerRefComponent.getUsername()) .param("targetName", sourcePlayerRefComponent.getUsername())
.param("toName", targetPlayerRefComponent.getUsername()) .param("toName", targetPlayerRefComponent.getUsername())
); );
sourceStore.ensureAndGetComponent(sourceRef, TeleportHistory.getComponentType()) sourceStore.ensureAndGetComponent(sourceRef, TeleportHistory.getComponentType())
.append( .append(
sourceWorld, sourceWorld,
pos, pos,
rotation, rotation,
"Teleport to " + targetPlayerRefComponent.getUsername() + " by " + context.sender().getDisplayName() "Teleport to " + targetPlayerRefComponent.getUsername() + " by " + context.sender().getDisplayName()
); );
} }
); }
} );
); }
);
}
} }
); );
} else { } else {

View File

@@ -1,19 +0,0 @@
package com.hypixel.hytale.builtin.worldgen;
import com.hypixel.hytale.server.worldgen.util.LogUtil;
import java.util.logging.Level;
import javax.annotation.Nonnull;
public interface FeatureFlags {
@Deprecated(since = "2026-01-19", forRemoval = true)
boolean VERSION_OVERRIDES = of("hytale.worldgen.version_overrides");
static boolean of(@Nonnull String featureFlag) {
if (System.getProperty(featureFlag) == null && System.getenv(featureFlag) == null) {
return false;
} else {
LogUtil.getLogger().at(Level.INFO).log("Feature %s is enabled.", featureFlag);
return true;
}
}
}

View File

@@ -49,15 +49,17 @@ public class WorldGenPlugin extends JavaPlugin {
instance = this; instance = this;
this.getEntityStoreRegistry().registerSystem(new BiomeDataSystem()); this.getEntityStoreRegistry().registerSystem(new BiomeDataSystem());
IWorldGenProvider.CODEC.register(Priority.DEFAULT.before(1), "Hytale", HytaleWorldGenProvider.class, HytaleWorldGenProvider.CODEC); IWorldGenProvider.CODEC.register(Priority.DEFAULT.before(1), "Hytale", HytaleWorldGenProvider.class, HytaleWorldGenProvider.CODEC);
FileIO.setDefaultRoot(AssetModule.get().getBaseAssetPack().getRoot()); AssetModule assets = AssetModule.get();
if (FeatureFlags.VERSION_OVERRIDES) { if (assets.getAssetPacks().isEmpty()) {
AssetModule assets = AssetModule.get(); this.getLogger().at(Level.SEVERE).log("No asset packs loaded");
} else {
FileIO.setDefaultRoot(assets.getBaseAssetPack().getRoot());
List<WorldGenPlugin.Version> packs = loadVersionPacks(assets); List<WorldGenPlugin.Version> packs = loadVersionPacks(assets);
Object2ObjectOpenHashMap<String, Semver> versions = new Object2ObjectOpenHashMap<>(); Object2ObjectOpenHashMap<String, Semver> versions = new Object2ObjectOpenHashMap<>();
for (WorldGenPlugin.Version version : packs) { for (WorldGenPlugin.Version version : packs) {
validateVersion(version, packs); validateVersion(version, packs);
assets.registerPack(version.getPackName(), version.path, version.manifest); assets.registerPack(version.getPackName(), version.path, version.manifest, false);
Semver latest = versions.get(version.name); Semver latest = versions.get(version.name);
if (latest == null || version.manifest.getVersion().compareTo(latest) > 0) { if (latest == null || version.manifest.getVersion().compareTo(latest) > 0) {
versions.put(version.name, version.manifest.getVersion()); versions.put(version.name, version.manifest.getVersion());

View File

@@ -315,7 +315,7 @@ public class BuilderCodec<T> implements Codec<T>, DirectDecodeCodec<T>, RawJsonC
} }
} }
public void decodeJson0(@Nonnull RawJsonReader reader, T t, ExtraInfo extraInfo) throws IOException { private void decodeJson0(@Nonnull RawJsonReader reader, T t, ExtraInfo extraInfo) throws IOException {
reader.expect('{'); reader.expect('{');
reader.consumeWhiteSpace(); reader.consumeWhiteSpace();
if (!reader.tryConsume('}')) { if (!reader.tryConsume('}')) {

View File

@@ -426,15 +426,19 @@ public class StringUtil {
public static String toPaddedBinaryString(int val) { public static String toPaddedBinaryString(int val) {
byte[] buf = new byte[]{48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48}; byte[] buf = new byte[]{48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48};
int leadingZeros = Integer.numberOfLeadingZeros(val); int leadingZeros = Integer.numberOfLeadingZeros(val);
int mag = 32 - leadingZeros; if (leadingZeros == 32) {
int pos = Math.max(mag, 1); return new String(buf, 0);
} else {
int mag = 32 - leadingZeros;
int pos = Math.max(mag, 1);
do { do {
buf[leadingZeros + --pos] = (byte)(48 + (val & 1)); buf[leadingZeros + --pos] = (byte)(48 + (val & 1));
val >>>= 1; val >>>= 1;
} while (pos > 0); } while (pos > 0);
return new String(buf, 0); return new String(buf, 0);
}
} }
@Nonnull @Nonnull

View File

@@ -13,6 +13,7 @@ public class ChunkUtil {
public static final int SIZE_COLUMNS = 1024; public static final int SIZE_COLUMNS = 1024;
public static final int SIZE_COLUMNS_MASK = 1023; public static final int SIZE_COLUMNS_MASK = 1023;
public static final int SIZE_BLOCKS = 32768; public static final int SIZE_BLOCKS = 32768;
public static final int SIZE_BLOCKS_MASK = 32767;
public static final int BITS2 = 10; public static final int BITS2 = 10;
public static final int NON_CHUNK_MASK = -32; public static final int NON_CHUNK_MASK = -32;
public static final int HEIGHT_SECTIONS = 10; public static final int HEIGHT_SECTIONS = 10;

View File

@@ -29,7 +29,7 @@ public interface FileIO {
} }
@Nonnull @Nonnull
static FileIOSystem openFileIOSystem(@Nonnull FileIOSystem fs) { static <FS extends FileIOSystem> FS openFileIOSystem(@Nonnull FS fs) {
FileIOSystem.Provider.set(fs); FileIOSystem.Provider.set(fs);
return fs; return fs;
} }
@@ -71,16 +71,20 @@ public interface FileIO {
Path assetDirPath = relativize(path, fs.baseRoot()); Path assetDirPath = relativize(path, fs.baseRoot());
ObjectArrayList<AssetPath> paths = new ObjectArrayList<>(); ObjectArrayList<AssetPath> paths = new ObjectArrayList<>();
ObjectOpenHashSet<AssetPath> visited = new ObjectOpenHashSet<>(); ObjectOpenHashSet<AssetPath> visited = new ObjectOpenHashSet<>();
ObjectOpenHashSet<AssetPath> disabled = new ObjectOpenHashSet<>();
for (Path root : fs.roots().paths) { for (Path root : fs.roots().paths) {
Path rootAssetDirPath = append(root, assetDirPath); Path rootAssetDirPath = append(root, assetDirPath);
if (Files.exists(rootAssetDirPath) && Files.isDirectory(rootAssetDirPath)) { if (Files.exists(rootAssetDirPath) && Files.isDirectory(rootAssetDirPath)) {
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(rootAssetDirPath)) { try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(rootAssetDirPath)) {
visited.addAll(disabled);
disabled.clear();
for (Path filepath : dirStream) { for (Path filepath : dirStream) {
AssetPath assetPath = AssetPath.fromAbsolute(root, filepath); AssetPath assetPath = AssetPath.fromAbsolute(root, filepath);
AssetPath disabledPath = disableOp.apply(assetPath); AssetPath disabledPath = disableOp.apply(assetPath);
if (disabledPath != assetPath) { if (disabledPath != assetPath) {
visited.add(disabledPath); disabled.add(disabledPath);
} else if (matcher.test(assetPath) && visited.add(assetPath)) { } else if (matcher.test(assetPath) && visited.add(assetPath)) {
paths.add(assetPath); paths.add(assetPath);
} }

View File

@@ -0,0 +1,179 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.PacketIO;
import com.hypixel.hytale.protocol.io.ProtocolException;
import com.hypixel.hytale.protocol.io.ValidationResult;
import com.hypixel.hytale.protocol.io.VarInt;
import io.netty.buffer.ByteBuf;
import java.util.Arrays;
import javax.annotation.Nonnull;
public class ActiveAnimationsUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 0;
public static final int FIXED_BLOCK_SIZE = 0;
public static final int VARIABLE_FIELD_COUNT = 1;
public static final int VARIABLE_BLOCK_START = 0;
public static final int MAX_SIZE = 1677721600;
@Nonnull
public String[] activeAnimations = new String[0];
public ActiveAnimationsUpdate() {
}
public ActiveAnimationsUpdate(@Nonnull String[] activeAnimations) {
this.activeAnimations = activeAnimations;
}
public ActiveAnimationsUpdate(@Nonnull ActiveAnimationsUpdate other) {
this.activeAnimations = other.activeAnimations;
}
@Nonnull
public static ActiveAnimationsUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
ActiveAnimationsUpdate obj = new ActiveAnimationsUpdate();
int pos = offset + 0;
int activeAnimationsCount = VarInt.peek(buf, pos);
if (activeAnimationsCount < 0) {
throw ProtocolException.negativeLength("ActiveAnimations", activeAnimationsCount);
} else if (activeAnimationsCount > 4096000) {
throw ProtocolException.arrayTooLong("ActiveAnimations", activeAnimationsCount, 4096000);
} else {
pos += VarInt.size(activeAnimationsCount);
int activeAnimationsBitfieldSize = (activeAnimationsCount + 7) / 8;
byte[] activeAnimationsBitfield = PacketIO.readBytes(buf, pos, activeAnimationsBitfieldSize);
pos += activeAnimationsBitfieldSize;
obj.activeAnimations = new String[activeAnimationsCount];
for (int i = 0; i < activeAnimationsCount; i++) {
if ((activeAnimationsBitfield[i / 8] & 1 << i % 8) != 0) {
int strLen = VarInt.peek(buf, pos);
if (strLen < 0) {
throw ProtocolException.negativeLength("activeAnimations[" + i + "]", strLen);
}
if (strLen > 4096000) {
throw ProtocolException.stringTooLong("activeAnimations[" + i + "]", strLen, 4096000);
}
int strVarLen = VarInt.length(buf, pos);
obj.activeAnimations[i] = PacketIO.readVarString(buf, pos);
pos += strVarLen + strLen;
}
}
return obj;
}
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
int pos = offset + 0;
int arrLen = VarInt.peek(buf, pos);
pos += VarInt.length(buf, pos);
int bitfieldSize = (arrLen + 7) / 8;
byte[] bitfield = PacketIO.readBytes(buf, pos, bitfieldSize);
pos += bitfieldSize;
for (int i = 0; i < arrLen; i++) {
if ((bitfield[i / 8] & 1 << i % 8) != 0) {
int sl = VarInt.peek(buf, pos);
pos += VarInt.length(buf, pos) + sl;
}
}
return pos - offset;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
if (this.activeAnimations.length > 4096000) {
throw ProtocolException.arrayTooLong("ActiveAnimations", this.activeAnimations.length, 4096000);
} else {
VarInt.write(buf, this.activeAnimations.length);
int activeAnimationsBitfieldSize = (this.activeAnimations.length + 7) / 8;
byte[] activeAnimationsBitfield = new byte[activeAnimationsBitfieldSize];
for (int i = 0; i < this.activeAnimations.length; i++) {
if (this.activeAnimations[i] != null) {
activeAnimationsBitfield[i / 8] = (byte)(activeAnimationsBitfield[i / 8] | (byte)(1 << i % 8));
}
}
buf.writeBytes(activeAnimationsBitfield);
for (int ix = 0; ix < this.activeAnimations.length; ix++) {
if (this.activeAnimations[ix] != null) {
PacketIO.writeVarString(buf, this.activeAnimations[ix], 4096000);
}
}
return buf.writerIndex() - startPos;
}
}
@Override
public int computeSize() {
int size = 0;
int activeAnimationsSize = 0;
for (String elem : this.activeAnimations) {
if (elem != null) {
activeAnimationsSize += PacketIO.stringSize(elem);
}
}
return size + VarInt.size(this.activeAnimations.length) + (this.activeAnimations.length + 7) / 8 + activeAnimationsSize;
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
if (buffer.readableBytes() - offset < 0) {
return ValidationResult.error("Buffer too small: expected at least 0 bytes");
} else {
int pos = offset + 0;
int activeAnimationsCount = VarInt.peek(buffer, pos);
if (activeAnimationsCount < 0) {
return ValidationResult.error("Invalid array count for ActiveAnimations");
} else if (activeAnimationsCount > 4096000) {
return ValidationResult.error("ActiveAnimations exceeds max length 4096000");
} else {
pos += VarInt.length(buffer, pos);
for (int i = 0; i < activeAnimationsCount; i++) {
int strLen = VarInt.peek(buffer, pos);
if (strLen < 0) {
return ValidationResult.error("Invalid string length in ActiveAnimations");
}
pos += VarInt.length(buffer, pos);
pos += strLen;
if (pos > buffer.writerIndex()) {
return ValidationResult.error("Buffer overflow reading string in ActiveAnimations");
}
}
return ValidationResult.OK;
}
}
}
public ActiveAnimationsUpdate clone() {
ActiveAnimationsUpdate copy = new ActiveAnimationsUpdate();
copy.activeAnimations = Arrays.copyOf(this.activeAnimations, this.activeAnimations.length);
return copy;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else {
return obj instanceof ActiveAnimationsUpdate other ? Arrays.equals((Object[])this.activeAnimations, (Object[])other.activeAnimations) : false;
}
}
@Override
public int hashCode() {
int result = 1;
return 31 * result + Arrays.hashCode((Object[])this.activeAnimations);
}
}

View File

@@ -0,0 +1,124 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.ProtocolException;
import com.hypixel.hytale.protocol.io.ValidationResult;
import com.hypixel.hytale.protocol.io.VarInt;
import io.netty.buffer.ByteBuf;
import java.util.Arrays;
import javax.annotation.Nonnull;
public class AudioUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 0;
public static final int FIXED_BLOCK_SIZE = 0;
public static final int VARIABLE_FIELD_COUNT = 1;
public static final int VARIABLE_BLOCK_START = 0;
public static final int MAX_SIZE = 16384005;
@Nonnull
public int[] soundEventIds = new int[0];
public AudioUpdate() {
}
public AudioUpdate(@Nonnull int[] soundEventIds) {
this.soundEventIds = soundEventIds;
}
public AudioUpdate(@Nonnull AudioUpdate other) {
this.soundEventIds = other.soundEventIds;
}
@Nonnull
public static AudioUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
AudioUpdate obj = new AudioUpdate();
int pos = offset + 0;
int soundEventIdsCount = VarInt.peek(buf, pos);
if (soundEventIdsCount < 0) {
throw ProtocolException.negativeLength("SoundEventIds", soundEventIdsCount);
} else if (soundEventIdsCount > 4096000) {
throw ProtocolException.arrayTooLong("SoundEventIds", soundEventIdsCount, 4096000);
} else {
int soundEventIdsVarLen = VarInt.size(soundEventIdsCount);
if (pos + soundEventIdsVarLen + soundEventIdsCount * 4L > buf.readableBytes()) {
throw ProtocolException.bufferTooSmall("SoundEventIds", pos + soundEventIdsVarLen + soundEventIdsCount * 4, buf.readableBytes());
} else {
pos += soundEventIdsVarLen;
obj.soundEventIds = new int[soundEventIdsCount];
for (int i = 0; i < soundEventIdsCount; i++) {
obj.soundEventIds[i] = buf.getIntLE(pos + i * 4);
}
pos += soundEventIdsCount * 4;
return obj;
}
}
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
int pos = offset + 0;
int arrLen = VarInt.peek(buf, pos);
pos += VarInt.length(buf, pos) + arrLen * 4;
return pos - offset;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
if (this.soundEventIds.length > 4096000) {
throw ProtocolException.arrayTooLong("SoundEventIds", this.soundEventIds.length, 4096000);
} else {
VarInt.write(buf, this.soundEventIds.length);
for (int item : this.soundEventIds) {
buf.writeIntLE(item);
}
return buf.writerIndex() - startPos;
}
}
@Override
public int computeSize() {
int size = 0;
return size + VarInt.size(this.soundEventIds.length) + this.soundEventIds.length * 4;
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
if (buffer.readableBytes() - offset < 0) {
return ValidationResult.error("Buffer too small: expected at least 0 bytes");
} else {
int pos = offset + 0;
int soundEventIdsCount = VarInt.peek(buffer, pos);
if (soundEventIdsCount < 0) {
return ValidationResult.error("Invalid array count for SoundEventIds");
} else if (soundEventIdsCount > 4096000) {
return ValidationResult.error("SoundEventIds exceeds max length 4096000");
} else {
pos += VarInt.length(buffer, pos);
pos += soundEventIdsCount * 4;
return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading SoundEventIds") : ValidationResult.OK;
}
}
}
public AudioUpdate clone() {
AudioUpdate copy = new AudioUpdate();
copy.soundEventIds = Arrays.copyOf(this.soundEventIds, this.soundEventIds.length);
return copy;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else {
return obj instanceof AudioUpdate other ? Arrays.equals(this.soundEventIds, other.soundEventIds) : false;
}
}
@Override
public int hashCode() {
int result = 1;
return 31 * result + Arrays.hashCode(this.soundEventIds);
}
}

View File

@@ -18,8 +18,8 @@ public class BenchRequirement {
public static final int MAX_SIZE = 1677721600; public static final int MAX_SIZE = 1677721600;
@Nonnull @Nonnull
public BenchType type = BenchType.Crafting; public BenchType type = BenchType.Crafting;
@Nullable @Nonnull
public String id; public String id = "";
@Nullable @Nullable
public String[] categories; public String[] categories;
public int requiredTierLevel; public int requiredTierLevel;
@@ -27,7 +27,7 @@ public class BenchRequirement {
public BenchRequirement() { public BenchRequirement() {
} }
public BenchRequirement(@Nonnull BenchType type, @Nullable String id, @Nullable String[] categories, int requiredTierLevel) { public BenchRequirement(@Nonnull BenchType type, @Nonnull String id, @Nullable String[] categories, int requiredTierLevel) {
this.type = type; this.type = type;
this.id = id; this.id = id;
this.categories = categories; this.categories = categories;
@@ -47,84 +47,77 @@ public class BenchRequirement {
byte nullBits = buf.getByte(offset); byte nullBits = buf.getByte(offset);
obj.type = BenchType.fromValue(buf.getByte(offset + 1)); obj.type = BenchType.fromValue(buf.getByte(offset + 1));
obj.requiredTierLevel = buf.getIntLE(offset + 2); obj.requiredTierLevel = buf.getIntLE(offset + 2);
if ((nullBits & 1) != 0) { int varPos0 = offset + 14 + buf.getIntLE(offset + 6);
int varPos0 = offset + 14 + buf.getIntLE(offset + 6); int idLen = VarInt.peek(buf, varPos0);
int idLen = VarInt.peek(buf, varPos0); if (idLen < 0) {
if (idLen < 0) { throw ProtocolException.negativeLength("Id", idLen);
throw ProtocolException.negativeLength("Id", idLen); } else if (idLen > 4096000) {
} throw ProtocolException.stringTooLong("Id", idLen, 4096000);
} else {
if (idLen > 4096000) {
throw ProtocolException.stringTooLong("Id", idLen, 4096000);
}
obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8); obj.id = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8);
} if ((nullBits & 1) != 0) {
varPos0 = offset + 14 + buf.getIntLE(offset + 10);
if ((nullBits & 2) != 0) { idLen = VarInt.peek(buf, varPos0);
int varPos1 = offset + 14 + buf.getIntLE(offset + 10); if (idLen < 0) {
int categoriesCount = VarInt.peek(buf, varPos1); throw ProtocolException.negativeLength("Categories", idLen);
if (categoriesCount < 0) {
throw ProtocolException.negativeLength("Categories", categoriesCount);
}
if (categoriesCount > 4096000) {
throw ProtocolException.arrayTooLong("Categories", categoriesCount, 4096000);
}
int varIntLen = VarInt.length(buf, varPos1);
if (varPos1 + varIntLen + categoriesCount * 1L > buf.readableBytes()) {
throw ProtocolException.bufferTooSmall("Categories", varPos1 + varIntLen + categoriesCount * 1, buf.readableBytes());
}
obj.categories = new String[categoriesCount];
int elemPos = varPos1 + varIntLen;
for (int i = 0; i < categoriesCount; i++) {
int strLen = VarInt.peek(buf, elemPos);
if (strLen < 0) {
throw ProtocolException.negativeLength("categories[" + i + "]", strLen);
} }
if (strLen > 4096000) { if (idLen > 4096000) {
throw ProtocolException.stringTooLong("categories[" + i + "]", strLen, 4096000); throw ProtocolException.arrayTooLong("Categories", idLen, 4096000);
} }
int strVarLen = VarInt.length(buf, elemPos); int varIntLen = VarInt.length(buf, varPos0);
obj.categories[i] = PacketIO.readVarString(buf, elemPos); if (varPos0 + varIntLen + idLen * 1L > buf.readableBytes()) {
elemPos += strVarLen + strLen; throw ProtocolException.bufferTooSmall("Categories", varPos0 + varIntLen + idLen * 1, buf.readableBytes());
} }
}
return obj; obj.categories = new String[idLen];
int elemPos = varPos0 + varIntLen;
for (int i = 0; i < idLen; i++) {
int strLen = VarInt.peek(buf, elemPos);
if (strLen < 0) {
throw ProtocolException.negativeLength("categories[" + i + "]", strLen);
}
if (strLen > 4096000) {
throw ProtocolException.stringTooLong("categories[" + i + "]", strLen, 4096000);
}
int strVarLen = VarInt.length(buf, elemPos);
obj.categories[i] = PacketIO.readVarString(buf, elemPos);
elemPos += strVarLen + strLen;
}
}
return obj;
}
} }
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
byte nullBits = buf.getByte(offset); byte nullBits = buf.getByte(offset);
int maxEnd = 14; int maxEnd = 14;
if ((nullBits & 1) != 0) { int fieldOffset0 = buf.getIntLE(offset + 6);
int fieldOffset0 = buf.getIntLE(offset + 6); int pos0 = offset + 14 + fieldOffset0;
int pos0 = offset + 14 + fieldOffset0; int sl = VarInt.peek(buf, pos0);
int sl = VarInt.peek(buf, pos0); pos0 += VarInt.length(buf, pos0) + sl;
pos0 += VarInt.length(buf, pos0) + sl; if (pos0 - offset > maxEnd) {
if (pos0 - offset > maxEnd) { maxEnd = pos0 - offset;
maxEnd = pos0 - offset;
}
} }
if ((nullBits & 2) != 0) { if ((nullBits & 1) != 0) {
int fieldOffset1 = buf.getIntLE(offset + 10); fieldOffset0 = buf.getIntLE(offset + 10);
int pos1 = offset + 14 + fieldOffset1; pos0 = offset + 14 + fieldOffset0;
int arrLen = VarInt.peek(buf, pos1); sl = VarInt.peek(buf, pos0);
pos1 += VarInt.length(buf, pos1); pos0 += VarInt.length(buf, pos0);
for (int i = 0; i < arrLen; i++) { for (int i = 0; i < sl; i++) {
int sl = VarInt.peek(buf, pos1); int slx = VarInt.peek(buf, pos0);
pos1 += VarInt.length(buf, pos1) + sl; pos0 += VarInt.length(buf, pos0) + slx;
} }
if (pos1 - offset > maxEnd) { if (pos0 - offset > maxEnd) {
maxEnd = pos1 - offset; maxEnd = pos0 - offset;
} }
} }
@@ -134,12 +127,8 @@ public class BenchRequirement {
public void serialize(@Nonnull ByteBuf buf) { public void serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex(); int startPos = buf.writerIndex();
byte nullBits = 0; byte nullBits = 0;
if (this.id != null) {
nullBits = (byte)(nullBits | 1);
}
if (this.categories != null) { if (this.categories != null) {
nullBits = (byte)(nullBits | 2); nullBits = (byte)(nullBits | 1);
} }
buf.writeByte(nullBits); buf.writeByte(nullBits);
@@ -150,13 +139,8 @@ public class BenchRequirement {
int categoriesOffsetSlot = buf.writerIndex(); int categoriesOffsetSlot = buf.writerIndex();
buf.writeIntLE(0); buf.writeIntLE(0);
int varBlockStart = buf.writerIndex(); int varBlockStart = buf.writerIndex();
if (this.id != null) { buf.setIntLE(idOffsetSlot, buf.writerIndex() - varBlockStart);
buf.setIntLE(idOffsetSlot, buf.writerIndex() - varBlockStart); PacketIO.writeVarString(buf, this.id, 4096000);
PacketIO.writeVarString(buf, this.id, 4096000);
} else {
buf.setIntLE(idOffsetSlot, -1);
}
if (this.categories != null) { if (this.categories != null) {
buf.setIntLE(categoriesOffsetSlot, buf.writerIndex() - varBlockStart); buf.setIntLE(categoriesOffsetSlot, buf.writerIndex() - varBlockStart);
if (this.categories.length > 4096000) { if (this.categories.length > 4096000) {
@@ -175,10 +159,7 @@ public class BenchRequirement {
public int computeSize() { public int computeSize() {
int size = 14; int size = 14;
if (this.id != null) { size += PacketIO.stringSize(this.id);
size += PacketIO.stringSize(this.id);
}
if (this.categories != null) { if (this.categories != null) {
int categoriesSize = 0; int categoriesSize = 0;
@@ -197,70 +178,66 @@ public class BenchRequirement {
return ValidationResult.error("Buffer too small: expected at least 14 bytes"); return ValidationResult.error("Buffer too small: expected at least 14 bytes");
} else { } else {
byte nullBits = buffer.getByte(offset); byte nullBits = buffer.getByte(offset);
if ((nullBits & 1) != 0) { int idOffset = buffer.getIntLE(offset + 6);
int idOffset = buffer.getIntLE(offset + 6); if (idOffset < 0) {
if (idOffset < 0) { return ValidationResult.error("Invalid offset for Id");
return ValidationResult.error("Invalid offset for Id"); } else {
}
int pos = offset + 14 + idOffset; int pos = offset + 14 + idOffset;
if (pos >= buffer.writerIndex()) { if (pos >= buffer.writerIndex()) {
return ValidationResult.error("Offset out of bounds for Id"); return ValidationResult.error("Offset out of bounds for Id");
} } else {
int idLen = VarInt.peek(buffer, pos);
if (idLen < 0) {
return ValidationResult.error("Invalid string length for Id");
} else if (idLen > 4096000) {
return ValidationResult.error("Id exceeds max length 4096000");
} else {
pos += VarInt.length(buffer, pos);
pos += idLen;
if (pos > buffer.writerIndex()) {
return ValidationResult.error("Buffer overflow reading Id");
} else {
if ((nullBits & 1) != 0) {
idOffset = buffer.getIntLE(offset + 10);
if (idOffset < 0) {
return ValidationResult.error("Invalid offset for Categories");
}
int idLen = VarInt.peek(buffer, pos); pos = offset + 14 + idOffset;
if (idLen < 0) { if (pos >= buffer.writerIndex()) {
return ValidationResult.error("Invalid string length for Id"); return ValidationResult.error("Offset out of bounds for Categories");
} }
if (idLen > 4096000) { idLen = VarInt.peek(buffer, pos);
return ValidationResult.error("Id exceeds max length 4096000"); if (idLen < 0) {
} return ValidationResult.error("Invalid array count for Categories");
}
pos += VarInt.length(buffer, pos); if (idLen > 4096000) {
pos += idLen; return ValidationResult.error("Categories exceeds max length 4096000");
if (pos > buffer.writerIndex()) { }
return ValidationResult.error("Buffer overflow reading Id");
}
}
if ((nullBits & 2) != 0) { pos += VarInt.length(buffer, pos);
int categoriesOffset = buffer.getIntLE(offset + 10);
if (categoriesOffset < 0) {
return ValidationResult.error("Invalid offset for Categories");
}
int posx = offset + 14 + categoriesOffset; for (int i = 0; i < idLen; i++) {
if (posx >= buffer.writerIndex()) { int strLen = VarInt.peek(buffer, pos);
return ValidationResult.error("Offset out of bounds for Categories"); if (strLen < 0) {
} return ValidationResult.error("Invalid string length in Categories");
}
int categoriesCount = VarInt.peek(buffer, posx); pos += VarInt.length(buffer, pos);
if (categoriesCount < 0) { pos += strLen;
return ValidationResult.error("Invalid array count for Categories"); if (pos > buffer.writerIndex()) {
} return ValidationResult.error("Buffer overflow reading string in Categories");
}
}
}
if (categoriesCount > 4096000) { return ValidationResult.OK;
return ValidationResult.error("Categories exceeds max length 4096000"); }
}
posx += VarInt.length(buffer, posx);
for (int i = 0; i < categoriesCount; i++) {
int strLen = VarInt.peek(buffer, posx);
if (strLen < 0) {
return ValidationResult.error("Invalid string length in Categories");
}
posx += VarInt.length(buffer, posx);
posx += strLen;
if (posx > buffer.writerIndex()) {
return ValidationResult.error("Buffer overflow reading string in Categories");
} }
} }
} }
return ValidationResult.OK;
} }
} }

View File

@@ -0,0 +1,79 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.ValidationResult;
import io.netty.buffer.ByteBuf;
import java.util.Objects;
import javax.annotation.Nonnull;
public class BlockUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 0;
public static final int FIXED_BLOCK_SIZE = 8;
public static final int VARIABLE_FIELD_COUNT = 0;
public static final int VARIABLE_BLOCK_START = 8;
public static final int MAX_SIZE = 8;
public int blockId;
public float entityScale;
public BlockUpdate() {
}
public BlockUpdate(int blockId, float entityScale) {
this.blockId = blockId;
this.entityScale = entityScale;
}
public BlockUpdate(@Nonnull BlockUpdate other) {
this.blockId = other.blockId;
this.entityScale = other.entityScale;
}
@Nonnull
public static BlockUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
BlockUpdate obj = new BlockUpdate();
obj.blockId = buf.getIntLE(offset + 0);
obj.entityScale = buf.getFloatLE(offset + 4);
return obj;
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
return 8;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
buf.writeIntLE(this.blockId);
buf.writeFloatLE(this.entityScale);
return buf.writerIndex() - startPos;
}
@Override
public int computeSize() {
return 8;
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
return buffer.readableBytes() - offset < 8 ? ValidationResult.error("Buffer too small: expected at least 8 bytes") : ValidationResult.OK;
}
public BlockUpdate clone() {
BlockUpdate copy = new BlockUpdate();
copy.blockId = this.blockId;
copy.entityScale = this.entityScale;
return copy;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else {
return !(obj instanceof BlockUpdate other) ? false : this.blockId == other.blockId && this.entityScale == other.entityScale;
}
}
@Override
public int hashCode() {
return Objects.hash(this.blockId, this.entityScale);
}
}

View File

@@ -4,24 +4,26 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
public final class CachedPacket<T extends Packet> implements Packet, AutoCloseable { public final class CachedPacket<T extends ToClientPacket> implements ToClientPacket, AutoCloseable {
private final Class<T> packetType; private final Class<T> packetType;
private final int packetId; private final int packetId;
private final NetworkChannel packetChannel;
private final ByteBuf cachedBytes; private final ByteBuf cachedBytes;
private CachedPacket(Class<T> packetType, int packetId, ByteBuf cachedBytes) { private CachedPacket(Class<T> packetType, int packetId, NetworkChannel packetChannel, ByteBuf cachedBytes) {
this.packetType = packetType; this.packetType = packetType;
this.packetId = packetId; this.packetId = packetId;
this.packetChannel = packetChannel;
this.cachedBytes = cachedBytes; this.cachedBytes = cachedBytes;
} }
public static <T extends Packet> CachedPacket<T> cache(@Nonnull T packet) { public static <T extends ToClientPacket> CachedPacket<T> cache(@Nonnull T packet) {
if (packet instanceof CachedPacket) { if (packet instanceof CachedPacket) {
throw new IllegalArgumentException("Cannot cache a CachedPacket"); throw new IllegalArgumentException("Cannot cache a CachedPacket");
} else { } else {
ByteBuf buf = Unpooled.buffer(); ByteBuf buf = Unpooled.buffer();
packet.serialize(buf); packet.serialize(buf);
return new CachedPacket<>((Class<T>)packet.getClass(), packet.getId(), buf); return new CachedPacket<>((Class<T>)packet.getClass(), packet.getId(), packet.getChannel(), buf);
} }
} }
@@ -30,6 +32,11 @@ public final class CachedPacket<T extends Packet> implements Packet, AutoCloseab
return this.packetId; return this.packetId;
} }
@Override
public NetworkChannel getChannel() {
return this.packetChannel;
}
@Override @Override
public void serialize(@Nonnull ByteBuf buf) { public void serialize(@Nonnull ByteBuf buf) {
if (this.cachedBytes.refCnt() <= 0) { if (this.cachedBytes.refCnt() <= 0) {

View File

@@ -7,22 +7,21 @@ import com.hypixel.hytale.protocol.io.VarInt;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import java.util.Objects; import java.util.Objects;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class CombatTextUpdate { public class CombatTextUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 1; public static final int NULLABLE_BIT_FIELD_SIZE = 0;
public static final int FIXED_BLOCK_SIZE = 5; public static final int FIXED_BLOCK_SIZE = 4;
public static final int VARIABLE_FIELD_COUNT = 1; public static final int VARIABLE_FIELD_COUNT = 1;
public static final int VARIABLE_BLOCK_START = 5; public static final int VARIABLE_BLOCK_START = 4;
public static final int MAX_SIZE = 16384010; public static final int MAX_SIZE = 16384009;
public float hitAngleDeg; public float hitAngleDeg;
@Nullable @Nonnull
public String text; public String text = "";
public CombatTextUpdate() { public CombatTextUpdate() {
} }
public CombatTextUpdate(float hitAngleDeg, @Nullable String text) { public CombatTextUpdate(float hitAngleDeg, @Nonnull String text) {
this.hitAngleDeg = hitAngleDeg; this.hitAngleDeg = hitAngleDeg;
this.text = text; this.text = text;
} }
@@ -35,84 +34,57 @@ public class CombatTextUpdate {
@Nonnull @Nonnull
public static CombatTextUpdate deserialize(@Nonnull ByteBuf buf, int offset) { public static CombatTextUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
CombatTextUpdate obj = new CombatTextUpdate(); CombatTextUpdate obj = new CombatTextUpdate();
byte nullBits = buf.getByte(offset); obj.hitAngleDeg = buf.getFloatLE(offset + 0);
obj.hitAngleDeg = buf.getFloatLE(offset + 1); int pos = offset + 4;
int pos = offset + 5; int textLen = VarInt.peek(buf, pos);
if ((nullBits & 1) != 0) { if (textLen < 0) {
int textLen = VarInt.peek(buf, pos); throw ProtocolException.negativeLength("Text", textLen);
if (textLen < 0) { } else if (textLen > 4096000) {
throw ProtocolException.negativeLength("Text", textLen); throw ProtocolException.stringTooLong("Text", textLen, 4096000);
} } else {
if (textLen > 4096000) {
throw ProtocolException.stringTooLong("Text", textLen, 4096000);
}
int textVarLen = VarInt.length(buf, pos); int textVarLen = VarInt.length(buf, pos);
obj.text = PacketIO.readVarString(buf, pos, PacketIO.UTF8); obj.text = PacketIO.readVarString(buf, pos, PacketIO.UTF8);
pos += textVarLen + textLen; pos += textVarLen + textLen;
return obj;
} }
return obj;
} }
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
byte nullBits = buf.getByte(offset); int pos = offset + 4;
int pos = offset + 5; int sl = VarInt.peek(buf, pos);
if ((nullBits & 1) != 0) { pos += VarInt.length(buf, pos) + sl;
int sl = VarInt.peek(buf, pos);
pos += VarInt.length(buf, pos) + sl;
}
return pos - offset; return pos - offset;
} }
public void serialize(@Nonnull ByteBuf buf) { @Override
byte nullBits = 0; public int serialize(@Nonnull ByteBuf buf) {
if (this.text != null) { int startPos = buf.writerIndex();
nullBits = (byte)(nullBits | 1);
}
buf.writeByte(nullBits);
buf.writeFloatLE(this.hitAngleDeg); buf.writeFloatLE(this.hitAngleDeg);
if (this.text != null) { PacketIO.writeVarString(buf, this.text, 4096000);
PacketIO.writeVarString(buf, this.text, 4096000); return buf.writerIndex() - startPos;
}
} }
@Override
public int computeSize() { public int computeSize() {
int size = 5; int size = 4;
if (this.text != null) { return size + PacketIO.stringSize(this.text);
size += PacketIO.stringSize(this.text);
}
return size;
} }
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
if (buffer.readableBytes() - offset < 5) { if (buffer.readableBytes() - offset < 4) {
return ValidationResult.error("Buffer too small: expected at least 5 bytes"); return ValidationResult.error("Buffer too small: expected at least 4 bytes");
} else { } else {
byte nullBits = buffer.getByte(offset); int pos = offset + 4;
int pos = offset + 5; int textLen = VarInt.peek(buffer, pos);
if ((nullBits & 1) != 0) { if (textLen < 0) {
int textLen = VarInt.peek(buffer, pos); return ValidationResult.error("Invalid string length for Text");
if (textLen < 0) { } else if (textLen > 4096000) {
return ValidationResult.error("Invalid string length for Text"); return ValidationResult.error("Text exceeds max length 4096000");
} } else {
if (textLen > 4096000) {
return ValidationResult.error("Text exceeds max length 4096000");
}
pos += VarInt.length(buffer, pos); pos += VarInt.length(buffer, pos);
pos += textLen; pos += textLen;
if (pos > buffer.writerIndex()) { return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading Text") : ValidationResult.OK;
return ValidationResult.error("Buffer overflow reading Text");
}
} }
return ValidationResult.OK;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,74 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.ValidationResult;
import io.netty.buffer.ByteBuf;
import java.util.Objects;
import javax.annotation.Nonnull;
public class DynamicLightUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 0;
public static final int FIXED_BLOCK_SIZE = 4;
public static final int VARIABLE_FIELD_COUNT = 0;
public static final int VARIABLE_BLOCK_START = 4;
public static final int MAX_SIZE = 4;
@Nonnull
public ColorLight dynamicLight = new ColorLight();
public DynamicLightUpdate() {
}
public DynamicLightUpdate(@Nonnull ColorLight dynamicLight) {
this.dynamicLight = dynamicLight;
}
public DynamicLightUpdate(@Nonnull DynamicLightUpdate other) {
this.dynamicLight = other.dynamicLight;
}
@Nonnull
public static DynamicLightUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
DynamicLightUpdate obj = new DynamicLightUpdate();
obj.dynamicLight = ColorLight.deserialize(buf, offset + 0);
return obj;
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
return 4;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
this.dynamicLight.serialize(buf);
return buf.writerIndex() - startPos;
}
@Override
public int computeSize() {
return 4;
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
return buffer.readableBytes() - offset < 4 ? ValidationResult.error("Buffer too small: expected at least 4 bytes") : ValidationResult.OK;
}
public DynamicLightUpdate clone() {
DynamicLightUpdate copy = new DynamicLightUpdate();
copy.dynamicLight = this.dynamicLight.clone();
return copy;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else {
return obj instanceof DynamicLightUpdate other ? Objects.equals(this.dynamicLight, other.dynamicLight) : false;
}
}
@Override
public int hashCode() {
return Objects.hash(this.dynamicLight);
}
}

View File

@@ -0,0 +1,144 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.ProtocolException;
import com.hypixel.hytale.protocol.io.ValidationResult;
import com.hypixel.hytale.protocol.io.VarInt;
import io.netty.buffer.ByteBuf;
import java.util.Arrays;
import javax.annotation.Nonnull;
public class EntityEffectsUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 0;
public static final int FIXED_BLOCK_SIZE = 0;
public static final int VARIABLE_FIELD_COUNT = 1;
public static final int VARIABLE_BLOCK_START = 0;
public static final int MAX_SIZE = 1677721600;
@Nonnull
public EntityEffectUpdate[] entityEffectUpdates = new EntityEffectUpdate[0];
public EntityEffectsUpdate() {
}
public EntityEffectsUpdate(@Nonnull EntityEffectUpdate[] entityEffectUpdates) {
this.entityEffectUpdates = entityEffectUpdates;
}
public EntityEffectsUpdate(@Nonnull EntityEffectsUpdate other) {
this.entityEffectUpdates = other.entityEffectUpdates;
}
@Nonnull
public static EntityEffectsUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
EntityEffectsUpdate obj = new EntityEffectsUpdate();
int pos = offset + 0;
int entityEffectUpdatesCount = VarInt.peek(buf, pos);
if (entityEffectUpdatesCount < 0) {
throw ProtocolException.negativeLength("EntityEffectUpdates", entityEffectUpdatesCount);
} else if (entityEffectUpdatesCount > 4096000) {
throw ProtocolException.arrayTooLong("EntityEffectUpdates", entityEffectUpdatesCount, 4096000);
} else {
int entityEffectUpdatesVarLen = VarInt.size(entityEffectUpdatesCount);
if (pos + entityEffectUpdatesVarLen + entityEffectUpdatesCount * 12L > buf.readableBytes()) {
throw ProtocolException.bufferTooSmall("EntityEffectUpdates", pos + entityEffectUpdatesVarLen + entityEffectUpdatesCount * 12, buf.readableBytes());
} else {
pos += entityEffectUpdatesVarLen;
obj.entityEffectUpdates = new EntityEffectUpdate[entityEffectUpdatesCount];
for (int i = 0; i < entityEffectUpdatesCount; i++) {
obj.entityEffectUpdates[i] = EntityEffectUpdate.deserialize(buf, pos);
pos += EntityEffectUpdate.computeBytesConsumed(buf, pos);
}
return obj;
}
}
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
int pos = offset + 0;
int arrLen = VarInt.peek(buf, pos);
pos += VarInt.length(buf, pos);
for (int i = 0; i < arrLen; i++) {
pos += EntityEffectUpdate.computeBytesConsumed(buf, pos);
}
return pos - offset;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
if (this.entityEffectUpdates.length > 4096000) {
throw ProtocolException.arrayTooLong("EntityEffectUpdates", this.entityEffectUpdates.length, 4096000);
} else {
VarInt.write(buf, this.entityEffectUpdates.length);
for (EntityEffectUpdate item : this.entityEffectUpdates) {
item.serialize(buf);
}
return buf.writerIndex() - startPos;
}
}
@Override
public int computeSize() {
int size = 0;
int entityEffectUpdatesSize = 0;
for (EntityEffectUpdate elem : this.entityEffectUpdates) {
entityEffectUpdatesSize += elem.computeSize();
}
return size + VarInt.size(this.entityEffectUpdates.length) + entityEffectUpdatesSize;
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
if (buffer.readableBytes() - offset < 0) {
return ValidationResult.error("Buffer too small: expected at least 0 bytes");
} else {
int pos = offset + 0;
int entityEffectUpdatesCount = VarInt.peek(buffer, pos);
if (entityEffectUpdatesCount < 0) {
return ValidationResult.error("Invalid array count for EntityEffectUpdates");
} else if (entityEffectUpdatesCount > 4096000) {
return ValidationResult.error("EntityEffectUpdates exceeds max length 4096000");
} else {
pos += VarInt.length(buffer, pos);
for (int i = 0; i < entityEffectUpdatesCount; i++) {
ValidationResult structResult = EntityEffectUpdate.validateStructure(buffer, pos);
if (!structResult.isValid()) {
return ValidationResult.error("Invalid EntityEffectUpdate in EntityEffectUpdates[" + i + "]: " + structResult.error());
}
pos += EntityEffectUpdate.computeBytesConsumed(buffer, pos);
}
return ValidationResult.OK;
}
}
}
public EntityEffectsUpdate clone() {
EntityEffectsUpdate copy = new EntityEffectsUpdate();
copy.entityEffectUpdates = Arrays.stream(this.entityEffectUpdates).map(e -> e.clone()).toArray(EntityEffectUpdate[]::new);
return copy;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else {
return obj instanceof EntityEffectsUpdate other ? Arrays.equals((Object[])this.entityEffectUpdates, (Object[])other.entityEffectUpdates) : false;
}
}
@Override
public int hashCode() {
int result = 1;
return 31 * result + Arrays.hashCode((Object[])this.entityEffectUpdates);
}
}

View File

@@ -0,0 +1,193 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.ProtocolException;
import com.hypixel.hytale.protocol.io.ValidationResult;
import com.hypixel.hytale.protocol.io.VarInt;
import io.netty.buffer.ByteBuf;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Map.Entry;
import javax.annotation.Nonnull;
public class EntityStatsUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 0;
public static final int FIXED_BLOCK_SIZE = 0;
public static final int VARIABLE_FIELD_COUNT = 1;
public static final int VARIABLE_BLOCK_START = 0;
public static final int MAX_SIZE = 1677721600;
@Nonnull
public Map<Integer, EntityStatUpdate[]> entityStatUpdates = new HashMap<>();
public EntityStatsUpdate() {
}
public EntityStatsUpdate(@Nonnull Map<Integer, EntityStatUpdate[]> entityStatUpdates) {
this.entityStatUpdates = entityStatUpdates;
}
public EntityStatsUpdate(@Nonnull EntityStatsUpdate other) {
this.entityStatUpdates = other.entityStatUpdates;
}
@Nonnull
public static EntityStatsUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
EntityStatsUpdate obj = new EntityStatsUpdate();
int pos = offset + 0;
int entityStatUpdatesCount = VarInt.peek(buf, pos);
if (entityStatUpdatesCount < 0) {
throw ProtocolException.negativeLength("EntityStatUpdates", entityStatUpdatesCount);
} else if (entityStatUpdatesCount > 4096000) {
throw ProtocolException.dictionaryTooLarge("EntityStatUpdates", entityStatUpdatesCount, 4096000);
} else {
pos += VarInt.size(entityStatUpdatesCount);
obj.entityStatUpdates = new HashMap<>(entityStatUpdatesCount);
for (int i = 0; i < entityStatUpdatesCount; i++) {
int key = buf.getIntLE(pos);
pos += 4;
int valLen = VarInt.peek(buf, pos);
if (valLen < 0) {
throw ProtocolException.negativeLength("val", valLen);
}
if (valLen > 64) {
throw ProtocolException.arrayTooLong("val", valLen, 64);
}
int valVarLen = VarInt.length(buf, pos);
if (pos + valVarLen + valLen * 13L > buf.readableBytes()) {
throw ProtocolException.bufferTooSmall("val", pos + valVarLen + valLen * 13, buf.readableBytes());
}
pos += valVarLen;
EntityStatUpdate[] val = new EntityStatUpdate[valLen];
for (int valIdx = 0; valIdx < valLen; valIdx++) {
val[valIdx] = EntityStatUpdate.deserialize(buf, pos);
pos += EntityStatUpdate.computeBytesConsumed(buf, pos);
}
if (obj.entityStatUpdates.put(key, val) != null) {
throw ProtocolException.duplicateKey("entityStatUpdates", key);
}
}
return obj;
}
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
int pos = offset + 0;
int dictLen = VarInt.peek(buf, pos);
pos += VarInt.length(buf, pos);
for (int i = 0; i < dictLen; i++) {
pos += 4;
int al = VarInt.peek(buf, pos);
pos += VarInt.length(buf, pos);
for (int j = 0; j < al; j++) {
pos += EntityStatUpdate.computeBytesConsumed(buf, pos);
}
}
return pos - offset;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
if (this.entityStatUpdates.size() > 4096000) {
throw ProtocolException.dictionaryTooLarge("EntityStatUpdates", this.entityStatUpdates.size(), 4096000);
} else {
VarInt.write(buf, this.entityStatUpdates.size());
for (Entry<Integer, EntityStatUpdate[]> e : this.entityStatUpdates.entrySet()) {
buf.writeIntLE(e.getKey());
VarInt.write(buf, e.getValue().length);
for (EntityStatUpdate arrItem : e.getValue()) {
arrItem.serialize(buf);
}
}
return buf.writerIndex() - startPos;
}
}
@Override
public int computeSize() {
int size = 0;
int entityStatUpdatesSize = 0;
for (Entry<Integer, EntityStatUpdate[]> kvp : this.entityStatUpdates.entrySet()) {
entityStatUpdatesSize += 4 + VarInt.size(kvp.getValue().length) + Arrays.stream(kvp.getValue()).mapToInt(inner -> inner.computeSize()).sum();
}
return size + VarInt.size(this.entityStatUpdates.size()) + entityStatUpdatesSize;
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
if (buffer.readableBytes() - offset < 0) {
return ValidationResult.error("Buffer too small: expected at least 0 bytes");
} else {
int pos = offset + 0;
int entityStatUpdatesCount = VarInt.peek(buffer, pos);
if (entityStatUpdatesCount < 0) {
return ValidationResult.error("Invalid dictionary count for EntityStatUpdates");
} else if (entityStatUpdatesCount > 4096000) {
return ValidationResult.error("EntityStatUpdates exceeds max length 4096000");
} else {
pos += VarInt.length(buffer, pos);
for (int i = 0; i < entityStatUpdatesCount; i++) {
pos += 4;
if (pos > buffer.writerIndex()) {
return ValidationResult.error("Buffer overflow reading key");
}
int valueArrCount = VarInt.peek(buffer, pos);
if (valueArrCount < 0) {
return ValidationResult.error("Invalid array count for value");
}
pos += VarInt.length(buffer, pos);
for (int valueArrIdx = 0; valueArrIdx < valueArrCount; valueArrIdx++) {
pos += EntityStatUpdate.computeBytesConsumed(buffer, pos);
}
}
return ValidationResult.OK;
}
}
}
public EntityStatsUpdate clone() {
EntityStatsUpdate copy = new EntityStatsUpdate();
Map<Integer, EntityStatUpdate[]> m = new HashMap<>();
for (Entry<Integer, EntityStatUpdate[]> e : this.entityStatUpdates.entrySet()) {
m.put(e.getKey(), Arrays.stream(e.getValue()).map(x -> x.clone()).toArray(EntityStatUpdate[]::new));
}
copy.entityStatUpdates = m;
return copy;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else {
return obj instanceof EntityStatsUpdate other ? Objects.equals(this.entityStatUpdates, other.entityStatUpdates) : false;
}
}
@Override
public int hashCode() {
return Objects.hash(this.entityStatUpdates);
}
}

View File

@@ -77,8 +77,8 @@ public class EntityUpdate {
} }
int varIntLen = VarInt.length(buf, varPos1); int varIntLen = VarInt.length(buf, varPos1);
if (varPos1 + varIntLen + updatesCount * 160L > buf.readableBytes()) { if (varPos1 + varIntLen + updatesCount * 1L > buf.readableBytes()) {
throw ProtocolException.bufferTooSmall("Updates", varPos1 + varIntLen + updatesCount * 160, buf.readableBytes()); throw ProtocolException.bufferTooSmall("Updates", varPos1 + varIntLen + updatesCount * 1, buf.readableBytes());
} }
obj.updates = new ComponentUpdate[updatesCount]; obj.updates = new ComponentUpdate[updatesCount];
@@ -166,7 +166,7 @@ public class EntityUpdate {
VarInt.write(buf, this.updates.length); VarInt.write(buf, this.updates.length);
for (ComponentUpdate item : this.updates) { for (ComponentUpdate item : this.updates) {
item.serialize(buf); item.serializeWithTypeId(buf);
} }
} else { } else {
buf.setIntLE(updatesOffsetSlot, -1); buf.setIntLE(updatesOffsetSlot, -1);
@@ -183,7 +183,7 @@ public class EntityUpdate {
int updatesSize = 0; int updatesSize = 0;
for (ComponentUpdate elem : this.updates) { for (ComponentUpdate elem : this.updates) {
updatesSize += elem.computeSize(); updatesSize += elem.computeSizeWithTypeId();
} }
size += VarInt.size(this.updates.length) + updatesSize; size += VarInt.size(this.updates.length) + updatesSize;
@@ -264,7 +264,7 @@ public class EntityUpdate {
EntityUpdate copy = new EntityUpdate(); EntityUpdate copy = new EntityUpdate();
copy.networkId = this.networkId; copy.networkId = this.networkId;
copy.removed = this.removed != null ? Arrays.copyOf(this.removed, this.removed.length) : null; copy.removed = this.removed != null ? Arrays.copyOf(this.removed, this.removed.length) : null;
copy.updates = this.updates != null ? Arrays.stream(this.updates).map(e -> e.clone()).toArray(ComponentUpdate[]::new) : null; copy.updates = this.updates != null ? Arrays.copyOf(this.updates, this.updates.length) : null;
return copy; return copy;
} }

View File

@@ -10,7 +10,7 @@ import java.util.Objects;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public class Equipment { public class EquipmentUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 1; public static final int NULLABLE_BIT_FIELD_SIZE = 1;
public static final int FIXED_BLOCK_SIZE = 1; public static final int FIXED_BLOCK_SIZE = 1;
public static final int VARIABLE_FIELD_COUNT = 3; public static final int VARIABLE_FIELD_COUNT = 3;
@@ -23,24 +23,24 @@ public class Equipment {
@Nullable @Nullable
public String leftHandItemId; public String leftHandItemId;
public Equipment() { public EquipmentUpdate() {
} }
public Equipment(@Nullable String[] armorIds, @Nullable String rightHandItemId, @Nullable String leftHandItemId) { public EquipmentUpdate(@Nullable String[] armorIds, @Nullable String rightHandItemId, @Nullable String leftHandItemId) {
this.armorIds = armorIds; this.armorIds = armorIds;
this.rightHandItemId = rightHandItemId; this.rightHandItemId = rightHandItemId;
this.leftHandItemId = leftHandItemId; this.leftHandItemId = leftHandItemId;
} }
public Equipment(@Nonnull Equipment other) { public EquipmentUpdate(@Nonnull EquipmentUpdate other) {
this.armorIds = other.armorIds; this.armorIds = other.armorIds;
this.rightHandItemId = other.rightHandItemId; this.rightHandItemId = other.rightHandItemId;
this.leftHandItemId = other.leftHandItemId; this.leftHandItemId = other.leftHandItemId;
} }
@Nonnull @Nonnull
public static Equipment deserialize(@Nonnull ByteBuf buf, int offset) { public static EquipmentUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
Equipment obj = new Equipment(); EquipmentUpdate obj = new EquipmentUpdate();
byte nullBits = buf.getByte(offset); byte nullBits = buf.getByte(offset);
if ((nullBits & 1) != 0) { if ((nullBits & 1) != 0) {
int varPos0 = offset + 13 + buf.getIntLE(offset + 1); int varPos0 = offset + 13 + buf.getIntLE(offset + 1);
@@ -150,7 +150,8 @@ public class Equipment {
return maxEnd; return maxEnd;
} }
public void serialize(@Nonnull ByteBuf buf) { @Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex(); int startPos = buf.writerIndex();
byte nullBits = 0; byte nullBits = 0;
if (this.armorIds != null) { if (this.armorIds != null) {
@@ -201,8 +202,11 @@ public class Equipment {
} else { } else {
buf.setIntLE(leftHandItemIdOffsetSlot, -1); buf.setIntLE(leftHandItemIdOffsetSlot, -1);
} }
return buf.writerIndex() - startPos;
} }
@Override
public int computeSize() { public int computeSize() {
int size = 13; int size = 13;
if (this.armorIds != null) { if (this.armorIds != null) {
@@ -325,8 +329,8 @@ public class Equipment {
} }
} }
public Equipment clone() { public EquipmentUpdate clone() {
Equipment copy = new Equipment(); EquipmentUpdate copy = new EquipmentUpdate();
copy.armorIds = this.armorIds != null ? Arrays.copyOf(this.armorIds, this.armorIds.length) : null; copy.armorIds = this.armorIds != null ? Arrays.copyOf(this.armorIds, this.armorIds.length) : null;
copy.rightHandItemId = this.rightHandItemId; copy.rightHandItemId = this.rightHandItemId;
copy.leftHandItemId = this.leftHandItemId; copy.leftHandItemId = this.leftHandItemId;
@@ -338,7 +342,7 @@ public class Equipment {
if (this == obj) { if (this == obj) {
return true; return true;
} else { } else {
return !(obj instanceof Equipment other) return !(obj instanceof EquipmentUpdate other)
? false ? false
: Arrays.equals((Object[])this.armorIds, (Object[])other.armorIds) : Arrays.equals((Object[])this.armorIds, (Object[])other.armorIds)
&& Objects.equals(this.rightHandItemId, other.rightHandItemId) && Objects.equals(this.rightHandItemId, other.rightHandItemId)

View File

@@ -0,0 +1,73 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.ValidationResult;
import io.netty.buffer.ByteBuf;
import java.util.Objects;
import javax.annotation.Nonnull;
public class HitboxCollisionUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 0;
public static final int FIXED_BLOCK_SIZE = 4;
public static final int VARIABLE_FIELD_COUNT = 0;
public static final int VARIABLE_BLOCK_START = 4;
public static final int MAX_SIZE = 4;
public int hitboxCollisionConfigIndex;
public HitboxCollisionUpdate() {
}
public HitboxCollisionUpdate(int hitboxCollisionConfigIndex) {
this.hitboxCollisionConfigIndex = hitboxCollisionConfigIndex;
}
public HitboxCollisionUpdate(@Nonnull HitboxCollisionUpdate other) {
this.hitboxCollisionConfigIndex = other.hitboxCollisionConfigIndex;
}
@Nonnull
public static HitboxCollisionUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
HitboxCollisionUpdate obj = new HitboxCollisionUpdate();
obj.hitboxCollisionConfigIndex = buf.getIntLE(offset + 0);
return obj;
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
return 4;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
buf.writeIntLE(this.hitboxCollisionConfigIndex);
return buf.writerIndex() - startPos;
}
@Override
public int computeSize() {
return 4;
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
return buffer.readableBytes() - offset < 4 ? ValidationResult.error("Buffer too small: expected at least 4 bytes") : ValidationResult.OK;
}
public HitboxCollisionUpdate clone() {
HitboxCollisionUpdate copy = new HitboxCollisionUpdate();
copy.hitboxCollisionConfigIndex = this.hitboxCollisionConfigIndex;
return copy;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else {
return obj instanceof HitboxCollisionUpdate other ? this.hitboxCollisionConfigIndex == other.hitboxCollisionConfigIndex : false;
}
}
@Override
public int hashCode() {
return Objects.hash(this.hitboxCollisionConfigIndex);
}
}

View File

@@ -0,0 +1,51 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.ValidationResult;
import io.netty.buffer.ByteBuf;
import javax.annotation.Nonnull;
public class IntangibleUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 0;
public static final int FIXED_BLOCK_SIZE = 0;
public static final int VARIABLE_FIELD_COUNT = 0;
public static final int VARIABLE_BLOCK_START = 0;
public static final int MAX_SIZE = 0;
@Nonnull
public static IntangibleUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
return new IntangibleUpdate();
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
return 0;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
return buf.writerIndex() - startPos;
}
@Override
public int computeSize() {
return 0;
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
return buffer.readableBytes() - offset < 0 ? ValidationResult.error("Buffer too small: expected at least 0 bytes") : ValidationResult.OK;
}
public IntangibleUpdate clone() {
return new IntangibleUpdate();
}
@Override
public boolean equals(Object obj) {
return this == obj ? true : obj instanceof IntangibleUpdate other;
}
@Override
public int hashCode() {
return 0;
}
}

View File

@@ -9,44 +9,44 @@ import java.util.Objects;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public class Nameplate { public class InteractableUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 1; public static final int NULLABLE_BIT_FIELD_SIZE = 1;
public static final int FIXED_BLOCK_SIZE = 1; public static final int FIXED_BLOCK_SIZE = 1;
public static final int VARIABLE_FIELD_COUNT = 1; public static final int VARIABLE_FIELD_COUNT = 1;
public static final int VARIABLE_BLOCK_START = 1; public static final int VARIABLE_BLOCK_START = 1;
public static final int MAX_SIZE = 16384006; public static final int MAX_SIZE = 16384006;
@Nullable @Nullable
public String text; public String interactionHint;
public Nameplate() { public InteractableUpdate() {
} }
public Nameplate(@Nullable String text) { public InteractableUpdate(@Nullable String interactionHint) {
this.text = text; this.interactionHint = interactionHint;
} }
public Nameplate(@Nonnull Nameplate other) { public InteractableUpdate(@Nonnull InteractableUpdate other) {
this.text = other.text; this.interactionHint = other.interactionHint;
} }
@Nonnull @Nonnull
public static Nameplate deserialize(@Nonnull ByteBuf buf, int offset) { public static InteractableUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
Nameplate obj = new Nameplate(); InteractableUpdate obj = new InteractableUpdate();
byte nullBits = buf.getByte(offset); byte nullBits = buf.getByte(offset);
int pos = offset + 1; int pos = offset + 1;
if ((nullBits & 1) != 0) { if ((nullBits & 1) != 0) {
int textLen = VarInt.peek(buf, pos); int interactionHintLen = VarInt.peek(buf, pos);
if (textLen < 0) { if (interactionHintLen < 0) {
throw ProtocolException.negativeLength("Text", textLen); throw ProtocolException.negativeLength("InteractionHint", interactionHintLen);
} }
if (textLen > 4096000) { if (interactionHintLen > 4096000) {
throw ProtocolException.stringTooLong("Text", textLen, 4096000); throw ProtocolException.stringTooLong("InteractionHint", interactionHintLen, 4096000);
} }
int textVarLen = VarInt.length(buf, pos); int interactionHintVarLen = VarInt.length(buf, pos);
obj.text = PacketIO.readVarString(buf, pos, PacketIO.UTF8); obj.interactionHint = PacketIO.readVarString(buf, pos, PacketIO.UTF8);
pos += textVarLen + textLen; pos += interactionHintVarLen + interactionHintLen;
} }
return obj; return obj;
@@ -63,22 +63,27 @@ public class Nameplate {
return pos - offset; return pos - offset;
} }
public void serialize(@Nonnull ByteBuf buf) { @Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
byte nullBits = 0; byte nullBits = 0;
if (this.text != null) { if (this.interactionHint != null) {
nullBits = (byte)(nullBits | 1); nullBits = (byte)(nullBits | 1);
} }
buf.writeByte(nullBits); buf.writeByte(nullBits);
if (this.text != null) { if (this.interactionHint != null) {
PacketIO.writeVarString(buf, this.text, 4096000); PacketIO.writeVarString(buf, this.interactionHint, 4096000);
} }
return buf.writerIndex() - startPos;
} }
@Override
public int computeSize() { public int computeSize() {
int size = 1; int size = 1;
if (this.text != null) { if (this.interactionHint != null) {
size += PacketIO.stringSize(this.text); size += PacketIO.stringSize(this.interactionHint);
} }
return size; return size;
@@ -91,19 +96,19 @@ public class Nameplate {
byte nullBits = buffer.getByte(offset); byte nullBits = buffer.getByte(offset);
int pos = offset + 1; int pos = offset + 1;
if ((nullBits & 1) != 0) { if ((nullBits & 1) != 0) {
int textLen = VarInt.peek(buffer, pos); int interactionHintLen = VarInt.peek(buffer, pos);
if (textLen < 0) { if (interactionHintLen < 0) {
return ValidationResult.error("Invalid string length for Text"); return ValidationResult.error("Invalid string length for InteractionHint");
} }
if (textLen > 4096000) { if (interactionHintLen > 4096000) {
return ValidationResult.error("Text exceeds max length 4096000"); return ValidationResult.error("InteractionHint exceeds max length 4096000");
} }
pos += VarInt.length(buffer, pos); pos += VarInt.length(buffer, pos);
pos += textLen; pos += interactionHintLen;
if (pos > buffer.writerIndex()) { if (pos > buffer.writerIndex()) {
return ValidationResult.error("Buffer overflow reading Text"); return ValidationResult.error("Buffer overflow reading InteractionHint");
} }
} }
@@ -111,9 +116,9 @@ public class Nameplate {
} }
} }
public Nameplate clone() { public InteractableUpdate clone() {
Nameplate copy = new Nameplate(); InteractableUpdate copy = new InteractableUpdate();
copy.text = this.text; copy.interactionHint = this.interactionHint;
return copy; return copy;
} }
@@ -122,12 +127,12 @@ public class Nameplate {
if (this == obj) { if (this == obj) {
return true; return true;
} else { } else {
return obj instanceof Nameplate other ? Objects.equals(this.text, other.text) : false; return obj instanceof InteractableUpdate other ? Objects.equals(this.interactionHint, other.interactionHint) : false;
} }
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(this.text); return Objects.hash(this.interactionHint);
} }
} }

View File

@@ -0,0 +1,241 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.PacketIO;
import com.hypixel.hytale.protocol.io.ProtocolException;
import com.hypixel.hytale.protocol.io.ValidationResult;
import com.hypixel.hytale.protocol.io.VarInt;
import io.netty.buffer.ByteBuf;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Map.Entry;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class InteractionsUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 1;
public static final int FIXED_BLOCK_SIZE = 1;
public static final int VARIABLE_FIELD_COUNT = 2;
public static final int VARIABLE_BLOCK_START = 9;
public static final int MAX_SIZE = 36864019;
@Nonnull
public Map<InteractionType, Integer> interactions = new HashMap<>();
@Nullable
public String interactionHint;
public InteractionsUpdate() {
}
public InteractionsUpdate(@Nonnull Map<InteractionType, Integer> interactions, @Nullable String interactionHint) {
this.interactions = interactions;
this.interactionHint = interactionHint;
}
public InteractionsUpdate(@Nonnull InteractionsUpdate other) {
this.interactions = other.interactions;
this.interactionHint = other.interactionHint;
}
@Nonnull
public static InteractionsUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
InteractionsUpdate obj = new InteractionsUpdate();
byte nullBits = buf.getByte(offset);
int varPos0 = offset + 9 + buf.getIntLE(offset + 1);
int interactionsCount = VarInt.peek(buf, varPos0);
if (interactionsCount < 0) {
throw ProtocolException.negativeLength("Interactions", interactionsCount);
} else if (interactionsCount > 4096000) {
throw ProtocolException.dictionaryTooLarge("Interactions", interactionsCount, 4096000);
} else {
int varIntLen = VarInt.length(buf, varPos0);
obj.interactions = new HashMap<>(interactionsCount);
int dictPos = varPos0 + varIntLen;
for (int i = 0; i < interactionsCount; i++) {
InteractionType key = InteractionType.fromValue(buf.getByte(dictPos));
int val = buf.getIntLE(++dictPos);
dictPos += 4;
if (obj.interactions.put(key, val) != null) {
throw ProtocolException.duplicateKey("interactions", key);
}
}
if ((nullBits & 1) != 0) {
varPos0 = offset + 9 + buf.getIntLE(offset + 5);
interactionsCount = VarInt.peek(buf, varPos0);
if (interactionsCount < 0) {
throw ProtocolException.negativeLength("InteractionHint", interactionsCount);
}
if (interactionsCount > 4096000) {
throw ProtocolException.stringTooLong("InteractionHint", interactionsCount, 4096000);
}
obj.interactionHint = PacketIO.readVarString(buf, varPos0, PacketIO.UTF8);
}
return obj;
}
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
byte nullBits = buf.getByte(offset);
int maxEnd = 9;
int fieldOffset0 = buf.getIntLE(offset + 1);
int pos0 = offset + 9 + fieldOffset0;
int dictLen = VarInt.peek(buf, pos0);
pos0 += VarInt.length(buf, pos0);
for (int i = 0; i < dictLen; i++) {
pos0 = ++pos0 + 4;
}
if (pos0 - offset > maxEnd) {
maxEnd = pos0 - offset;
}
if ((nullBits & 1) != 0) {
fieldOffset0 = buf.getIntLE(offset + 5);
pos0 = offset + 9 + fieldOffset0;
dictLen = VarInt.peek(buf, pos0);
pos0 += VarInt.length(buf, pos0) + dictLen;
if (pos0 - offset > maxEnd) {
maxEnd = pos0 - offset;
}
}
return maxEnd;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
byte nullBits = 0;
if (this.interactionHint != null) {
nullBits = (byte)(nullBits | 1);
}
buf.writeByte(nullBits);
int interactionsOffsetSlot = buf.writerIndex();
buf.writeIntLE(0);
int interactionHintOffsetSlot = buf.writerIndex();
buf.writeIntLE(0);
int varBlockStart = buf.writerIndex();
buf.setIntLE(interactionsOffsetSlot, buf.writerIndex() - varBlockStart);
if (this.interactions.size() > 4096000) {
throw ProtocolException.dictionaryTooLarge("Interactions", this.interactions.size(), 4096000);
} else {
VarInt.write(buf, this.interactions.size());
for (Entry<InteractionType, Integer> e : this.interactions.entrySet()) {
buf.writeByte(e.getKey().getValue());
buf.writeIntLE(e.getValue());
}
if (this.interactionHint != null) {
buf.setIntLE(interactionHintOffsetSlot, buf.writerIndex() - varBlockStart);
PacketIO.writeVarString(buf, this.interactionHint, 4096000);
} else {
buf.setIntLE(interactionHintOffsetSlot, -1);
}
return buf.writerIndex() - startPos;
}
}
@Override
public int computeSize() {
int size = 9;
size += VarInt.size(this.interactions.size()) + this.interactions.size() * 5;
if (this.interactionHint != null) {
size += PacketIO.stringSize(this.interactionHint);
}
return size;
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
if (buffer.readableBytes() - offset < 9) {
return ValidationResult.error("Buffer too small: expected at least 9 bytes");
} else {
byte nullBits = buffer.getByte(offset);
int interactionsOffset = buffer.getIntLE(offset + 1);
if (interactionsOffset < 0) {
return ValidationResult.error("Invalid offset for Interactions");
} else {
int pos = offset + 9 + interactionsOffset;
if (pos >= buffer.writerIndex()) {
return ValidationResult.error("Offset out of bounds for Interactions");
} else {
int interactionsCount = VarInt.peek(buffer, pos);
if (interactionsCount < 0) {
return ValidationResult.error("Invalid dictionary count for Interactions");
} else if (interactionsCount > 4096000) {
return ValidationResult.error("Interactions exceeds max length 4096000");
} else {
pos += VarInt.length(buffer, pos);
for (int i = 0; i < interactionsCount; i++) {
pos = ++pos + 4;
if (pos > buffer.writerIndex()) {
return ValidationResult.error("Buffer overflow reading value");
}
}
if ((nullBits & 1) != 0) {
interactionsOffset = buffer.getIntLE(offset + 5);
if (interactionsOffset < 0) {
return ValidationResult.error("Invalid offset for InteractionHint");
}
pos = offset + 9 + interactionsOffset;
if (pos >= buffer.writerIndex()) {
return ValidationResult.error("Offset out of bounds for InteractionHint");
}
interactionsCount = VarInt.peek(buffer, pos);
if (interactionsCount < 0) {
return ValidationResult.error("Invalid string length for InteractionHint");
}
if (interactionsCount > 4096000) {
return ValidationResult.error("InteractionHint exceeds max length 4096000");
}
pos += VarInt.length(buffer, pos);
pos += interactionsCount;
if (pos > buffer.writerIndex()) {
return ValidationResult.error("Buffer overflow reading InteractionHint");
}
}
return ValidationResult.OK;
}
}
}
}
}
public InteractionsUpdate clone() {
InteractionsUpdate copy = new InteractionsUpdate();
copy.interactions = new HashMap<>(this.interactions);
copy.interactionHint = this.interactionHint;
return copy;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else {
return !(obj instanceof InteractionsUpdate other)
? false
: Objects.equals(this.interactions, other.interactions) && Objects.equals(this.interactionHint, other.interactionHint);
}
}
@Override
public int hashCode() {
return Objects.hash(this.interactions, this.interactionHint);
}
}

View File

@@ -0,0 +1,51 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.ValidationResult;
import io.netty.buffer.ByteBuf;
import javax.annotation.Nonnull;
public class InvulnerableUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 0;
public static final int FIXED_BLOCK_SIZE = 0;
public static final int VARIABLE_FIELD_COUNT = 0;
public static final int VARIABLE_BLOCK_START = 0;
public static final int MAX_SIZE = 0;
@Nonnull
public static InvulnerableUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
return new InvulnerableUpdate();
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
return 0;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
return buf.writerIndex() - startPos;
}
@Override
public int computeSize() {
return 0;
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
return buffer.readableBytes() - offset < 0 ? ValidationResult.error("Buffer too small: expected at least 0 bytes") : ValidationResult.OK;
}
public InvulnerableUpdate clone() {
return new InvulnerableUpdate();
}
@Override
public boolean equals(Object obj) {
return this == obj ? true : obj instanceof InvulnerableUpdate other;
}
@Override
public int hashCode() {
return 0;
}
}

View File

@@ -0,0 +1,96 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.ValidationResult;
import io.netty.buffer.ByteBuf;
import java.util.Objects;
import javax.annotation.Nonnull;
public class ItemUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 0;
public static final int FIXED_BLOCK_SIZE = 4;
public static final int VARIABLE_FIELD_COUNT = 1;
public static final int VARIABLE_BLOCK_START = 4;
public static final int MAX_SIZE = 32768044;
@Nonnull
public ItemWithAllMetadata item = new ItemWithAllMetadata();
public float entityScale;
public ItemUpdate() {
}
public ItemUpdate(@Nonnull ItemWithAllMetadata item, float entityScale) {
this.item = item;
this.entityScale = entityScale;
}
public ItemUpdate(@Nonnull ItemUpdate other) {
this.item = other.item;
this.entityScale = other.entityScale;
}
@Nonnull
public static ItemUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
ItemUpdate obj = new ItemUpdate();
obj.entityScale = buf.getFloatLE(offset + 0);
int pos = offset + 4;
obj.item = ItemWithAllMetadata.deserialize(buf, pos);
pos += ItemWithAllMetadata.computeBytesConsumed(buf, pos);
return obj;
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
int pos = offset + 4;
pos += ItemWithAllMetadata.computeBytesConsumed(buf, pos);
return pos - offset;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
buf.writeFloatLE(this.entityScale);
this.item.serialize(buf);
return buf.writerIndex() - startPos;
}
@Override
public int computeSize() {
int size = 4;
return size + this.item.computeSize();
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
if (buffer.readableBytes() - offset < 4) {
return ValidationResult.error("Buffer too small: expected at least 4 bytes");
} else {
int pos = offset + 4;
ValidationResult itemResult = ItemWithAllMetadata.validateStructure(buffer, pos);
if (!itemResult.isValid()) {
return ValidationResult.error("Invalid Item: " + itemResult.error());
} else {
pos += ItemWithAllMetadata.computeBytesConsumed(buffer, pos);
return ValidationResult.OK;
}
}
}
public ItemUpdate clone() {
ItemUpdate copy = new ItemUpdate();
copy.item = this.item.clone();
copy.entityScale = this.entityScale;
return copy;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else {
return !(obj instanceof ItemUpdate other) ? false : Objects.equals(this.item, other.item) && this.entityScale == other.entityScale;
}
}
@Override
public int hashCode() {
return Objects.hash(this.item, this.entityScale);
}
}

View File

@@ -15,9 +15,9 @@ import javax.annotation.Nullable;
public class Model { public class Model {
public static final int NULLABLE_BIT_FIELD_SIZE = 2; public static final int NULLABLE_BIT_FIELD_SIZE = 2;
public static final int FIXED_BLOCK_SIZE = 43; public static final int FIXED_BLOCK_SIZE = 51;
public static final int VARIABLE_FIELD_COUNT = 12; public static final int VARIABLE_FIELD_COUNT = 12;
public static final int VARIABLE_BLOCK_START = 91; public static final int VARIABLE_BLOCK_START = 99;
public static final int MAX_SIZE = 1677721600; public static final int MAX_SIZE = 1677721600;
@Nullable @Nullable
public String assetId; public String assetId;
@@ -34,6 +34,8 @@ public class Model {
public float scale; public float scale;
public float eyeHeight; public float eyeHeight;
public float crouchOffset; public float crouchOffset;
public float sittingOffset;
public float sleepingOffset;
@Nullable @Nullable
public Map<String, AnimationSet> animationSets; public Map<String, AnimationSet> animationSets;
@Nullable @Nullable
@@ -66,6 +68,8 @@ public class Model {
float scale, float scale,
float eyeHeight, float eyeHeight,
float crouchOffset, float crouchOffset,
float sittingOffset,
float sleepingOffset,
@Nullable Map<String, AnimationSet> animationSets, @Nullable Map<String, AnimationSet> animationSets,
@Nullable ModelAttachment[] attachments, @Nullable ModelAttachment[] attachments,
@Nullable Hitbox hitbox, @Nullable Hitbox hitbox,
@@ -85,6 +89,8 @@ public class Model {
this.scale = scale; this.scale = scale;
this.eyeHeight = eyeHeight; this.eyeHeight = eyeHeight;
this.crouchOffset = crouchOffset; this.crouchOffset = crouchOffset;
this.sittingOffset = sittingOffset;
this.sleepingOffset = sleepingOffset;
this.animationSets = animationSets; this.animationSets = animationSets;
this.attachments = attachments; this.attachments = attachments;
this.hitbox = hitbox; this.hitbox = hitbox;
@@ -106,6 +112,8 @@ public class Model {
this.scale = other.scale; this.scale = other.scale;
this.eyeHeight = other.eyeHeight; this.eyeHeight = other.eyeHeight;
this.crouchOffset = other.crouchOffset; this.crouchOffset = other.crouchOffset;
this.sittingOffset = other.sittingOffset;
this.sleepingOffset = other.sleepingOffset;
this.animationSets = other.animationSets; this.animationSets = other.animationSets;
this.attachments = other.attachments; this.attachments = other.attachments;
this.hitbox = other.hitbox; this.hitbox = other.hitbox;
@@ -124,17 +132,19 @@ public class Model {
obj.scale = buf.getFloatLE(offset + 2); obj.scale = buf.getFloatLE(offset + 2);
obj.eyeHeight = buf.getFloatLE(offset + 6); obj.eyeHeight = buf.getFloatLE(offset + 6);
obj.crouchOffset = buf.getFloatLE(offset + 10); obj.crouchOffset = buf.getFloatLE(offset + 10);
obj.sittingOffset = buf.getFloatLE(offset + 14);
obj.sleepingOffset = buf.getFloatLE(offset + 18);
if ((nullBits[0] & 1) != 0) { if ((nullBits[0] & 1) != 0) {
obj.hitbox = Hitbox.deserialize(buf, offset + 14); obj.hitbox = Hitbox.deserialize(buf, offset + 22);
} }
if ((nullBits[0] & 2) != 0) { if ((nullBits[0] & 2) != 0) {
obj.light = ColorLight.deserialize(buf, offset + 38); obj.light = ColorLight.deserialize(buf, offset + 46);
} }
obj.phobia = Phobia.fromValue(buf.getByte(offset + 42)); obj.phobia = Phobia.fromValue(buf.getByte(offset + 50));
if ((nullBits[0] & 4) != 0) { if ((nullBits[0] & 4) != 0) {
int varPos0 = offset + 91 + buf.getIntLE(offset + 43); int varPos0 = offset + 99 + buf.getIntLE(offset + 51);
int assetIdLen = VarInt.peek(buf, varPos0); int assetIdLen = VarInt.peek(buf, varPos0);
if (assetIdLen < 0) { if (assetIdLen < 0) {
throw ProtocolException.negativeLength("AssetId", assetIdLen); throw ProtocolException.negativeLength("AssetId", assetIdLen);
@@ -148,7 +158,7 @@ public class Model {
} }
if ((nullBits[0] & 8) != 0) { if ((nullBits[0] & 8) != 0) {
int varPos1 = offset + 91 + buf.getIntLE(offset + 47); int varPos1 = offset + 99 + buf.getIntLE(offset + 55);
int pathLen = VarInt.peek(buf, varPos1); int pathLen = VarInt.peek(buf, varPos1);
if (pathLen < 0) { if (pathLen < 0) {
throw ProtocolException.negativeLength("Path", pathLen); throw ProtocolException.negativeLength("Path", pathLen);
@@ -162,7 +172,7 @@ public class Model {
} }
if ((nullBits[0] & 16) != 0) { if ((nullBits[0] & 16) != 0) {
int varPos2 = offset + 91 + buf.getIntLE(offset + 51); int varPos2 = offset + 99 + buf.getIntLE(offset + 59);
int textureLen = VarInt.peek(buf, varPos2); int textureLen = VarInt.peek(buf, varPos2);
if (textureLen < 0) { if (textureLen < 0) {
throw ProtocolException.negativeLength("Texture", textureLen); throw ProtocolException.negativeLength("Texture", textureLen);
@@ -176,7 +186,7 @@ public class Model {
} }
if ((nullBits[0] & 32) != 0) { if ((nullBits[0] & 32) != 0) {
int varPos3 = offset + 91 + buf.getIntLE(offset + 55); int varPos3 = offset + 99 + buf.getIntLE(offset + 63);
int gradientSetLen = VarInt.peek(buf, varPos3); int gradientSetLen = VarInt.peek(buf, varPos3);
if (gradientSetLen < 0) { if (gradientSetLen < 0) {
throw ProtocolException.negativeLength("GradientSet", gradientSetLen); throw ProtocolException.negativeLength("GradientSet", gradientSetLen);
@@ -190,7 +200,7 @@ public class Model {
} }
if ((nullBits[0] & 64) != 0) { if ((nullBits[0] & 64) != 0) {
int varPos4 = offset + 91 + buf.getIntLE(offset + 59); int varPos4 = offset + 99 + buf.getIntLE(offset + 67);
int gradientIdLen = VarInt.peek(buf, varPos4); int gradientIdLen = VarInt.peek(buf, varPos4);
if (gradientIdLen < 0) { if (gradientIdLen < 0) {
throw ProtocolException.negativeLength("GradientId", gradientIdLen); throw ProtocolException.negativeLength("GradientId", gradientIdLen);
@@ -204,12 +214,12 @@ public class Model {
} }
if ((nullBits[0] & 128) != 0) { if ((nullBits[0] & 128) != 0) {
int varPos5 = offset + 91 + buf.getIntLE(offset + 63); int varPos5 = offset + 99 + buf.getIntLE(offset + 71);
obj.camera = CameraSettings.deserialize(buf, varPos5); obj.camera = CameraSettings.deserialize(buf, varPos5);
} }
if ((nullBits[1] & 1) != 0) { if ((nullBits[1] & 1) != 0) {
int varPos6 = offset + 91 + buf.getIntLE(offset + 67); int varPos6 = offset + 99 + buf.getIntLE(offset + 75);
int animationSetsCount = VarInt.peek(buf, varPos6); int animationSetsCount = VarInt.peek(buf, varPos6);
if (animationSetsCount < 0) { if (animationSetsCount < 0) {
throw ProtocolException.negativeLength("AnimationSets", animationSetsCount); throw ProtocolException.negativeLength("AnimationSets", animationSetsCount);
@@ -245,7 +255,7 @@ public class Model {
} }
if ((nullBits[1] & 2) != 0) { if ((nullBits[1] & 2) != 0) {
int varPos7 = offset + 91 + buf.getIntLE(offset + 71); int varPos7 = offset + 99 + buf.getIntLE(offset + 79);
int attachmentsCount = VarInt.peek(buf, varPos7); int attachmentsCount = VarInt.peek(buf, varPos7);
if (attachmentsCount < 0) { if (attachmentsCount < 0) {
throw ProtocolException.negativeLength("Attachments", attachmentsCount); throw ProtocolException.negativeLength("Attachments", attachmentsCount);
@@ -270,7 +280,7 @@ public class Model {
} }
if ((nullBits[1] & 4) != 0) { if ((nullBits[1] & 4) != 0) {
int varPos8 = offset + 91 + buf.getIntLE(offset + 75); int varPos8 = offset + 99 + buf.getIntLE(offset + 83);
int particlesCount = VarInt.peek(buf, varPos8); int particlesCount = VarInt.peek(buf, varPos8);
if (particlesCount < 0) { if (particlesCount < 0) {
throw ProtocolException.negativeLength("Particles", particlesCount); throw ProtocolException.negativeLength("Particles", particlesCount);
@@ -295,7 +305,7 @@ public class Model {
} }
if ((nullBits[1] & 8) != 0) { if ((nullBits[1] & 8) != 0) {
int varPos9 = offset + 91 + buf.getIntLE(offset + 79); int varPos9 = offset + 99 + buf.getIntLE(offset + 87);
int trailsCount = VarInt.peek(buf, varPos9); int trailsCount = VarInt.peek(buf, varPos9);
if (trailsCount < 0) { if (trailsCount < 0) {
throw ProtocolException.negativeLength("Trails", trailsCount); throw ProtocolException.negativeLength("Trails", trailsCount);
@@ -320,7 +330,7 @@ public class Model {
} }
if ((nullBits[1] & 16) != 0) { if ((nullBits[1] & 16) != 0) {
int varPos10 = offset + 91 + buf.getIntLE(offset + 83); int varPos10 = offset + 99 + buf.getIntLE(offset + 91);
int detailBoxesCount = VarInt.peek(buf, varPos10); int detailBoxesCount = VarInt.peek(buf, varPos10);
if (detailBoxesCount < 0) { if (detailBoxesCount < 0) {
throw ProtocolException.negativeLength("DetailBoxes", detailBoxesCount); throw ProtocolException.negativeLength("DetailBoxes", detailBoxesCount);
@@ -376,7 +386,7 @@ public class Model {
} }
if ((nullBits[1] & 32) != 0) { if ((nullBits[1] & 32) != 0) {
int varPos11 = offset + 91 + buf.getIntLE(offset + 87); int varPos11 = offset + 99 + buf.getIntLE(offset + 95);
obj.phobiaModel = deserialize(buf, varPos11); obj.phobiaModel = deserialize(buf, varPos11);
} }
@@ -385,10 +395,10 @@ public class Model {
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) { public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
byte[] nullBits = PacketIO.readBytes(buf, offset, 2); byte[] nullBits = PacketIO.readBytes(buf, offset, 2);
int maxEnd = 91; int maxEnd = 99;
if ((nullBits[0] & 4) != 0) { if ((nullBits[0] & 4) != 0) {
int fieldOffset0 = buf.getIntLE(offset + 43); int fieldOffset0 = buf.getIntLE(offset + 51);
int pos0 = offset + 91 + fieldOffset0; int pos0 = offset + 99 + fieldOffset0;
int sl = VarInt.peek(buf, pos0); int sl = VarInt.peek(buf, pos0);
pos0 += VarInt.length(buf, pos0) + sl; pos0 += VarInt.length(buf, pos0) + sl;
if (pos0 - offset > maxEnd) { if (pos0 - offset > maxEnd) {
@@ -397,8 +407,8 @@ public class Model {
} }
if ((nullBits[0] & 8) != 0) { if ((nullBits[0] & 8) != 0) {
int fieldOffset1 = buf.getIntLE(offset + 47); int fieldOffset1 = buf.getIntLE(offset + 55);
int pos1 = offset + 91 + fieldOffset1; int pos1 = offset + 99 + fieldOffset1;
int sl = VarInt.peek(buf, pos1); int sl = VarInt.peek(buf, pos1);
pos1 += VarInt.length(buf, pos1) + sl; pos1 += VarInt.length(buf, pos1) + sl;
if (pos1 - offset > maxEnd) { if (pos1 - offset > maxEnd) {
@@ -407,8 +417,8 @@ public class Model {
} }
if ((nullBits[0] & 16) != 0) { if ((nullBits[0] & 16) != 0) {
int fieldOffset2 = buf.getIntLE(offset + 51); int fieldOffset2 = buf.getIntLE(offset + 59);
int pos2 = offset + 91 + fieldOffset2; int pos2 = offset + 99 + fieldOffset2;
int sl = VarInt.peek(buf, pos2); int sl = VarInt.peek(buf, pos2);
pos2 += VarInt.length(buf, pos2) + sl; pos2 += VarInt.length(buf, pos2) + sl;
if (pos2 - offset > maxEnd) { if (pos2 - offset > maxEnd) {
@@ -417,8 +427,8 @@ public class Model {
} }
if ((nullBits[0] & 32) != 0) { if ((nullBits[0] & 32) != 0) {
int fieldOffset3 = buf.getIntLE(offset + 55); int fieldOffset3 = buf.getIntLE(offset + 63);
int pos3 = offset + 91 + fieldOffset3; int pos3 = offset + 99 + fieldOffset3;
int sl = VarInt.peek(buf, pos3); int sl = VarInt.peek(buf, pos3);
pos3 += VarInt.length(buf, pos3) + sl; pos3 += VarInt.length(buf, pos3) + sl;
if (pos3 - offset > maxEnd) { if (pos3 - offset > maxEnd) {
@@ -427,8 +437,8 @@ public class Model {
} }
if ((nullBits[0] & 64) != 0) { if ((nullBits[0] & 64) != 0) {
int fieldOffset4 = buf.getIntLE(offset + 59); int fieldOffset4 = buf.getIntLE(offset + 67);
int pos4 = offset + 91 + fieldOffset4; int pos4 = offset + 99 + fieldOffset4;
int sl = VarInt.peek(buf, pos4); int sl = VarInt.peek(buf, pos4);
pos4 += VarInt.length(buf, pos4) + sl; pos4 += VarInt.length(buf, pos4) + sl;
if (pos4 - offset > maxEnd) { if (pos4 - offset > maxEnd) {
@@ -437,8 +447,8 @@ public class Model {
} }
if ((nullBits[0] & 128) != 0) { if ((nullBits[0] & 128) != 0) {
int fieldOffset5 = buf.getIntLE(offset + 63); int fieldOffset5 = buf.getIntLE(offset + 71);
int pos5 = offset + 91 + fieldOffset5; int pos5 = offset + 99 + fieldOffset5;
pos5 += CameraSettings.computeBytesConsumed(buf, pos5); pos5 += CameraSettings.computeBytesConsumed(buf, pos5);
if (pos5 - offset > maxEnd) { if (pos5 - offset > maxEnd) {
maxEnd = pos5 - offset; maxEnd = pos5 - offset;
@@ -446,8 +456,8 @@ public class Model {
} }
if ((nullBits[1] & 1) != 0) { if ((nullBits[1] & 1) != 0) {
int fieldOffset6 = buf.getIntLE(offset + 67); int fieldOffset6 = buf.getIntLE(offset + 75);
int pos6 = offset + 91 + fieldOffset6; int pos6 = offset + 99 + fieldOffset6;
int dictLen = VarInt.peek(buf, pos6); int dictLen = VarInt.peek(buf, pos6);
pos6 += VarInt.length(buf, pos6); pos6 += VarInt.length(buf, pos6);
@@ -463,8 +473,8 @@ public class Model {
} }
if ((nullBits[1] & 2) != 0) { if ((nullBits[1] & 2) != 0) {
int fieldOffset7 = buf.getIntLE(offset + 71); int fieldOffset7 = buf.getIntLE(offset + 79);
int pos7 = offset + 91 + fieldOffset7; int pos7 = offset + 99 + fieldOffset7;
int arrLen = VarInt.peek(buf, pos7); int arrLen = VarInt.peek(buf, pos7);
pos7 += VarInt.length(buf, pos7); pos7 += VarInt.length(buf, pos7);
@@ -478,8 +488,8 @@ public class Model {
} }
if ((nullBits[1] & 4) != 0) { if ((nullBits[1] & 4) != 0) {
int fieldOffset8 = buf.getIntLE(offset + 75); int fieldOffset8 = buf.getIntLE(offset + 83);
int pos8 = offset + 91 + fieldOffset8; int pos8 = offset + 99 + fieldOffset8;
int arrLen = VarInt.peek(buf, pos8); int arrLen = VarInt.peek(buf, pos8);
pos8 += VarInt.length(buf, pos8); pos8 += VarInt.length(buf, pos8);
@@ -493,8 +503,8 @@ public class Model {
} }
if ((nullBits[1] & 8) != 0) { if ((nullBits[1] & 8) != 0) {
int fieldOffset9 = buf.getIntLE(offset + 79); int fieldOffset9 = buf.getIntLE(offset + 87);
int pos9 = offset + 91 + fieldOffset9; int pos9 = offset + 99 + fieldOffset9;
int arrLen = VarInt.peek(buf, pos9); int arrLen = VarInt.peek(buf, pos9);
pos9 += VarInt.length(buf, pos9); pos9 += VarInt.length(buf, pos9);
@@ -508,8 +518,8 @@ public class Model {
} }
if ((nullBits[1] & 16) != 0) { if ((nullBits[1] & 16) != 0) {
int fieldOffset10 = buf.getIntLE(offset + 83); int fieldOffset10 = buf.getIntLE(offset + 91);
int pos10 = offset + 91 + fieldOffset10; int pos10 = offset + 99 + fieldOffset10;
int dictLen = VarInt.peek(buf, pos10); int dictLen = VarInt.peek(buf, pos10);
pos10 += VarInt.length(buf, pos10); pos10 += VarInt.length(buf, pos10);
@@ -530,8 +540,8 @@ public class Model {
} }
if ((nullBits[1] & 32) != 0) { if ((nullBits[1] & 32) != 0) {
int fieldOffset11 = buf.getIntLE(offset + 87); int fieldOffset11 = buf.getIntLE(offset + 95);
int pos11 = offset + 91 + fieldOffset11; int pos11 = offset + 99 + fieldOffset11;
pos11 += computeBytesConsumed(buf, pos11); pos11 += computeBytesConsumed(buf, pos11);
if (pos11 - offset > maxEnd) { if (pos11 - offset > maxEnd) {
maxEnd = pos11 - offset; maxEnd = pos11 - offset;
@@ -604,6 +614,8 @@ public class Model {
buf.writeFloatLE(this.scale); buf.writeFloatLE(this.scale);
buf.writeFloatLE(this.eyeHeight); buf.writeFloatLE(this.eyeHeight);
buf.writeFloatLE(this.crouchOffset); buf.writeFloatLE(this.crouchOffset);
buf.writeFloatLE(this.sittingOffset);
buf.writeFloatLE(this.sleepingOffset);
if (this.hitbox != null) { if (this.hitbox != null) {
this.hitbox.serialize(buf); this.hitbox.serialize(buf);
} else { } else {
@@ -774,7 +786,7 @@ public class Model {
} }
public int computeSize() { public int computeSize() {
int size = 91; int size = 99;
if (this.assetId != null) { if (this.assetId != null) {
size += PacketIO.stringSize(this.assetId); size += PacketIO.stringSize(this.assetId);
} }
@@ -857,17 +869,17 @@ public class Model {
} }
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) { public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
if (buffer.readableBytes() - offset < 91) { if (buffer.readableBytes() - offset < 99) {
return ValidationResult.error("Buffer too small: expected at least 91 bytes"); return ValidationResult.error("Buffer too small: expected at least 99 bytes");
} else { } else {
byte[] nullBits = PacketIO.readBytes(buffer, offset, 2); byte[] nullBits = PacketIO.readBytes(buffer, offset, 2);
if ((nullBits[0] & 4) != 0) { if ((nullBits[0] & 4) != 0) {
int assetIdOffset = buffer.getIntLE(offset + 43); int assetIdOffset = buffer.getIntLE(offset + 51);
if (assetIdOffset < 0) { if (assetIdOffset < 0) {
return ValidationResult.error("Invalid offset for AssetId"); return ValidationResult.error("Invalid offset for AssetId");
} }
int pos = offset + 91 + assetIdOffset; int pos = offset + 99 + assetIdOffset;
if (pos >= buffer.writerIndex()) { if (pos >= buffer.writerIndex()) {
return ValidationResult.error("Offset out of bounds for AssetId"); return ValidationResult.error("Offset out of bounds for AssetId");
} }
@@ -889,12 +901,12 @@ public class Model {
} }
if ((nullBits[0] & 8) != 0) { if ((nullBits[0] & 8) != 0) {
int pathOffset = buffer.getIntLE(offset + 47); int pathOffset = buffer.getIntLE(offset + 55);
if (pathOffset < 0) { if (pathOffset < 0) {
return ValidationResult.error("Invalid offset for Path"); return ValidationResult.error("Invalid offset for Path");
} }
int posx = offset + 91 + pathOffset; int posx = offset + 99 + pathOffset;
if (posx >= buffer.writerIndex()) { if (posx >= buffer.writerIndex()) {
return ValidationResult.error("Offset out of bounds for Path"); return ValidationResult.error("Offset out of bounds for Path");
} }
@@ -916,12 +928,12 @@ public class Model {
} }
if ((nullBits[0] & 16) != 0) { if ((nullBits[0] & 16) != 0) {
int textureOffset = buffer.getIntLE(offset + 51); int textureOffset = buffer.getIntLE(offset + 59);
if (textureOffset < 0) { if (textureOffset < 0) {
return ValidationResult.error("Invalid offset for Texture"); return ValidationResult.error("Invalid offset for Texture");
} }
int posxx = offset + 91 + textureOffset; int posxx = offset + 99 + textureOffset;
if (posxx >= buffer.writerIndex()) { if (posxx >= buffer.writerIndex()) {
return ValidationResult.error("Offset out of bounds for Texture"); return ValidationResult.error("Offset out of bounds for Texture");
} }
@@ -943,12 +955,12 @@ public class Model {
} }
if ((nullBits[0] & 32) != 0) { if ((nullBits[0] & 32) != 0) {
int gradientSetOffset = buffer.getIntLE(offset + 55); int gradientSetOffset = buffer.getIntLE(offset + 63);
if (gradientSetOffset < 0) { if (gradientSetOffset < 0) {
return ValidationResult.error("Invalid offset for GradientSet"); return ValidationResult.error("Invalid offset for GradientSet");
} }
int posxxx = offset + 91 + gradientSetOffset; int posxxx = offset + 99 + gradientSetOffset;
if (posxxx >= buffer.writerIndex()) { if (posxxx >= buffer.writerIndex()) {
return ValidationResult.error("Offset out of bounds for GradientSet"); return ValidationResult.error("Offset out of bounds for GradientSet");
} }
@@ -970,12 +982,12 @@ public class Model {
} }
if ((nullBits[0] & 64) != 0) { if ((nullBits[0] & 64) != 0) {
int gradientIdOffset = buffer.getIntLE(offset + 59); int gradientIdOffset = buffer.getIntLE(offset + 67);
if (gradientIdOffset < 0) { if (gradientIdOffset < 0) {
return ValidationResult.error("Invalid offset for GradientId"); return ValidationResult.error("Invalid offset for GradientId");
} }
int posxxxx = offset + 91 + gradientIdOffset; int posxxxx = offset + 99 + gradientIdOffset;
if (posxxxx >= buffer.writerIndex()) { if (posxxxx >= buffer.writerIndex()) {
return ValidationResult.error("Offset out of bounds for GradientId"); return ValidationResult.error("Offset out of bounds for GradientId");
} }
@@ -997,12 +1009,12 @@ public class Model {
} }
if ((nullBits[0] & 128) != 0) { if ((nullBits[0] & 128) != 0) {
int cameraOffset = buffer.getIntLE(offset + 63); int cameraOffset = buffer.getIntLE(offset + 71);
if (cameraOffset < 0) { if (cameraOffset < 0) {
return ValidationResult.error("Invalid offset for Camera"); return ValidationResult.error("Invalid offset for Camera");
} }
int posxxxxx = offset + 91 + cameraOffset; int posxxxxx = offset + 99 + cameraOffset;
if (posxxxxx >= buffer.writerIndex()) { if (posxxxxx >= buffer.writerIndex()) {
return ValidationResult.error("Offset out of bounds for Camera"); return ValidationResult.error("Offset out of bounds for Camera");
} }
@@ -1016,12 +1028,12 @@ public class Model {
} }
if ((nullBits[1] & 1) != 0) { if ((nullBits[1] & 1) != 0) {
int animationSetsOffset = buffer.getIntLE(offset + 67); int animationSetsOffset = buffer.getIntLE(offset + 75);
if (animationSetsOffset < 0) { if (animationSetsOffset < 0) {
return ValidationResult.error("Invalid offset for AnimationSets"); return ValidationResult.error("Invalid offset for AnimationSets");
} }
int posxxxxxx = offset + 91 + animationSetsOffset; int posxxxxxx = offset + 99 + animationSetsOffset;
if (posxxxxxx >= buffer.writerIndex()) { if (posxxxxxx >= buffer.writerIndex()) {
return ValidationResult.error("Offset out of bounds for AnimationSets"); return ValidationResult.error("Offset out of bounds for AnimationSets");
} }
@@ -1058,12 +1070,12 @@ public class Model {
} }
if ((nullBits[1] & 2) != 0) { if ((nullBits[1] & 2) != 0) {
int attachmentsOffset = buffer.getIntLE(offset + 71); int attachmentsOffset = buffer.getIntLE(offset + 79);
if (attachmentsOffset < 0) { if (attachmentsOffset < 0) {
return ValidationResult.error("Invalid offset for Attachments"); return ValidationResult.error("Invalid offset for Attachments");
} }
int posxxxxxxx = offset + 91 + attachmentsOffset; int posxxxxxxx = offset + 99 + attachmentsOffset;
if (posxxxxxxx >= buffer.writerIndex()) { if (posxxxxxxx >= buffer.writerIndex()) {
return ValidationResult.error("Offset out of bounds for Attachments"); return ValidationResult.error("Offset out of bounds for Attachments");
} }
@@ -1090,12 +1102,12 @@ public class Model {
} }
if ((nullBits[1] & 4) != 0) { if ((nullBits[1] & 4) != 0) {
int particlesOffset = buffer.getIntLE(offset + 75); int particlesOffset = buffer.getIntLE(offset + 83);
if (particlesOffset < 0) { if (particlesOffset < 0) {
return ValidationResult.error("Invalid offset for Particles"); return ValidationResult.error("Invalid offset for Particles");
} }
int posxxxxxxxx = offset + 91 + particlesOffset; int posxxxxxxxx = offset + 99 + particlesOffset;
if (posxxxxxxxx >= buffer.writerIndex()) { if (posxxxxxxxx >= buffer.writerIndex()) {
return ValidationResult.error("Offset out of bounds for Particles"); return ValidationResult.error("Offset out of bounds for Particles");
} }
@@ -1122,12 +1134,12 @@ public class Model {
} }
if ((nullBits[1] & 8) != 0) { if ((nullBits[1] & 8) != 0) {
int trailsOffset = buffer.getIntLE(offset + 79); int trailsOffset = buffer.getIntLE(offset + 87);
if (trailsOffset < 0) { if (trailsOffset < 0) {
return ValidationResult.error("Invalid offset for Trails"); return ValidationResult.error("Invalid offset for Trails");
} }
int posxxxxxxxxx = offset + 91 + trailsOffset; int posxxxxxxxxx = offset + 99 + trailsOffset;
if (posxxxxxxxxx >= buffer.writerIndex()) { if (posxxxxxxxxx >= buffer.writerIndex()) {
return ValidationResult.error("Offset out of bounds for Trails"); return ValidationResult.error("Offset out of bounds for Trails");
} }
@@ -1154,12 +1166,12 @@ public class Model {
} }
if ((nullBits[1] & 16) != 0) { if ((nullBits[1] & 16) != 0) {
int detailBoxesOffset = buffer.getIntLE(offset + 83); int detailBoxesOffset = buffer.getIntLE(offset + 91);
if (detailBoxesOffset < 0) { if (detailBoxesOffset < 0) {
return ValidationResult.error("Invalid offset for DetailBoxes"); return ValidationResult.error("Invalid offset for DetailBoxes");
} }
int posxxxxxxxxxx = offset + 91 + detailBoxesOffset; int posxxxxxxxxxx = offset + 99 + detailBoxesOffset;
if (posxxxxxxxxxx >= buffer.writerIndex()) { if (posxxxxxxxxxx >= buffer.writerIndex()) {
return ValidationResult.error("Offset out of bounds for DetailBoxes"); return ValidationResult.error("Offset out of bounds for DetailBoxes");
} }
@@ -1205,12 +1217,12 @@ public class Model {
} }
if ((nullBits[1] & 32) != 0) { if ((nullBits[1] & 32) != 0) {
int phobiaModelOffset = buffer.getIntLE(offset + 87); int phobiaModelOffset = buffer.getIntLE(offset + 95);
if (phobiaModelOffset < 0) { if (phobiaModelOffset < 0) {
return ValidationResult.error("Invalid offset for PhobiaModel"); return ValidationResult.error("Invalid offset for PhobiaModel");
} }
int posxxxxxxxxxxx = offset + 91 + phobiaModelOffset; int posxxxxxxxxxxx = offset + 99 + phobiaModelOffset;
if (posxxxxxxxxxxx >= buffer.writerIndex()) { if (posxxxxxxxxxxx >= buffer.writerIndex()) {
return ValidationResult.error("Offset out of bounds for PhobiaModel"); return ValidationResult.error("Offset out of bounds for PhobiaModel");
} }
@@ -1238,6 +1250,8 @@ public class Model {
copy.scale = this.scale; copy.scale = this.scale;
copy.eyeHeight = this.eyeHeight; copy.eyeHeight = this.eyeHeight;
copy.crouchOffset = this.crouchOffset; copy.crouchOffset = this.crouchOffset;
copy.sittingOffset = this.sittingOffset;
copy.sleepingOffset = this.sleepingOffset;
if (this.animationSets != null) { if (this.animationSets != null) {
Map<String, AnimationSet> m = new HashMap<>(); Map<String, AnimationSet> m = new HashMap<>();
@@ -1284,6 +1298,8 @@ public class Model {
&& this.scale == other.scale && this.scale == other.scale
&& this.eyeHeight == other.eyeHeight && this.eyeHeight == other.eyeHeight
&& this.crouchOffset == other.crouchOffset && this.crouchOffset == other.crouchOffset
&& this.sittingOffset == other.sittingOffset
&& this.sleepingOffset == other.sleepingOffset
&& Objects.equals(this.animationSets, other.animationSets) && Objects.equals(this.animationSets, other.animationSets)
&& Arrays.equals((Object[])this.attachments, (Object[])other.attachments) && Arrays.equals((Object[])this.attachments, (Object[])other.attachments)
&& Objects.equals(this.hitbox, other.hitbox) && Objects.equals(this.hitbox, other.hitbox)
@@ -1308,6 +1324,8 @@ public class Model {
result = 31 * result + Float.hashCode(this.scale); result = 31 * result + Float.hashCode(this.scale);
result = 31 * result + Float.hashCode(this.eyeHeight); result = 31 * result + Float.hashCode(this.eyeHeight);
result = 31 * result + Float.hashCode(this.crouchOffset); result = 31 * result + Float.hashCode(this.crouchOffset);
result = 31 * result + Float.hashCode(this.sittingOffset);
result = 31 * result + Float.hashCode(this.sleepingOffset);
result = 31 * result + Objects.hashCode(this.animationSets); result = 31 * result + Objects.hashCode(this.animationSets);
result = 31 * result + Arrays.hashCode((Object[])this.attachments); result = 31 * result + Arrays.hashCode((Object[])this.attachments);
result = 31 * result + Objects.hashCode(this.hitbox); result = 31 * result + Objects.hashCode(this.hitbox);

View File

@@ -0,0 +1,122 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.ValidationResult;
import io.netty.buffer.ByteBuf;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class ModelUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 1;
public static final int FIXED_BLOCK_SIZE = 5;
public static final int VARIABLE_FIELD_COUNT = 1;
public static final int VARIABLE_BLOCK_START = 5;
public static final int MAX_SIZE = 1677721600;
@Nullable
public Model model;
public float entityScale;
public ModelUpdate() {
}
public ModelUpdate(@Nullable Model model, float entityScale) {
this.model = model;
this.entityScale = entityScale;
}
public ModelUpdate(@Nonnull ModelUpdate other) {
this.model = other.model;
this.entityScale = other.entityScale;
}
@Nonnull
public static ModelUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
ModelUpdate obj = new ModelUpdate();
byte nullBits = buf.getByte(offset);
obj.entityScale = buf.getFloatLE(offset + 1);
int pos = offset + 5;
if ((nullBits & 1) != 0) {
obj.model = Model.deserialize(buf, pos);
pos += Model.computeBytesConsumed(buf, pos);
}
return obj;
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
byte nullBits = buf.getByte(offset);
int pos = offset + 5;
if ((nullBits & 1) != 0) {
pos += Model.computeBytesConsumed(buf, pos);
}
return pos - offset;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
byte nullBits = 0;
if (this.model != null) {
nullBits = (byte)(nullBits | 1);
}
buf.writeByte(nullBits);
buf.writeFloatLE(this.entityScale);
if (this.model != null) {
this.model.serialize(buf);
}
return buf.writerIndex() - startPos;
}
@Override
public int computeSize() {
int size = 5;
if (this.model != null) {
size += this.model.computeSize();
}
return size;
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
if (buffer.readableBytes() - offset < 5) {
return ValidationResult.error("Buffer too small: expected at least 5 bytes");
} else {
byte nullBits = buffer.getByte(offset);
int pos = offset + 5;
if ((nullBits & 1) != 0) {
ValidationResult modelResult = Model.validateStructure(buffer, pos);
if (!modelResult.isValid()) {
return ValidationResult.error("Invalid Model: " + modelResult.error());
}
pos += Model.computeBytesConsumed(buffer, pos);
}
return ValidationResult.OK;
}
}
public ModelUpdate clone() {
ModelUpdate copy = new ModelUpdate();
copy.model = this.model != null ? this.model.clone() : null;
copy.entityScale = this.entityScale;
return copy;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else {
return !(obj instanceof ModelUpdate other) ? false : Objects.equals(this.model, other.model) && this.entityScale == other.entityScale;
}
}
@Override
public int hashCode() {
return Objects.hash(this.model, this.entityScale);
}
}

View File

@@ -6,7 +6,7 @@ import java.util.Objects;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public class MountedUpdate { public class MountedUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 1; public static final int NULLABLE_BIT_FIELD_SIZE = 1;
public static final int FIXED_BLOCK_SIZE = 48; public static final int FIXED_BLOCK_SIZE = 48;
public static final int VARIABLE_FIELD_COUNT = 0; public static final int VARIABLE_FIELD_COUNT = 0;
@@ -58,7 +58,9 @@ public class MountedUpdate {
return 48; return 48;
} }
public void serialize(@Nonnull ByteBuf buf) { @Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
byte nullBits = 0; byte nullBits = 0;
if (this.attachmentOffset != null) { if (this.attachmentOffset != null) {
nullBits = (byte)(nullBits | 1); nullBits = (byte)(nullBits | 1);
@@ -82,8 +84,11 @@ public class MountedUpdate {
} else { } else {
buf.writeZero(30); buf.writeZero(30);
} }
return buf.writerIndex() - startPos;
} }
@Override
public int computeSize() { public int computeSize() {
return 48; return 48;
} }

View File

@@ -0,0 +1,74 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.ValidationResult;
import io.netty.buffer.ByteBuf;
import java.util.Objects;
import javax.annotation.Nonnull;
public class MovementStatesUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 0;
public static final int FIXED_BLOCK_SIZE = 22;
public static final int VARIABLE_FIELD_COUNT = 0;
public static final int VARIABLE_BLOCK_START = 22;
public static final int MAX_SIZE = 22;
@Nonnull
public MovementStates movementStates = new MovementStates();
public MovementStatesUpdate() {
}
public MovementStatesUpdate(@Nonnull MovementStates movementStates) {
this.movementStates = movementStates;
}
public MovementStatesUpdate(@Nonnull MovementStatesUpdate other) {
this.movementStates = other.movementStates;
}
@Nonnull
public static MovementStatesUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
MovementStatesUpdate obj = new MovementStatesUpdate();
obj.movementStates = MovementStates.deserialize(buf, offset + 0);
return obj;
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
return 22;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
this.movementStates.serialize(buf);
return buf.writerIndex() - startPos;
}
@Override
public int computeSize() {
return 22;
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
return buffer.readableBytes() - offset < 22 ? ValidationResult.error("Buffer too small: expected at least 22 bytes") : ValidationResult.OK;
}
public MovementStatesUpdate clone() {
MovementStatesUpdate copy = new MovementStatesUpdate();
copy.movementStates = this.movementStates.clone();
return copy;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else {
return obj instanceof MovementStatesUpdate other ? Objects.equals(this.movementStates, other.movementStates) : false;
}
}
@Override
public int hashCode() {
return Objects.hash(this.movementStates);
}
}

View File

@@ -0,0 +1,105 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.PacketIO;
import com.hypixel.hytale.protocol.io.ProtocolException;
import com.hypixel.hytale.protocol.io.ValidationResult;
import com.hypixel.hytale.protocol.io.VarInt;
import io.netty.buffer.ByteBuf;
import java.util.Objects;
import javax.annotation.Nonnull;
public class NameplateUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 0;
public static final int FIXED_BLOCK_SIZE = 0;
public static final int VARIABLE_FIELD_COUNT = 1;
public static final int VARIABLE_BLOCK_START = 0;
public static final int MAX_SIZE = 16384005;
@Nonnull
public String text = "";
public NameplateUpdate() {
}
public NameplateUpdate(@Nonnull String text) {
this.text = text;
}
public NameplateUpdate(@Nonnull NameplateUpdate other) {
this.text = other.text;
}
@Nonnull
public static NameplateUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
NameplateUpdate obj = new NameplateUpdate();
int pos = offset + 0;
int textLen = VarInt.peek(buf, pos);
if (textLen < 0) {
throw ProtocolException.negativeLength("Text", textLen);
} else if (textLen > 4096000) {
throw ProtocolException.stringTooLong("Text", textLen, 4096000);
} else {
int textVarLen = VarInt.length(buf, pos);
obj.text = PacketIO.readVarString(buf, pos, PacketIO.UTF8);
pos += textVarLen + textLen;
return obj;
}
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
int pos = offset + 0;
int sl = VarInt.peek(buf, pos);
pos += VarInt.length(buf, pos) + sl;
return pos - offset;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
PacketIO.writeVarString(buf, this.text, 4096000);
return buf.writerIndex() - startPos;
}
@Override
public int computeSize() {
int size = 0;
return size + PacketIO.stringSize(this.text);
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
if (buffer.readableBytes() - offset < 0) {
return ValidationResult.error("Buffer too small: expected at least 0 bytes");
} else {
int pos = offset + 0;
int textLen = VarInt.peek(buffer, pos);
if (textLen < 0) {
return ValidationResult.error("Invalid string length for Text");
} else if (textLen > 4096000) {
return ValidationResult.error("Text exceeds max length 4096000");
} else {
pos += VarInt.length(buffer, pos);
pos += textLen;
return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading Text") : ValidationResult.OK;
}
}
}
public NameplateUpdate clone() {
NameplateUpdate copy = new NameplateUpdate();
copy.text = this.text;
return copy;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else {
return obj instanceof NameplateUpdate other ? Objects.equals(this.text, other.text) : false;
}
}
@Override
public int hashCode() {
return Objects.hash(this.text);
}
}

View File

@@ -0,0 +1,27 @@
package com.hypixel.hytale.protocol;
public enum NetworkChannel {
Default(0),
Chunks(1),
WorldMap(2);
public static final NetworkChannel[] VALUES = values();
public static final int COUNT = VALUES.length;
private final int value;
private NetworkChannel(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
public static NetworkChannel fromValue(int value) {
if (value >= 0 && value < VALUES.length) {
return VALUES[value];
} else {
throw new IllegalArgumentException("Invalid network channel: " + value);
}
}
}

View File

@@ -0,0 +1,51 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.ValidationResult;
import io.netty.buffer.ByteBuf;
import javax.annotation.Nonnull;
public class NewSpawnUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 0;
public static final int FIXED_BLOCK_SIZE = 0;
public static final int VARIABLE_FIELD_COUNT = 0;
public static final int VARIABLE_BLOCK_START = 0;
public static final int MAX_SIZE = 0;
@Nonnull
public static NewSpawnUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
return new NewSpawnUpdate();
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
return 0;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
return buf.writerIndex() - startPos;
}
@Override
public int computeSize() {
return 0;
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
return buffer.readableBytes() - offset < 0 ? ValidationResult.error("Buffer too small: expected at least 0 bytes") : ValidationResult.OK;
}
public NewSpawnUpdate clone() {
return new NewSpawnUpdate();
}
@Override
public boolean equals(Object obj) {
return this == obj ? true : obj instanceof NewSpawnUpdate other;
}
@Override
public int hashCode() {
return 0;
}
}

View File

@@ -6,6 +6,8 @@ import javax.annotation.Nonnull;
public interface Packet { public interface Packet {
int getId(); int getId();
NetworkChannel getChannel();
void serialize(@Nonnull ByteBuf var1); void serialize(@Nonnull ByteBuf var1);
int computeSize(); int computeSize();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,116 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.ValidationResult;
import io.netty.buffer.ByteBuf;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PlayerSkinUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 1;
public static final int FIXED_BLOCK_SIZE = 1;
public static final int VARIABLE_FIELD_COUNT = 1;
public static final int VARIABLE_BLOCK_START = 1;
public static final int MAX_SIZE = 327680184;
@Nullable
public PlayerSkin skin;
public PlayerSkinUpdate() {
}
public PlayerSkinUpdate(@Nullable PlayerSkin skin) {
this.skin = skin;
}
public PlayerSkinUpdate(@Nonnull PlayerSkinUpdate other) {
this.skin = other.skin;
}
@Nonnull
public static PlayerSkinUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
PlayerSkinUpdate obj = new PlayerSkinUpdate();
byte nullBits = buf.getByte(offset);
int pos = offset + 1;
if ((nullBits & 1) != 0) {
obj.skin = PlayerSkin.deserialize(buf, pos);
pos += PlayerSkin.computeBytesConsumed(buf, pos);
}
return obj;
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
byte nullBits = buf.getByte(offset);
int pos = offset + 1;
if ((nullBits & 1) != 0) {
pos += PlayerSkin.computeBytesConsumed(buf, pos);
}
return pos - offset;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
byte nullBits = 0;
if (this.skin != null) {
nullBits = (byte)(nullBits | 1);
}
buf.writeByte(nullBits);
if (this.skin != null) {
this.skin.serialize(buf);
}
return buf.writerIndex() - startPos;
}
@Override
public int computeSize() {
int size = 1;
if (this.skin != null) {
size += this.skin.computeSize();
}
return size;
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
if (buffer.readableBytes() - offset < 1) {
return ValidationResult.error("Buffer too small: expected at least 1 bytes");
} else {
byte nullBits = buffer.getByte(offset);
int pos = offset + 1;
if ((nullBits & 1) != 0) {
ValidationResult skinResult = PlayerSkin.validateStructure(buffer, pos);
if (!skinResult.isValid()) {
return ValidationResult.error("Invalid Skin: " + skinResult.error());
}
pos += PlayerSkin.computeBytesConsumed(buffer, pos);
}
return ValidationResult.OK;
}
}
public PlayerSkinUpdate clone() {
PlayerSkinUpdate copy = new PlayerSkinUpdate();
copy.skin = this.skin != null ? this.skin.clone() : null;
return copy;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else {
return obj instanceof PlayerSkinUpdate other ? Objects.equals(this.skin, other.skin) : false;
}
}
@Override
public int hashCode() {
return Objects.hash(this.skin);
}
}

View File

@@ -0,0 +1,76 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.PacketIO;
import com.hypixel.hytale.protocol.io.ValidationResult;
import io.netty.buffer.ByteBuf;
import java.util.Objects;
import java.util.UUID;
import javax.annotation.Nonnull;
public class PredictionUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 0;
public static final int FIXED_BLOCK_SIZE = 16;
public static final int VARIABLE_FIELD_COUNT = 0;
public static final int VARIABLE_BLOCK_START = 16;
public static final int MAX_SIZE = 16;
@Nonnull
public UUID predictionId = new UUID(0L, 0L);
public PredictionUpdate() {
}
public PredictionUpdate(@Nonnull UUID predictionId) {
this.predictionId = predictionId;
}
public PredictionUpdate(@Nonnull PredictionUpdate other) {
this.predictionId = other.predictionId;
}
@Nonnull
public static PredictionUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
PredictionUpdate obj = new PredictionUpdate();
obj.predictionId = PacketIO.readUUID(buf, offset + 0);
return obj;
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
return 16;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
PacketIO.writeUUID(buf, this.predictionId);
return buf.writerIndex() - startPos;
}
@Override
public int computeSize() {
return 16;
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
return buffer.readableBytes() - offset < 16 ? ValidationResult.error("Buffer too small: expected at least 16 bytes") : ValidationResult.OK;
}
public PredictionUpdate clone() {
PredictionUpdate copy = new PredictionUpdate();
copy.predictionId = this.predictionId;
return copy;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else {
return obj instanceof PredictionUpdate other ? Objects.equals(this.predictionId, other.predictionId) : false;
}
}
@Override
public int hashCode() {
return Objects.hash(this.predictionId);
}
}

View File

@@ -0,0 +1,51 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.ValidationResult;
import io.netty.buffer.ByteBuf;
import javax.annotation.Nonnull;
public class PropUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 0;
public static final int FIXED_BLOCK_SIZE = 0;
public static final int VARIABLE_FIELD_COUNT = 0;
public static final int VARIABLE_BLOCK_START = 0;
public static final int MAX_SIZE = 0;
@Nonnull
public static PropUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
return new PropUpdate();
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
return 0;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
return buf.writerIndex() - startPos;
}
@Override
public int computeSize() {
return 0;
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
return buffer.readableBytes() - offset < 0 ? ValidationResult.error("Buffer too small: expected at least 0 bytes") : ValidationResult.OK;
}
public PropUpdate clone() {
return new PropUpdate();
}
@Override
public boolean equals(Object obj) {
return this == obj ? true : obj instanceof PropUpdate other;
}
@Override
public int hashCode() {
return 0;
}
}

View File

@@ -1,11 +1,11 @@
package com.hypixel.hytale.protocol; package com.hypixel.hytale.protocol;
public final class ProtocolSettings { public final class ProtocolSettings {
public static final int PROTOCOL_CRC = 672031543; public static final int PROTOCOL_CRC = -1356075132;
public static final int PROTOCOL_VERSION = 2; public static final int PROTOCOL_VERSION = 2;
public static final int PROTOCOL_BUILD_NUMBER = 12; public static final int PROTOCOL_BUILD_NUMBER = 20;
public static final int PACKET_COUNT = 270; public static final int PACKET_COUNT = 268;
public static final int STRUCT_COUNT = 318; public static final int STRUCT_COUNT = 339;
public static final int ENUM_COUNT = 137; public static final int ENUM_COUNT = 137;
public static final int MAX_PACKET_SIZE = 1677721600; public static final int MAX_PACKET_SIZE = 1677721600;
@@ -13,6 +13,6 @@ public final class ProtocolSettings {
} }
public static boolean validateCrc(int crc) { public static boolean validateCrc(int crc) {
return 672031543 == crc; return -1356075132 == crc;
} }
} }

View File

@@ -0,0 +1,73 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.ValidationResult;
import io.netty.buffer.ByteBuf;
import java.util.Objects;
import javax.annotation.Nonnull;
public class RepulsionUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 0;
public static final int FIXED_BLOCK_SIZE = 4;
public static final int VARIABLE_FIELD_COUNT = 0;
public static final int VARIABLE_BLOCK_START = 4;
public static final int MAX_SIZE = 4;
public int repulsionConfigIndex;
public RepulsionUpdate() {
}
public RepulsionUpdate(int repulsionConfigIndex) {
this.repulsionConfigIndex = repulsionConfigIndex;
}
public RepulsionUpdate(@Nonnull RepulsionUpdate other) {
this.repulsionConfigIndex = other.repulsionConfigIndex;
}
@Nonnull
public static RepulsionUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
RepulsionUpdate obj = new RepulsionUpdate();
obj.repulsionConfigIndex = buf.getIntLE(offset + 0);
return obj;
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
return 4;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
buf.writeIntLE(this.repulsionConfigIndex);
return buf.writerIndex() - startPos;
}
@Override
public int computeSize() {
return 4;
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
return buffer.readableBytes() - offset < 4 ? ValidationResult.error("Buffer too small: expected at least 4 bytes") : ValidationResult.OK;
}
public RepulsionUpdate clone() {
RepulsionUpdate copy = new RepulsionUpdate();
copy.repulsionConfigIndex = this.repulsionConfigIndex;
return copy;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else {
return obj instanceof RepulsionUpdate other ? this.repulsionConfigIndex == other.repulsionConfigIndex : false;
}
}
@Override
public int hashCode() {
return Objects.hash(this.repulsionConfigIndex);
}
}

View File

@@ -0,0 +1,51 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.ValidationResult;
import io.netty.buffer.ByteBuf;
import javax.annotation.Nonnull;
public class RespondToHitUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 0;
public static final int FIXED_BLOCK_SIZE = 0;
public static final int VARIABLE_FIELD_COUNT = 0;
public static final int VARIABLE_BLOCK_START = 0;
public static final int MAX_SIZE = 0;
@Nonnull
public static RespondToHitUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
return new RespondToHitUpdate();
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
return 0;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
return buf.writerIndex() - startPos;
}
@Override
public int computeSize() {
return 0;
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
return buffer.readableBytes() - offset < 0 ? ValidationResult.error("Buffer too small: expected at least 0 bytes") : ValidationResult.OK;
}
public RespondToHitUpdate clone() {
return new RespondToHitUpdate();
}
@Override
public boolean equals(Object obj) {
return this == obj ? true : obj instanceof RespondToHitUpdate other;
}
@Override
public int hashCode() {
return 0;
}
}

View File

@@ -0,0 +1,4 @@
package com.hypixel.hytale.protocol;
public interface ToClientPacket extends Packet {
}

View File

@@ -0,0 +1,4 @@
package com.hypixel.hytale.protocol;
public interface ToServerPacket extends Packet {
}

View File

@@ -0,0 +1,74 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.ValidationResult;
import io.netty.buffer.ByteBuf;
import java.util.Objects;
import javax.annotation.Nonnull;
public class TransformUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 0;
public static final int FIXED_BLOCK_SIZE = 49;
public static final int VARIABLE_FIELD_COUNT = 0;
public static final int VARIABLE_BLOCK_START = 49;
public static final int MAX_SIZE = 49;
@Nonnull
public ModelTransform transform = new ModelTransform();
public TransformUpdate() {
}
public TransformUpdate(@Nonnull ModelTransform transform) {
this.transform = transform;
}
public TransformUpdate(@Nonnull TransformUpdate other) {
this.transform = other.transform;
}
@Nonnull
public static TransformUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
TransformUpdate obj = new TransformUpdate();
obj.transform = ModelTransform.deserialize(buf, offset + 0);
return obj;
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
return 49;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
this.transform.serialize(buf);
return buf.writerIndex() - startPos;
}
@Override
public int computeSize() {
return 49;
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
return buffer.readableBytes() - offset < 49 ? ValidationResult.error("Buffer too small: expected at least 49 bytes") : ValidationResult.OK;
}
public TransformUpdate clone() {
TransformUpdate copy = new TransformUpdate();
copy.transform = this.transform.clone();
return copy;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else {
return obj instanceof TransformUpdate other ? Objects.equals(this.transform, other.transform) : false;
}
}
@Override
public int hashCode() {
return Objects.hash(this.transform);
}
}

View File

@@ -0,0 +1,124 @@
package com.hypixel.hytale.protocol;
import com.hypixel.hytale.protocol.io.ProtocolException;
import com.hypixel.hytale.protocol.io.ValidationResult;
import com.hypixel.hytale.protocol.io.VarInt;
import io.netty.buffer.ByteBuf;
import java.util.Arrays;
import javax.annotation.Nonnull;
public class UIComponentsUpdate extends ComponentUpdate {
public static final int NULLABLE_BIT_FIELD_SIZE = 0;
public static final int FIXED_BLOCK_SIZE = 0;
public static final int VARIABLE_FIELD_COUNT = 1;
public static final int VARIABLE_BLOCK_START = 0;
public static final int MAX_SIZE = 16384005;
@Nonnull
public int[] components = new int[0];
public UIComponentsUpdate() {
}
public UIComponentsUpdate(@Nonnull int[] components) {
this.components = components;
}
public UIComponentsUpdate(@Nonnull UIComponentsUpdate other) {
this.components = other.components;
}
@Nonnull
public static UIComponentsUpdate deserialize(@Nonnull ByteBuf buf, int offset) {
UIComponentsUpdate obj = new UIComponentsUpdate();
int pos = offset + 0;
int componentsCount = VarInt.peek(buf, pos);
if (componentsCount < 0) {
throw ProtocolException.negativeLength("Components", componentsCount);
} else if (componentsCount > 4096000) {
throw ProtocolException.arrayTooLong("Components", componentsCount, 4096000);
} else {
int componentsVarLen = VarInt.size(componentsCount);
if (pos + componentsVarLen + componentsCount * 4L > buf.readableBytes()) {
throw ProtocolException.bufferTooSmall("Components", pos + componentsVarLen + componentsCount * 4, buf.readableBytes());
} else {
pos += componentsVarLen;
obj.components = new int[componentsCount];
for (int i = 0; i < componentsCount; i++) {
obj.components[i] = buf.getIntLE(pos + i * 4);
}
pos += componentsCount * 4;
return obj;
}
}
}
public static int computeBytesConsumed(@Nonnull ByteBuf buf, int offset) {
int pos = offset + 0;
int arrLen = VarInt.peek(buf, pos);
pos += VarInt.length(buf, pos) + arrLen * 4;
return pos - offset;
}
@Override
public int serialize(@Nonnull ByteBuf buf) {
int startPos = buf.writerIndex();
if (this.components.length > 4096000) {
throw ProtocolException.arrayTooLong("Components", this.components.length, 4096000);
} else {
VarInt.write(buf, this.components.length);
for (int item : this.components) {
buf.writeIntLE(item);
}
return buf.writerIndex() - startPos;
}
}
@Override
public int computeSize() {
int size = 0;
return size + VarInt.size(this.components.length) + this.components.length * 4;
}
public static ValidationResult validateStructure(@Nonnull ByteBuf buffer, int offset) {
if (buffer.readableBytes() - offset < 0) {
return ValidationResult.error("Buffer too small: expected at least 0 bytes");
} else {
int pos = offset + 0;
int componentsCount = VarInt.peek(buffer, pos);
if (componentsCount < 0) {
return ValidationResult.error("Invalid array count for Components");
} else if (componentsCount > 4096000) {
return ValidationResult.error("Components exceeds max length 4096000");
} else {
pos += VarInt.length(buffer, pos);
pos += componentsCount * 4;
return pos > buffer.writerIndex() ? ValidationResult.error("Buffer overflow reading Components") : ValidationResult.OK;
}
}
}
public UIComponentsUpdate clone() {
UIComponentsUpdate copy = new UIComponentsUpdate();
copy.components = Arrays.copyOf(this.components, this.components.length);
return copy;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else {
return obj instanceof UIComponentsUpdate other ? Arrays.equals(this.components, other.components) : false;
}
}
@Override
public int hashCode() {
int result = 1;
return 31 * result + Arrays.hashCode(this.components);
}
}

View File

@@ -305,7 +305,7 @@ public final class PacketIO {
if (id == null) { if (id == null) {
throw new ProtocolException("Unknown packet type: " + packetClass.getName()); throw new ProtocolException("Unknown packet type: " + packetClass.getName());
} else { } else {
PacketRegistry.PacketInfo info = PacketRegistry.getById(id); PacketRegistry.PacketInfo info = PacketRegistry.getToClientPacketById(id);
int lengthIndex = out.writerIndex(); int lengthIndex = out.writerIndex();
out.writeIntLE(0); out.writeIntLE(0);
out.writeIntLE(id); out.writeIntLE(id);
@@ -351,7 +351,7 @@ public final class PacketIO {
@Nonnull @Nonnull
public static Packet readFramedPacket(@Nonnull ByteBuf in, int payloadLength, @Nonnull PacketStatsRecorder statsRecorder) { public static Packet readFramedPacket(@Nonnull ByteBuf in, int payloadLength, @Nonnull PacketStatsRecorder statsRecorder) {
int packetId = in.readIntLE(); int packetId = in.readIntLE();
PacketRegistry.PacketInfo info = PacketRegistry.getById(packetId); PacketRegistry.PacketInfo info = PacketRegistry.getToServerPacketById(packetId);
if (info == null) { if (info == null) {
in.skipBytes(payloadLength); in.skipBytes(payloadLength);
throw new ProtocolException("Unknown packet ID: " + packetId); throw new ProtocolException("Unknown packet ID: " + packetId);

View File

@@ -1,5 +1,6 @@
package com.hypixel.hytale.protocol.io.netty; package com.hypixel.hytale.protocol.io.netty;
import com.hypixel.hytale.protocol.NetworkChannel;
import com.hypixel.hytale.protocol.PacketRegistry; import com.hypixel.hytale.protocol.PacketRegistry;
import com.hypixel.hytale.protocol.io.PacketIO; import com.hypixel.hytale.protocol.io.PacketIO;
import com.hypixel.hytale.protocol.io.PacketStatsRecorder; import com.hypixel.hytale.protocol.io.PacketStatsRecorder;
@@ -80,30 +81,36 @@ public class PacketDecoder extends ByteToMessageDecoder {
int payloadLength = in.readIntLE(); int payloadLength = in.readIntLE();
if (payloadLength >= 0 && payloadLength <= 1677721600) { if (payloadLength >= 0 && payloadLength <= 1677721600) {
int packetId = in.readIntLE(); int packetId = in.readIntLE();
PacketRegistry.PacketInfo packetInfo = PacketRegistry.getById(packetId); PacketRegistry.PacketInfo packetInfo = PacketRegistry.getToServerPacketById(packetId);
if (packetInfo == null) { if (packetInfo == null) {
in.skipBytes(in.readableBytes()); in.skipBytes(in.readableBytes());
ProtocolUtil.closeConnection(ctx.channel()); ProtocolUtil.closeConnection(ctx.channel());
} else if (payloadLength > packetInfo.maxSize()) { } else if (payloadLength > packetInfo.maxSize()) {
in.skipBytes(in.readableBytes()); in.skipBytes(in.readableBytes());
ProtocolUtil.closeConnection(ctx.channel()); ProtocolUtil.closeConnection(ctx.channel());
} else if (in.readableBytes() < payloadLength) {
in.resetReaderIndex();
} else { } else {
PacketStatsRecorder statsRecorder = ctx.channel().attr(PacketStatsRecorder.CHANNEL_KEY).get(); NetworkChannel channelVal = ctx.channel().attr(ProtocolUtil.STREAM_CHANNEL_KEY).get();
if (statsRecorder == null) { if (channelVal != null && channelVal != packetInfo.channel()) {
statsRecorder = PacketStatsRecorder.NOOP; in.skipBytes(in.readableBytes());
} ProtocolUtil.closeConnection(ctx.channel());
} else if (in.readableBytes() < payloadLength) {
in.resetReaderIndex();
} else {
PacketStatsRecorder statsRecorder = ctx.channel().attr(PacketStatsRecorder.CHANNEL_KEY).get();
if (statsRecorder == null) {
statsRecorder = PacketStatsRecorder.NOOP;
}
try { try {
out.add(PacketIO.readFramedPacketWithInfo(in, payloadLength, packetInfo, statsRecorder)); out.add(PacketIO.readFramedPacketWithInfo(in, payloadLength, packetInfo, statsRecorder));
this.lastPacketTimeNanos = System.nanoTime(); this.lastPacketTimeNanos = System.nanoTime();
} catch (ProtocolException var9) { } catch (ProtocolException var10) {
in.skipBytes(in.readableBytes()); in.skipBytes(in.readableBytes());
ProtocolUtil.closeConnection(ctx.channel()); ProtocolUtil.closeConnection(ctx.channel());
} catch (IndexOutOfBoundsException var10) { } catch (IndexOutOfBoundsException var11) {
in.skipBytes(in.readableBytes()); in.skipBytes(in.readableBytes());
ProtocolUtil.closeConnection(ctx.channel()); ProtocolUtil.closeConnection(ctx.channel());
}
} }
} }
} else { } else {

View File

@@ -1,6 +1,7 @@
package com.hypixel.hytale.protocol.io.netty; package com.hypixel.hytale.protocol.io.netty;
import com.hypixel.hytale.protocol.CachedPacket; import com.hypixel.hytale.protocol.CachedPacket;
import com.hypixel.hytale.protocol.NetworkChannel;
import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.Packet;
import com.hypixel.hytale.protocol.io.PacketIO; import com.hypixel.hytale.protocol.io.PacketIO;
import com.hypixel.hytale.protocol.io.PacketStatsRecorder; import com.hypixel.hytale.protocol.io.PacketStatsRecorder;
@@ -20,11 +21,16 @@ public class PacketEncoder extends MessageToByteEncoder<Packet> {
packetClass = (Class<? extends Packet>)packet.getClass(); packetClass = (Class<? extends Packet>)packet.getClass();
} }
PacketStatsRecorder statsRecorder = ctx.channel().attr(PacketStatsRecorder.CHANNEL_KEY).get(); NetworkChannel channelAttr = ctx.channel().attr(ProtocolUtil.STREAM_CHANNEL_KEY).get();
if (statsRecorder == null) { if (channelAttr != null && channelAttr != packet.getChannel()) {
statsRecorder = PacketStatsRecorder.NOOP; throw new IllegalArgumentException("Packet channel " + packet.getChannel() + " does not match stream channel " + channelAttr);
} } else {
PacketStatsRecorder statsRecorder = ctx.channel().attr(PacketStatsRecorder.CHANNEL_KEY).get();
if (statsRecorder == null) {
statsRecorder = PacketStatsRecorder.NOOP;
}
PacketIO.writeFramedPacket(packet, packetClass, out, statsRecorder); PacketIO.writeFramedPacket(packet, packetClass, out, statsRecorder);
}
} }
} }

View File

@@ -1,5 +1,6 @@
package com.hypixel.hytale.protocol.io.netty; package com.hypixel.hytale.protocol.io.netty;
import com.hypixel.hytale.protocol.NetworkChannel;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
@@ -13,6 +14,7 @@ import java.time.Duration;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
public final class ProtocolUtil { public final class ProtocolUtil {
public static final AttributeKey<NetworkChannel> STREAM_CHANNEL_KEY = AttributeKey.newInstance("STREAM_CHANNEL_ID");
public static final AttributeKey<Duration> PACKET_TIMEOUT_KEY = AttributeKey.newInstance("PACKET_TIMEOUT"); public static final AttributeKey<Duration> PACKET_TIMEOUT_KEY = AttributeKey.newInstance("PACKET_TIMEOUT");
public static final int APPLICATION_NO_ERROR = 0; public static final int APPLICATION_NO_ERROR = 0;
public static final int APPLICATION_RATE_LIMITED = 1; public static final int APPLICATION_RATE_LIMITED = 1;

View File

@@ -1,6 +1,8 @@
package com.hypixel.hytale.protocol.packets.asseteditor; package com.hypixel.hytale.protocol.packets.asseteditor;
import com.hypixel.hytale.protocol.NetworkChannel;
import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.Packet;
import com.hypixel.hytale.protocol.ToServerPacket;
import com.hypixel.hytale.protocol.io.PacketIO; import com.hypixel.hytale.protocol.io.PacketIO;
import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ProtocolException;
import com.hypixel.hytale.protocol.io.ValidationResult; import com.hypixel.hytale.protocol.io.ValidationResult;
@@ -10,7 +12,7 @@ import java.util.Objects;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public class AssetEditorActivateButton implements Packet { public class AssetEditorActivateButton implements Packet, ToServerPacket {
public static final int PACKET_ID = 335; public static final int PACKET_ID = 335;
public static final boolean IS_COMPRESSED = false; public static final boolean IS_COMPRESSED = false;
public static final int NULLABLE_BIT_FIELD_SIZE = 1; public static final int NULLABLE_BIT_FIELD_SIZE = 1;
@@ -26,6 +28,11 @@ public class AssetEditorActivateButton implements Packet {
return 335; return 335;
} }
@Override
public NetworkChannel getChannel() {
return NetworkChannel.Default;
}
public AssetEditorActivateButton() { public AssetEditorActivateButton() {
} }

View File

@@ -1,6 +1,8 @@
package com.hypixel.hytale.protocol.packets.asseteditor; package com.hypixel.hytale.protocol.packets.asseteditor;
import com.hypixel.hytale.protocol.NetworkChannel;
import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.Packet;
import com.hypixel.hytale.protocol.ToClientPacket;
import com.hypixel.hytale.protocol.io.PacketIO; import com.hypixel.hytale.protocol.io.PacketIO;
import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ProtocolException;
import com.hypixel.hytale.protocol.io.ValidationResult; import com.hypixel.hytale.protocol.io.ValidationResult;
@@ -11,7 +13,7 @@ import java.util.Objects;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public class AssetEditorAssetListSetup implements Packet { public class AssetEditorAssetListSetup implements Packet, ToClientPacket {
public static final int PACKET_ID = 319; public static final int PACKET_ID = 319;
public static final boolean IS_COMPRESSED = true; public static final boolean IS_COMPRESSED = true;
public static final int NULLABLE_BIT_FIELD_SIZE = 1; public static final int NULLABLE_BIT_FIELD_SIZE = 1;
@@ -33,6 +35,11 @@ public class AssetEditorAssetListSetup implements Packet {
return 319; return 319;
} }
@Override
public NetworkChannel getChannel() {
return NetworkChannel.Default;
}
public AssetEditorAssetListSetup() { public AssetEditorAssetListSetup() {
} }

View File

@@ -1,6 +1,8 @@
package com.hypixel.hytale.protocol.packets.asseteditor; package com.hypixel.hytale.protocol.packets.asseteditor;
import com.hypixel.hytale.protocol.NetworkChannel;
import com.hypixel.hytale.protocol.Packet; import com.hypixel.hytale.protocol.Packet;
import com.hypixel.hytale.protocol.ToClientPacket;
import com.hypixel.hytale.protocol.io.PacketIO; import com.hypixel.hytale.protocol.io.PacketIO;
import com.hypixel.hytale.protocol.io.ProtocolException; import com.hypixel.hytale.protocol.io.ProtocolException;
import com.hypixel.hytale.protocol.io.ValidationResult; import com.hypixel.hytale.protocol.io.ValidationResult;
@@ -11,7 +13,7 @@ import java.util.Objects;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public class AssetEditorAssetListUpdate implements Packet { public class AssetEditorAssetListUpdate implements Packet, ToClientPacket {
public static final int PACKET_ID = 320; public static final int PACKET_ID = 320;
public static final boolean IS_COMPRESSED = true; public static final boolean IS_COMPRESSED = true;
public static final int NULLABLE_BIT_FIELD_SIZE = 1; public static final int NULLABLE_BIT_FIELD_SIZE = 1;
@@ -31,6 +33,11 @@ public class AssetEditorAssetListUpdate implements Packet {
return 320; return 320;
} }
@Override
public NetworkChannel getChannel() {
return NetworkChannel.Default;
}
public AssetEditorAssetListUpdate() { public AssetEditorAssetListUpdate() {
} }

Some files were not shown because too many files have changed in this diff Show More