hytale-server/com/hypixel/hytale/builtin/buildertools/prefabeditor/PrefabEditSession.java

269 lines
11 KiB
Java

package com.hypixel.hytale.builtin.buildertools.prefabeditor;
import com.hypixel.hytale.builtin.buildertools.BuilderToolsPlugin;
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.component.ComponentAccessor;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Resource;
import com.hypixel.hytale.component.ResourceType;
import com.hypixel.hytale.math.codec.Vector3iArrayCodec;
import com.hypixel.hytale.math.vector.Transform;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.protocol.packets.buildertools.BuilderToolHideAnchors;
import com.hypixel.hytale.protocol.packets.worldmap.MapMarker;
import com.hypixel.hytale.server.core.entity.UUIDComponent;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.io.PacketHandler;
import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.core.util.PositionUtil;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import java.nio.file.Path;
import java.util.Map;
import java.util.UUID;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PrefabEditSession implements Resource<EntityStore> {
@Nonnull
public static final BuilderCodec<PrefabEditSession> CODEC = BuilderCodec.builder(PrefabEditSession.class, PrefabEditSession::new)
.append(new KeyedCodec<>("WorldName", Codec.STRING, true), (o, worldName) -> o.worldName = worldName, o -> o.worldName)
.add()
.append(new KeyedCodec<>("WorldArrivedFrom", Codec.UUID_STRING, true), (o, worldName) -> o.worldArrivedFrom = worldName, o -> o.worldArrivedFrom)
.add()
.append(
new KeyedCodec<>("TransformArrivedFrom", Transform.CODEC, true),
(o, positionArrivedFrom) -> o.transformArrivedFrom = positionArrivedFrom,
o -> o.transformArrivedFrom
)
.add()
.append(new KeyedCodec<>("WorldCreatorUUID", Codec.UUID_STRING, true), (o, worldCreatorUuid) -> o.worldCreator = worldCreatorUuid, o -> o.worldCreator)
.add()
.append(new KeyedCodec<>("SpawnPoint", new Vector3iArrayCodec(), true), (o, spawnPoint) -> o.spawnPoint = spawnPoint, o -> o.spawnPoint)
.add()
.append(
new KeyedCodec<>("LoadedPrefabMetadata", new ArrayCodec<>(PrefabEditingMetadata.CODEC, PrefabEditingMetadata[]::new), false),
(editSession, prefabEditingMetadata) -> {
for (PrefabEditingMetadata prefabEditMetadata : prefabEditingMetadata) {
editSession.loadedPrefabMetadata.put(prefabEditMetadata.getUuid(), prefabEditMetadata);
}
},
editSession -> editSession.loadedPrefabMetadata.values().toArray(PrefabEditingMetadata[]::new)
)
.add()
.afterDecode(editSession -> {
PrefabEditSessionManager prefabEditSessionManager = BuilderToolsPlugin.get().getPrefabEditSessionManager();
prefabEditSessionManager.populateActiveEditSession(editSession.getWorldCreator(), editSession);
for (PrefabEditingMetadata value : editSession.loadedPrefabMetadata.values()) {
prefabEditSessionManager.populatePrefabsBeingEdited(value.getPrefabPath());
}
prefabEditSessionManager.scheduleAnchorEntityRecreation(editSession);
})
.build();
private String worldName;
private UUID worldArrivedFrom;
@Nullable
private Transform transformArrivedFrom;
private UUID worldCreator;
@Nonnull
private final Map<UUID, PrefabEditingMetadata> loadedPrefabMetadata = new Object2ObjectOpenHashMap();
@Nonnull
private final Map<UUID, UUID> selectedPrefab = new Object2ObjectOpenHashMap();
@Nonnull
private Vector3i spawnPoint = new Vector3i(0, 0, 0);
@Nonnull
public static ResourceType<EntityStore, PrefabEditSession> getResourceType() {
return BuilderToolsPlugin.get().getPrefabEditSessionResourceType();
}
private PrefabEditSession() {
}
public PrefabEditSession(@Nonnull String worldName, @Nonnull UUID worldCreator, @Nonnull UUID worldArrivedFrom, @Nonnull Transform transformArrivedFrom) {
this.worldName = worldName;
this.worldCreator = worldCreator;
this.worldArrivedFrom = worldArrivedFrom;
this.transformArrivedFrom = transformArrivedFrom;
}
public PrefabEditSession(@Nonnull PrefabEditSession other) {
this.worldName = other.worldName;
this.worldArrivedFrom = other.worldArrivedFrom;
this.transformArrivedFrom = other.transformArrivedFrom;
this.worldCreator = other.worldCreator;
this.spawnPoint = other.spawnPoint;
}
public void addPrefab(
@Nonnull Path prefabPath, @Nonnull Vector3i minPoint, @Nonnull Vector3i maxPoint, @Nonnull Vector3i anchorPoint, @Nonnull Vector3i pastePosition
) {
if (this.loadedPrefabMetadata.isEmpty()) {
this.spawnPoint.assign(maxPoint);
}
PrefabEditingMetadata prefabEditingMetadata = new PrefabEditingMetadata(
prefabPath, minPoint, maxPoint, anchorPoint, pastePosition, Universe.get().getWorld(this.worldName)
);
this.loadedPrefabMetadata.put(prefabEditingMetadata.getUuid(), prefabEditingMetadata);
}
@Nullable
public PrefabEditingMetadata updatePrefabBounds(@Nonnull UUID prefab, @Nonnull Vector3i newMin, @Nonnull Vector3i newMax) {
PrefabEditingMetadata prefabEditingMetadata = this.loadedPrefabMetadata.get(prefab);
if (prefabEditingMetadata == null) {
return null;
} else {
prefabEditingMetadata.setMaxPoint(newMax);
prefabEditingMetadata.setMinPoint(newMin);
prefabEditingMetadata.setDirty(true);
return prefabEditingMetadata;
}
}
public void setSelectedPrefab(
@Nonnull Ref<EntityStore> ref, @Nonnull PrefabEditingMetadata prefabEditingMetadata, @Nonnull ComponentAccessor<EntityStore> componentAccessor
) {
UUIDComponent uuidComponent = componentAccessor.getComponent(ref, UUIDComponent.getComponentType());
assert uuidComponent != null;
Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType());
assert playerComponent != null;
PlayerRef playerRefComponent = componentAccessor.getComponent(ref, PlayerRef.getComponentType());
assert playerRefComponent != null;
UUID playerUUID = uuidComponent.getUuid();
if (this.selectedPrefab.get(playerUUID) != null && this.selectedPrefab.get(playerUUID).equals(prefabEditingMetadata.getUuid())) {
BlockSelection selection = BuilderToolsPlugin.getState(playerComponent, playerRefComponent).getSelection();
if (selection != null
&& prefabEditingMetadata.getMinPoint().equals(selection.getSelectionMin())
&& prefabEditingMetadata.getMaxPoint().equals(selection.getSelectionMax())) {
return;
}
}
this.selectedPrefab.put(playerUUID, prefabEditingMetadata.getUuid());
prefabEditingMetadata.sendAnchorHighlightingPacket(playerRefComponent.getPacketHandler());
BuilderToolsPlugin.addToQueue(
playerComponent,
playerRefComponent,
(r, s, compAccess) -> s.select(prefabEditingMetadata.getMinPoint(), prefabEditingMetadata.getMaxPoint(), null, compAccess)
);
}
public void hidePrefabAnchors(@Nonnull PacketHandler packetHandler) {
packetHandler.writeNoCache(new BuilderToolHideAnchors());
}
@Nullable
public PrefabEditingMetadata getSelectedPrefab(@Nonnull UUID playerUuid) {
UUID prefabUuid = this.selectedPrefab.get(playerUuid);
return prefabUuid == null ? null : this.loadedPrefabMetadata.get(prefabUuid);
}
public boolean clearSelectedPrefab(@Nonnull Ref<EntityStore> ref, @Nonnull ComponentAccessor<EntityStore> componentAccessor) {
UUIDComponent uuidComponent = componentAccessor.getComponent(ref, UUIDComponent.getComponentType());
assert uuidComponent != null;
UUID playerUUID = uuidComponent.getUuid();
if (this.selectedPrefab.remove(playerUUID) == null) {
return false;
} else {
PlayerRef playerRefComponent = componentAccessor.getComponent(ref, PlayerRef.getComponentType());
assert playerRefComponent != null;
Player playerComponent = componentAccessor.getComponent(ref, Player.getComponentType());
assert playerComponent != null;
this.hidePrefabAnchors(playerRefComponent.getPacketHandler());
BuilderToolsPlugin.addToQueue(playerComponent, playerRefComponent, (r, s, compAccess) -> s.deselect(compAccess));
return true;
}
}
@Nonnull
public String getWorldName() {
return this.worldName;
}
public UUID getWorldArrivedFrom() {
return this.worldArrivedFrom;
}
@Nullable
public Transform getTransformArrivedFrom() {
return this.transformArrivedFrom;
}
public UUID getWorldCreator() {
return this.worldCreator;
}
@Nonnull
public Vector3i getSpawnPoint() {
return this.spawnPoint;
}
@Nonnull
public Map<UUID, PrefabEditingMetadata> getLoadedPrefabMetadata() {
return this.loadedPrefabMetadata;
}
public void markPrefabsDirtyAtPosition(@Nonnull Vector3i position) {
for (PrefabEditingMetadata metadata : this.loadedPrefabMetadata.values()) {
if (metadata.isLocationWithinPrefabBoundingBox(position)) {
metadata.setDirty(true);
}
}
}
public void markPrefabsDirtyInBounds(@Nonnull Vector3i min, @Nonnull Vector3i max) {
for (PrefabEditingMetadata metadata : this.loadedPrefabMetadata.values()) {
if (boundsIntersect(metadata.getMinPoint(), metadata.getMaxPoint(), min, max)) {
metadata.setDirty(true);
}
}
}
private static boolean boundsIntersect(@Nonnull Vector3i aMin, @Nonnull Vector3i aMax, @Nonnull Vector3i bMin, @Nonnull Vector3i bMax) {
return aMin.x <= bMax.x && aMax.x >= bMin.x && aMin.y <= bMax.y && aMax.y >= bMin.y && aMin.z <= bMax.z && aMax.z >= bMin.z;
}
@Nonnull
public MapMarker[] createPrefabMarkers() {
return this.loadedPrefabMetadata.values().stream().map(PrefabEditSession::createPrefabMarker).toArray(MapMarker[]::new);
}
@Nonnull
public static MapMarker createPrefabMarker(@Nonnull PrefabEditingMetadata metadata) {
String fileName = metadata.getPrefabPath().getFileName().toString();
String prefabName = fileName.replace(".prefab.json", "");
return new MapMarker(
"prefab-" + metadata.getUuid(),
prefabName,
"Prefab.png",
PositionUtil.toTransformPacket(new Transform(metadata.getAnchorEntityPosition().toVector3d())),
null
);
}
@Nonnull
public PrefabEditSession clone() {
return new PrefabEditSession(this);
}
}