hytale-server/com/hypixel/hytale/server/flock/FlockMembershipSystems.java

760 lines
34 KiB
Java

package com.hypixel.hytale.server.flock;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.SystemGroup;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.HolderSystem;
import com.hypixel.hytale.component.system.RefChangeSystem;
import com.hypixel.hytale.component.system.RefSystem;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.protocol.GameMode;
import com.hypixel.hytale.server.core.entity.UUIDComponent;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.entity.group.EntityGroup;
import com.hypixel.hytale.server.core.modules.entity.EntityModule;
import com.hypixel.hytale.server.core.modules.entity.component.FromWorldGen;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.modules.entity.damage.Damage;
import com.hypixel.hytale.server.core.modules.entity.damage.DamageEventSystem;
import com.hypixel.hytale.server.core.modules.entity.damage.DamageModule;
import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.server.npc.role.RoleDebugFlags;
import java.util.EnumSet;
import java.util.UUID;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class FlockMembershipSystems {
public static boolean canJoinFlock(@Nonnull Ref<EntityStore> reference, @Nonnull Ref<EntityStore> flockReference, @Nonnull Store<EntityStore> store) {
Flock flockComponent = store.getComponent(flockReference, Flock.getComponentType());
assert flockComponent != null;
PersistentFlockData flockData = flockComponent.getFlockData();
if (flockData == null) {
return false;
} else {
EntityGroup entityGroupComponent = store.getComponent(flockReference, EntityGroup.getComponentType());
assert entityGroupComponent != null;
if (entityGroupComponent.size() >= flockData.getMaxGrowSize()) {
return false;
} else {
NPCEntity npcComponent = store.getComponent(reference, NPCEntity.getComponentType());
if (npcComponent == null) {
return false;
} else {
String roleName = npcComponent.getRoleName();
return roleName != null && flockData.isFlockAllowedRole(roleName);
}
}
}
}
public static void join(@Nonnull Ref<EntityStore> ref, @Nonnull Ref<EntityStore> flockRef, @Nonnull Store<EntityStore> store) {
FlockMembership membership = new FlockMembership();
UUIDComponent uuidComponent = store.getComponent(flockRef, UUIDComponent.getComponentType());
assert uuidComponent != null;
membership.setFlockId(uuidComponent.getUuid());
membership.setFlockRef(flockRef);
membership.setMembershipType(FlockMembership.Type.JOINING);
store.putComponent(ref, FlockMembership.getComponentType(), membership);
}
private static boolean canBecomeLeader(@Nonnull Ref<EntityStore> ref) {
Store<EntityStore> store = ref.getStore();
if (store.getComponent(ref, Player.getComponentType()) != null) {
return true;
} else {
NPCEntity npcComponent = store.getComponent(ref, NPCEntity.getComponentType());
return npcComponent != null && npcComponent.getRole() != null ? npcComponent.getRole().isCanLeadFlock() : false;
}
}
private static void markChunkNeedsSaving(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store) {
TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType());
if (transformComponent != null) {
transformComponent.markChunkDirty(store);
}
}
public static class EntityRef extends RefSystem<EntityStore> {
@Nonnull
private final ComponentType<EntityStore, FlockMembership> flockMembershipComponentType;
public EntityRef(@Nonnull ComponentType<EntityStore, FlockMembership> flockMembershipComponentType) {
this.flockMembershipComponentType = flockMembershipComponentType;
}
@Override
public Query<EntityStore> getQuery() {
return this.flockMembershipComponentType;
}
@Override
public void onEntityAdded(
@Nonnull Ref<EntityStore> ref, @Nonnull AddReason reason, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer
) {
commandBuffer.run(_store -> this.joinOrCreateFlock(ref, _store));
}
private void joinOrCreateFlock(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store) {
FlockMembership flockMembershipComponent = store.getComponent(ref, this.flockMembershipComponentType);
assert flockMembershipComponent != null;
UUID flockId = flockMembershipComponent.getFlockId();
Ref<EntityStore> flockReference = store.getExternalData().getRefFromUUID(flockId);
EntityGroup entityGroup;
Flock flock;
if (flockReference != null) {
entityGroup = store.getComponent(flockReference, EntityGroup.getComponentType());
assert entityGroup != null;
flock = store.getComponent(flockReference, Flock.getComponentType());
assert flock != null;
} else {
entityGroup = new EntityGroup();
flock = new Flock();
Holder<EntityStore> holder = EntityStore.REGISTRY.newHolder();
holder.addComponent(UUIDComponent.getComponentType(), new UUIDComponent(flockId));
holder.addComponent(EntityGroup.getComponentType(), entityGroup);
holder.addComponent(Flock.getComponentType(), flock);
flockReference = store.addEntity(holder, AddReason.LOAD);
}
flockMembershipComponent.setFlockRef(flockReference);
if (entityGroup.isMember(ref)) {
throw new IllegalStateException(String.format("Entity %s attempting to reload into group with ID %s despite already being a member", ref, flockId));
} else {
entityGroup.add(ref);
if (flockMembershipComponent.getMembershipType() == FlockMembership.Type.LEADER) {
PersistentFlockData persistentFlockData = store.getComponent(ref, PersistentFlockData.getComponentType());
if (persistentFlockData != null) {
flock.setFlockData(persistentFlockData);
} else {
PersistentFlockData flockData = flock.getFlockData();
if (flockData != null) {
store.putComponent(ref, PersistentFlockData.getComponentType(), flockData);
}
}
Ref<EntityStore> oldLeaderRef = entityGroup.getLeaderRef();
entityGroup.setLeaderRef(ref);
if (oldLeaderRef != null && !oldLeaderRef.equals(ref)) {
FlockMembership oldLeaderComponent = store.getComponent(oldLeaderRef, this.flockMembershipComponentType);
if (oldLeaderComponent != null) {
oldLeaderComponent.setMembershipType(FlockMembership.Type.MEMBER);
}
store.tryRemoveComponent(oldLeaderRef, PersistentFlockData.getComponentType());
FlockMembershipSystems.markChunkNeedsSaving(oldLeaderRef, store);
}
markNeedsSave(ref, store, flock);
if (flock.isTrace()) {
FlockPlugin.get()
.getLogger()
.at(Level.INFO)
.log("Flock %s: Set new leader, old=%s, new=%s, size=%s", flockId, oldLeaderRef, ref, entityGroup.size());
}
} else if (entityGroup.getLeaderRef() == null) {
setInterimLeader(store, flockMembershipComponent, entityGroup, ref, flock, flockId);
}
if (flock.isTrace()) {
FlockPlugin.get().getLogger().at(Level.INFO).log("Flock %s: reference=%s, size=%s", flockId, ref, entityGroup.size());
}
}
}
@Override
public void onEntityRemove(
@Nonnull Ref<EntityStore> ref, @Nonnull RemoveReason reason, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer
) {
FlockMembership membership = store.getComponent(ref, this.flockMembershipComponentType);
assert membership != null;
Ref<EntityStore> flockRef = membership.getFlockRef();
if (flockRef != null && flockRef.isValid()) {
Flock flockComponent = commandBuffer.getComponent(flockRef, Flock.getComponentType());
assert flockComponent != null;
EntityGroup entityGroupComponent = commandBuffer.getComponent(flockRef, EntityGroup.getComponentType());
assert entityGroupComponent != null;
UUIDComponent uuidComponent = commandBuffer.getComponent(flockRef, UUIDComponent.getComponentType());
assert uuidComponent != null;
UUID flockId = uuidComponent.getUuid();
if (reason == RemoveReason.REMOVE || store.getArchetype(ref).contains(Player.getComponentType())) {
entityGroupComponent.remove(ref);
if (flockComponent.isTrace()) {
FlockPlugin.get()
.getLogger()
.at(Level.INFO)
.log(
"Flock %s: Left flock, reference=%s, leader=%s, size=%s",
flockId,
ref,
entityGroupComponent.getLeaderRef(),
entityGroupComponent.size()
);
}
if (!entityGroupComponent.isDissolved() && entityGroupComponent.size() < 2) {
commandBuffer.removeEntity(flockRef, RemoveReason.REMOVE);
return;
}
if (entityGroupComponent.isDissolved()) {
return;
}
PersistentFlockData flockData = flockComponent.getFlockData();
if (flockData != null) {
flockData.decreaseSize();
}
Ref<EntityStore> leader = entityGroupComponent.getLeaderRef();
if (leader != null && !leader.equals(ref)) {
FlockMembershipSystems.markChunkNeedsSaving(leader, store);
return;
}
Ref<EntityStore> newLeader = entityGroupComponent.testMembers(FlockMembershipSystems::canBecomeLeader, true);
if (newLeader == null) {
if (flockComponent.isTrace()) {
FlockPlugin.get()
.getLogger()
.at(Level.INFO)
.log(
"Flock %s: Leave failed to get new leader, reference=%s, leader=%s, size=%s",
flockId,
ref,
entityGroupComponent.getLeaderRef(),
entityGroupComponent.size()
);
}
commandBuffer.removeEntity(flockRef, RemoveReason.REMOVE);
return;
}
entityGroupComponent.setLeaderRef(newLeader);
FlockMembership flockMembershipComponent = store.getComponent(newLeader, this.flockMembershipComponentType);
assert flockMembershipComponent != null;
flockMembershipComponent.setMembershipType(FlockMembership.Type.LEADER);
if (flockData != null) {
commandBuffer.putComponent(newLeader, PersistentFlockData.getComponentType(), flockData);
}
markNeedsSave(newLeader, store, flockComponent);
if (flockComponent.isTrace()) {
FlockPlugin.get()
.getLogger()
.at(Level.INFO)
.log("Flock %s: Set new leader, old=%s, new=%s, size=%s", flockId, ref, newLeader, entityGroupComponent.size());
}
} else if (reason == RemoveReason.UNLOAD) {
entityGroupComponent.remove(ref);
if (!entityGroupComponent.isDissolved() && membership.getMembershipType().isActingAsLeader()) {
Ref<EntityStore> interimLeader = entityGroupComponent.testMembers(member -> true, true);
if (interimLeader != null) {
FlockMembership interimLeaderMembership = store.getComponent(interimLeader, this.flockMembershipComponentType);
if (interimLeaderMembership == null) {
throw new IllegalStateException("Member is missing FlockMembership component!");
}
setInterimLeader(store, interimLeaderMembership, entityGroupComponent, interimLeader, flockComponent, flockId);
}
}
membership.unload();
if (entityGroupComponent.size() <= 0) {
commandBuffer.tryRemoveEntity(flockRef, RemoveReason.UNLOAD);
}
if (flockComponent.isTrace()) {
FlockPlugin.get()
.getLogger()
.at(Level.INFO)
.log(
"Flock %s: Unloaded from flock, reference=%s, leader=%s, size=%s",
flockId,
ref,
entityGroupComponent.getLeaderRef(),
entityGroupComponent.size()
);
}
}
}
}
private static void markNeedsSave(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull Flock flockComponent) {
NPCEntity npcComponent = store.getComponent(ref, NPCEntity.getComponentType());
if (npcComponent != null) {
Role role = npcComponent.getRole();
if (role != null) {
EnumSet<RoleDebugFlags> flags = role.getDebugSupport().getDebugFlags();
flockComponent.setTrace(flags.contains(RoleDebugFlags.Flock));
}
}
FlockMembershipSystems.markChunkNeedsSaving(ref, store);
}
private static void setInterimLeader(
@Nonnull Store<EntityStore> store,
@Nonnull FlockMembership interimLeaderMembership,
@Nonnull EntityGroup entityGroup,
Ref<EntityStore> interimLeader,
@Nonnull Flock flockComponent,
@Nonnull UUID flockId
) {
interimLeaderMembership.setMembershipType(FlockMembership.Type.INTERIM_LEADER);
entityGroup.setLeaderRef(interimLeader);
markNeedsSave(interimLeader, store, flockComponent);
if (flockComponent.isTrace()) {
FlockPlugin.get()
.getLogger()
.at(Level.INFO)
.log("Flock %s: Set new interim leader, old=%s, new=%s, size=%s", flockId, entityGroup.getLeaderRef(), interimLeader, entityGroup.size());
}
}
}
public static class NPCAddedFromWorldGen extends HolderSystem<EntityStore> {
@Nullable
private final ComponentType<EntityStore, NPCEntity> npcComponentType = NPCEntity.getComponentType();
@Nonnull
private final ComponentType<EntityStore, FromWorldGen> fromWorldGenComponentType = FromWorldGen.getComponentType();
@Nonnull
private final ComponentType<EntityStore, FlockMembership> flockMembershipComponentType = FlockMembership.getComponentType();
@Nonnull
private final Query<EntityStore> query = Query.and(this.npcComponentType, this.fromWorldGenComponentType, this.flockMembershipComponentType);
@Nonnull
@Override
public Query<EntityStore> getQuery() {
return this.query;
}
@Nullable
@Override
public SystemGroup<EntityStore> getGroup() {
return EntityModule.get().getPreClearMarkersGroup();
}
@Override
public void onEntityAdd(@Nonnull Holder<EntityStore> holder, @Nonnull AddReason reason, @Nonnull Store<EntityStore> store) {
holder.removeComponent(this.flockMembershipComponentType);
}
@Override
public void onEntityRemoved(@Nonnull Holder<EntityStore> holder, @Nonnull RemoveReason reason, @Nonnull Store<EntityStore> store) {
}
}
public static class OnDamageDealt extends DamageEventSystem {
@Nullable
@Override
public SystemGroup<EntityStore> getGroup() {
return DamageModule.get().getInspectDamageGroup();
}
@Nullable
@Override
public Query<EntityStore> getQuery() {
return Archetype.empty();
}
public void handle(
int index,
@Nonnull ArchetypeChunk<EntityStore> archetypeChunk,
@Nonnull Store<EntityStore> store,
@Nonnull CommandBuffer<EntityStore> commandBuffer,
@Nonnull Damage damage
) {
if (damage.getSource() instanceof Damage.EntitySource entitySource) {
Ref<EntityStore> damageSourceRef = entitySource.getRef();
if (damageSourceRef.isValid()) {
FlockMembership flockMembershipComponent = commandBuffer.getComponent(damageSourceRef, FlockMembership.getComponentType());
if (flockMembershipComponent != null) {
Ref<EntityStore> flockReference = flockMembershipComponent.getFlockRef();
if (flockReference != null && flockReference.isValid()) {
Flock flockComponent = commandBuffer.getComponent(flockReference, Flock.getComponentType());
assert flockComponent != null;
Ref<EntityStore> entityRef = archetypeChunk.getReferenceTo(index);
flockComponent.getNextDamageData().onInflictedDamage(entityRef, damage.getAmount());
if (flockMembershipComponent.getMembershipType().isActingAsLeader()) {
flockComponent.getNextLeaderDamageData().onInflictedDamage(entityRef, damage.getAmount());
}
}
}
}
}
}
}
public static class OnDamageReceived extends DamageEventSystem {
@Nonnull
private final Query<EntityStore> query = FlockMembership.getComponentType();
@Nullable
@Override
public SystemGroup<EntityStore> getGroup() {
return DamageModule.get().getInspectDamageGroup();
}
@Nonnull
@Override
public Query<EntityStore> getQuery() {
return this.query;
}
public void handle(
int index,
@Nonnull ArchetypeChunk<EntityStore> archetypeChunk,
@Nonnull Store<EntityStore> store,
@Nonnull CommandBuffer<EntityStore> commandBuffer,
@Nonnull Damage danage
) {
FlockMembership flockMembershipComponent = archetypeChunk.getComponent(index, FlockMembership.getComponentType());
if (flockMembershipComponent != null) {
Ref<EntityStore> flockRef = flockMembershipComponent.getFlockRef();
if (flockRef != null && flockRef.isValid()) {
Flock flockComponent = commandBuffer.getComponent(flockRef, Flock.getComponentType());
assert flockComponent != null;
flockComponent.getNextDamageData().onSufferedDamage(commandBuffer, danage);
if (flockMembershipComponent.getMembershipType().isActingAsLeader()) {
flockComponent.getNextLeaderDamageData().onSufferedDamage(commandBuffer, danage);
}
}
}
}
}
public static class RefChange extends RefChangeSystem<EntityStore, FlockMembership> {
@Nonnull
private final ComponentType<EntityStore, FlockMembership> flockMembershipComponentType;
public RefChange(@Nonnull ComponentType<EntityStore, FlockMembership> flockMembershipComponentType) {
this.flockMembershipComponentType = flockMembershipComponentType;
}
@Override
public Query<EntityStore> getQuery() {
return this.flockMembershipComponentType;
}
@Nonnull
@Override
public ComponentType<EntityStore, FlockMembership> componentType() {
return this.flockMembershipComponentType;
}
public void onComponentAdded(
@Nonnull Ref<EntityStore> ref,
@Nonnull FlockMembership component,
@Nonnull Store<EntityStore> store,
@Nonnull CommandBuffer<EntityStore> commandBuffer
) {
this.doJoin(ref, component, store, commandBuffer);
}
public void onComponentSet(
@Nonnull Ref<EntityStore> ref,
FlockMembership oldComponent,
@Nonnull FlockMembership newComponent,
@Nonnull Store<EntityStore> store,
@Nonnull CommandBuffer<EntityStore> commandBuffer
) {
assert oldComponent != null;
if (oldComponent.getMembershipType() == FlockMembership.Type.JOINING) {
this.doJoin(ref, newComponent, store, commandBuffer);
} else {
doLeave(ref, oldComponent, store, commandBuffer);
commandBuffer.run(_store -> this.doJoin(ref, newComponent, store, commandBuffer));
}
}
public void onComponentRemoved(
@Nonnull Ref<EntityStore> ref,
@Nonnull FlockMembership component,
@Nonnull Store<EntityStore> store,
@Nonnull CommandBuffer<EntityStore> commandBuffer
) {
if (component.getMembershipType() != FlockMembership.Type.JOINING) {
doLeave(ref, component, store, commandBuffer);
}
}
private void doJoin(
@Nonnull Ref<EntityStore> ref,
@Nonnull FlockMembership membershipComponent,
@Nonnull Store<EntityStore> store,
@Nonnull CommandBuffer<EntityStore> commandBuffer
) {
Ref<EntityStore> flockRef = membershipComponent.getFlockRef();
if (flockRef != null) {
if (!flockRef.isValid()) {
((HytaleLogger.Api)FlockPlugin.get().getLogger().atWarning()).log("Entity %s attempting to join invalid flock with ref %s", ref, flockRef);
commandBuffer.removeComponent(ref, this.flockMembershipComponentType);
} else {
Flock flockComponent = commandBuffer.getComponent(flockRef, Flock.getComponentType());
assert flockComponent != null;
EntityGroup entityGroupComponent = commandBuffer.getComponent(flockRef, EntityGroup.getComponentType());
assert entityGroupComponent != null;
UUIDComponent uuidComponent = commandBuffer.getComponent(flockRef, UUIDComponent.getComponentType());
assert uuidComponent != null;
UUID flockId = uuidComponent.getUuid();
if (!entityGroupComponent.isMember(ref)) {
if (membershipComponent.getMembershipType() != FlockMembership.Type.JOINING) {
throw new IllegalStateException(
String.format(
"Entity %s attempting to join group with ID %s but has wrong membership status %s",
ref,
flockId,
membershipComponent.getMembershipType()
)
);
} else {
boolean isDead = store.getArchetype(ref).contains(DeathComponent.getComponentType());
if (ref.isValid() && !isDead) {
Player playerComponent = commandBuffer.getComponent(ref, Player.getComponentType());
if (playerComponent != null && playerComponent.getGameMode() != GameMode.Adventure) {
if (flockComponent.isTrace()) {
FlockPlugin.get().getLogger().at(Level.INFO).log("Flock %s: Failed to join, ref=%s. Player in creative mode.", flockId, ref);
}
commandBuffer.removeComponent(ref, this.flockMembershipComponentType);
} else {
PersistentFlockData flockData = flockComponent.getFlockData();
if (flockData == null) {
if (flockComponent.isTrace()) {
FlockPlugin.get()
.getLogger()
.at(Level.INFO)
.log(
"Flock %s: Rejected join entity due to leader not being loaded, ref=%s, size=%s",
flockId,
ref,
entityGroupComponent.size()
);
}
commandBuffer.removeComponent(ref, this.flockMembershipComponentType);
} else {
Ref<EntityStore> leader = entityGroupComponent.getLeaderRef();
boolean mustBecomeLeader = leader == null;
boolean wasFirstJoiner = mustBecomeLeader;
if (playerComponent != null) {
if (leader != null && store.getComponent(leader, Player.getComponentType()) != null) {
if (flockComponent.isTrace()) {
FlockPlugin.get()
.getLogger()
.at(Level.INFO)
.log("Flock %s: Failed join 2 players, ref=%s, size=%s", flockId, ref, entityGroupComponent.size());
}
commandBuffer.removeComponent(ref, this.flockMembershipComponentType);
return;
}
mustBecomeLeader = true;
}
entityGroupComponent.add(ref);
if (mustBecomeLeader) {
setNewLeader(flockId, entityGroupComponent, flockComponent, ref, store, commandBuffer);
if (wasFirstJoiner && flockComponent.isTrace()) {
FlockPlugin.get()
.getLogger()
.at(Level.INFO)
.log("Flock %s: Joined no leader, ref=%s, size=%s", flockId, ref, entityGroupComponent.size());
}
} else {
membershipComponent.setMembershipType(FlockMembership.Type.MEMBER);
}
flockData.increaseSize();
FlockMembershipSystems.markChunkNeedsSaving(entityGroupComponent.getLeaderRef(), store);
if (flockComponent.isTrace()) {
FlockPlugin.get()
.getLogger()
.at(Level.INFO)
.log("Flock %s: Joined join ref=%s, size=%s", flockId, ref, entityGroupComponent.size());
}
FlockMembershipSystems.markChunkNeedsSaving(ref, store);
}
}
} else {
if (flockComponent.isTrace()) {
FlockPlugin.get()
.getLogger()
.at(Level.INFO)
.log("Flock %s: Failed to join entity ref=%s, size=%s", flockId, ref, entityGroupComponent.size());
}
commandBuffer.removeComponent(ref, this.flockMembershipComponentType);
}
}
}
}
}
}
private static void doLeave(
@Nonnull Ref<EntityStore> ref,
@Nonnull FlockMembership membershipComponent,
@Nonnull Store<EntityStore> store,
@Nonnull CommandBuffer<EntityStore> commandBuffer
) {
Ref<EntityStore> flockReference = membershipComponent.getFlockRef();
if (flockReference != null && flockReference.isValid()) {
Flock flockComponent = commandBuffer.getComponent(flockReference, Flock.getComponentType());
assert flockComponent != null;
EntityGroup entityGroupComponent = commandBuffer.getComponent(flockReference, EntityGroup.getComponentType());
assert entityGroupComponent != null;
UUIDComponent uuidComponent = commandBuffer.getComponent(flockReference, UUIDComponent.getComponentType());
assert uuidComponent != null;
UUID flockId = uuidComponent.getUuid();
entityGroupComponent.remove(ref);
if (flockComponent.isTrace()) {
FlockPlugin.get()
.getLogger()
.at(Level.INFO)
.log("Flock %s: Left flock, reference=%s, leader=%s, size=%s", flockId, ref, entityGroupComponent.getLeaderRef(), entityGroupComponent.size());
}
if (!entityGroupComponent.isDissolved() && entityGroupComponent.size() < 2) {
commandBuffer.removeEntity(flockReference, RemoveReason.REMOVE);
} else if (!entityGroupComponent.isDissolved()) {
PersistentFlockData flockData = flockComponent.getFlockData();
if (flockData != null) {
flockData.decreaseSize();
}
Ref<EntityStore> leader = entityGroupComponent.getLeaderRef();
if (leader != null && !ref.equals(leader)) {
FlockMembershipSystems.markChunkNeedsSaving(leader, store);
} else {
Ref<EntityStore> newLeader = entityGroupComponent.testMembers(FlockMembershipSystems::canBecomeLeader, true);
if (newLeader == null) {
if (flockComponent.isTrace()) {
FlockPlugin.get()
.getLogger()
.at(Level.INFO)
.log(
"Flock %s: Leave failed to get new leader, reference=%s, leader=%s, size=%s",
flockId,
ref,
entityGroupComponent.getLeaderRef(),
entityGroupComponent.size()
);
}
commandBuffer.removeEntity(flockReference, RemoveReason.REMOVE);
return;
}
setNewLeader(flockId, entityGroupComponent, flockComponent, newLeader, store, commandBuffer);
}
}
}
}
private static void setNewLeader(
@Nonnull UUID flockId,
@Nonnull EntityGroup entityGroup,
@Nonnull Flock flock,
@Nonnull Ref<EntityStore> ref,
@Nonnull Store<EntityStore> store,
@Nonnull CommandBuffer<EntityStore> commandBuffer
) {
Ref<EntityStore> oldLeader = entityGroup.getLeaderRef();
if (oldLeader == null || !oldLeader.equals(ref)) {
entityGroup.setLeaderRef(ref);
if (oldLeader != null) {
FlockMembership oldLeaderComponent = store.getComponent(oldLeader, FlockMembership.getComponentType());
if (oldLeaderComponent != null) {
oldLeaderComponent.setMembershipType(FlockMembership.Type.MEMBER);
}
commandBuffer.tryRemoveComponent(oldLeader, PersistentFlockData.getComponentType());
FlockMembershipSystems.markChunkNeedsSaving(oldLeader, store);
}
FlockMembership flockMembershipComponent = store.getComponent(ref, FlockMembership.getComponentType());
assert flockMembershipComponent != null;
flockMembershipComponent.setMembershipType(FlockMembership.Type.LEADER);
NPCEntity newLeaderNpcComponent = store.getComponent(ref, NPCEntity.getComponentType());
if (newLeaderNpcComponent != null) {
Role role = newLeaderNpcComponent.getRole();
if (role != null) {
EnumSet<RoleDebugFlags> flags = role.getDebugSupport().getDebugFlags();
flock.setTrace(flags.contains(RoleDebugFlags.Flock));
}
}
PersistentFlockData flockData = flock.getFlockData();
if (flockData != null) {
commandBuffer.putComponent(ref, PersistentFlockData.getComponentType(), flockData);
}
FlockMembershipSystems.markChunkNeedsSaving(ref, store);
if (flock.isTrace()) {
FlockPlugin.get()
.getLogger()
.at(Level.INFO)
.log("Flock %s: Set new leader, old=%s, new=%s, size=%s", flockId, oldLeader, ref, entityGroup.size());
}
}
}
}
}