Implemented infinite-radius teammate tracking
This commit is contained in:
parent
1f28554a23
commit
dfbf2ca76f
|
@ -0,0 +1,38 @@
|
|||
package io.github.vftdan.mcentityvisibilityrules;
|
||||
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
public interface DynamicPriorityEntityVisibilityRule extends EntityVisibilityRule {
|
||||
void setRulePriority(int priority);
|
||||
|
||||
public static class Wrapper<T extends EntityVisibilityRule> implements DynamicPriorityEntityVisibilityRule {
|
||||
protected T wrapped;
|
||||
protected int priority;
|
||||
|
||||
public Wrapper(T inner, int initialPriority) {
|
||||
wrapped = inner;
|
||||
priority = initialPriority;
|
||||
}
|
||||
|
||||
public T unwrap() {
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PriorityBooleanMonoid shouldBeVisible(ServerPlayerEntity player, EntityTrackerFieldsAccessor tracker) {
|
||||
if (wrapped == null) {
|
||||
return PriorityBooleanMonoid.IGNORE;
|
||||
}
|
||||
PriorityBooleanMonoid result = wrapped.shouldBeVisible(player, tracker);
|
||||
if (result == null) {
|
||||
return result;
|
||||
}
|
||||
return result.setPriority(priority);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRulePriority(int priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
package io.github.vftdan.mcentityvisibilityrules;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.server.network.EntityTrackerEntry;
|
||||
import net.minecraft.util.math.ChunkSectionPos;
|
||||
|
||||
public interface EntityTrackerFieldsAccessor {
|
||||
EntityTrackerEntry getEntry();
|
||||
Entity getEntity();
|
||||
int getMaxDistance();
|
||||
ChunkSectionPos getTrackedSection();
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package io.github.vftdan.mcentityvisibilityrules;
|
||||
|
||||
import static com.google.common.collect.Sets.newIdentityHashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
public interface EntityVisibilityRule {
|
||||
PriorityBooleanMonoid shouldBeVisible(ServerPlayerEntity player, EntityTrackerFieldsAccessor tracker);
|
||||
|
||||
public static final Set<EntityVisibilityRule> registry = newIdentityHashSet();
|
||||
|
||||
public static PriorityBooleanMonoid applyAll(ServerPlayerEntity player, EntityTrackerFieldsAccessor tracker) {
|
||||
return applyAll(registry, player, tracker);
|
||||
}
|
||||
|
||||
public static PriorityBooleanMonoid applyAll(final Iterable<EntityVisibilityRule> rules, final ServerPlayerEntity player, final EntityTrackerFieldsAccessor tracker) {
|
||||
var mappedIterator = new Iterator<PriorityBooleanMonoid>() {
|
||||
final Iterator<EntityVisibilityRule> wrapped = rules.iterator();
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return wrapped.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PriorityBooleanMonoid next() {
|
||||
EntityVisibilityRule rule = wrapped.next();
|
||||
if (rule == null) {
|
||||
return null;
|
||||
}
|
||||
return rule.shouldBeVisible(player, tracker);
|
||||
}
|
||||
};
|
||||
return PriorityBooleanMonoid.fold(mappedIterator);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package io.github.vftdan.mcentityvisibilityrules;
|
||||
|
||||
import io.github.vftdan.mcentityvisibilityrules.rules.GloballyVisibleTeammates;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
@ -19,6 +21,7 @@ public class EntityVisibilityRules implements ModInitializer {
|
|||
// However, some things (like resources) may still be uninitialized.
|
||||
// Proceed with mild caution.
|
||||
|
||||
LOGGER.info("Hello Fabric world!");
|
||||
LOGGER.info("Initializing entity-visibility-rules");
|
||||
EntityVisibilityRule.registry.add(GloballyVisibleTeammates.INSTANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
package io.github.vftdan.mcentityvisibilityrules;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class PriorityBooleanMonoid implements Comparable<PriorityBooleanMonoid> /* partial ordering (ignores logicalValue) */ {
|
||||
private final boolean present;
|
||||
private final boolean logicalValue;
|
||||
private final int priority;
|
||||
|
||||
public static final PriorityBooleanMonoid IGNORE = new PriorityBooleanMonoid();
|
||||
|
||||
public PriorityBooleanMonoid(boolean logicalValue, int priority) {
|
||||
present = true;
|
||||
this.logicalValue = logicalValue;
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
private PriorityBooleanMonoid() {
|
||||
present = false;
|
||||
logicalValue = false;
|
||||
priority = 0;
|
||||
}
|
||||
|
||||
public static PriorityBooleanMonoid acceptWith(int priority) {
|
||||
return new PriorityBooleanMonoid(true, priority);
|
||||
}
|
||||
|
||||
public static PriorityBooleanMonoid rejectWith(int priority) {
|
||||
return new PriorityBooleanMonoid(false, priority);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(PriorityBooleanMonoid other) {
|
||||
if (other == null) {
|
||||
return present ? 1 : 0;
|
||||
}
|
||||
if (!present) {
|
||||
return other.present ? -1 : 0;
|
||||
}
|
||||
if (!other.present) {
|
||||
return 1;
|
||||
}
|
||||
return priority - other.priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (other == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (other instanceof PriorityBooleanMonoid casted) {
|
||||
return equals(casted);
|
||||
}
|
||||
return super.equals(other);
|
||||
}
|
||||
|
||||
public boolean equals(PriorityBooleanMonoid other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!present) {
|
||||
return !other.present;
|
||||
}
|
||||
|
||||
if (!other.present) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return priority == other.priority && logicalValue == other.logicalValue;
|
||||
}
|
||||
|
||||
public PriorityBooleanMonoid setPriority(int newPriority) {
|
||||
if (!present) {
|
||||
return this;
|
||||
}
|
||||
return new PriorityBooleanMonoid(logicalValue, newPriority);
|
||||
}
|
||||
|
||||
public Boolean getLogicalValueOrNull() {
|
||||
if (!present) {
|
||||
return null;
|
||||
}
|
||||
return logicalValue;
|
||||
}
|
||||
|
||||
public Integer getPriorityOrNull() {
|
||||
if (!present) {
|
||||
return null;
|
||||
}
|
||||
return priority;
|
||||
}
|
||||
|
||||
public boolean getLogicalValueOr(boolean dflt) {
|
||||
if (!present) {
|
||||
return dflt;
|
||||
}
|
||||
return logicalValue;
|
||||
}
|
||||
|
||||
public boolean isPresent() {
|
||||
return present;
|
||||
}
|
||||
|
||||
public PriorityBooleanMonoid combineWith(PriorityBooleanMonoid other) {
|
||||
if (other == null) {
|
||||
return this;
|
||||
}
|
||||
if (!present) {
|
||||
return other;
|
||||
}
|
||||
return (other.priority >= priority) ? other : this;
|
||||
}
|
||||
|
||||
public static PriorityBooleanMonoid fold(Iterator<PriorityBooleanMonoid> items) {
|
||||
PriorityBooleanMonoid result = IGNORE;
|
||||
if (items == null) {
|
||||
return result;
|
||||
}
|
||||
while (items.hasNext()) {
|
||||
result = result.combineWith(items.next());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static PriorityBooleanMonoid fold(Iterable<PriorityBooleanMonoid> items) {
|
||||
if (items == null) {
|
||||
return IGNORE;
|
||||
}
|
||||
return fold(items.iterator());
|
||||
}
|
||||
|
||||
public static PriorityBooleanMonoid fold(PriorityBooleanMonoid... items) {
|
||||
if (items == null) {
|
||||
return IGNORE;
|
||||
}
|
||||
return fold(Arrays.asList(items));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package io.github.vftdan.mcentityvisibilityrules.mixin;
|
||||
|
||||
import io.github.vftdan.mcentityvisibilityrules.EntityTrackerFieldsAccessor;
|
||||
import io.github.vftdan.mcentityvisibilityrules.EntityVisibilityRule;
|
||||
import java.util.Set;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.server.network.EntityTrackerEntry;
|
||||
import net.minecraft.server.network.PlayerAssociatedNetworkHandler;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerChunkLoadingManager;
|
||||
import net.minecraft.util.math.ChunkSectionPos;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(targets = "net.minecraft.server.world.ServerChunkLoadingManager$EntityTracker")
|
||||
public abstract class EntityTrackerMixin implements EntityTrackerFieldsAccessor {
|
||||
@Inject(at = @At("HEAD"), method = "updateTrackedStatus", cancellable = true)
|
||||
private void onUpdateTrackedStatus(ServerPlayerEntity player, CallbackInfo info) {
|
||||
if (player == getEntity()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Boolean result = EntityVisibilityRule.applyAll(player, this).getLogicalValueOrNull();
|
||||
if (result == null) {
|
||||
return; // do not cancel
|
||||
}
|
||||
info.cancel();
|
||||
if (result) {
|
||||
startTrackingPlayer(player);
|
||||
} else {
|
||||
stopTrackingPlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
@Accessor
|
||||
public abstract EntityTrackerEntry getEntry();
|
||||
|
||||
@Accessor
|
||||
public abstract Entity getEntity();
|
||||
|
||||
@Accessor
|
||||
public abstract int getMaxDistance();
|
||||
|
||||
@Accessor
|
||||
public abstract ChunkSectionPos getTrackedSection();
|
||||
|
||||
@Accessor
|
||||
protected abstract Set<PlayerAssociatedNetworkHandler> getListeners();
|
||||
|
||||
@Invoker("stopTracking")
|
||||
protected abstract void stopTrackingPlayer(ServerPlayerEntity player);
|
||||
|
||||
protected void startTrackingPlayer(ServerPlayerEntity player) {
|
||||
if (getListeners().add(player.networkHandler)) {
|
||||
getEntry().startTracking(player);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package io.github.vftdan.mcentityvisibilityrules.rules;
|
||||
|
||||
import io.github.vftdan.mcentityvisibilityrules.DynamicPriorityEntityVisibilityRule;
|
||||
import io.github.vftdan.mcentityvisibilityrules.EntityTrackerFieldsAccessor;
|
||||
import io.github.vftdan.mcentityvisibilityrules.PriorityBooleanMonoid;
|
||||
import static io.github.vftdan.mcentityvisibilityrules.PriorityBooleanMonoid.IGNORE;
|
||||
import net.minecraft.scoreboard.Team;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
|
||||
public class GloballyVisibleTeammates implements DynamicPriorityEntityVisibilityRule {
|
||||
public static final GloballyVisibleTeammates INSTANCE = new GloballyVisibleTeammates();
|
||||
|
||||
private PriorityBooleanMonoid ACCEPT = PriorityBooleanMonoid.acceptWith(0);
|
||||
|
||||
protected GloballyVisibleTeammates() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public PriorityBooleanMonoid shouldBeVisible(ServerPlayerEntity player, EntityTrackerFieldsAccessor tracker) {
|
||||
if (tracker.getEntity() instanceof PlayerEntity otherPlayer) {
|
||||
Team observerTeam = player.getScoreboardTeam();
|
||||
if (observerTeam == null) {
|
||||
return IGNORE;
|
||||
}
|
||||
|
||||
Team observeeTeam = otherPlayer.getScoreboardTeam();
|
||||
if (!observerTeam.isEqual(observeeTeam)) {
|
||||
return IGNORE;
|
||||
}
|
||||
|
||||
if (!observerTeam.shouldShowFriendlyInvisibles()) {
|
||||
return IGNORE;
|
||||
}
|
||||
|
||||
return ACCEPT;
|
||||
}
|
||||
return IGNORE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRulePriority(int priority) {
|
||||
ACCEPT = PriorityBooleanMonoid.acceptWith(priority);;
|
||||
}
|
||||
}
|
|
@ -3,9 +3,10 @@
|
|||
"package": "io.github.vftdan.mcentityvisibilityrules.mixin",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"mixins": [
|
||||
"ExampleMixin"
|
||||
"ExampleMixin",
|
||||
"EntityTrackerMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
},
|
||||
"license": "CC0-1.0",
|
||||
"icon": "assets/entity-visibility-rules/icon.png",
|
||||
"environment": "server",
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"io.github.vftdan.mcentityvisibilityrules.EntityVisibilityRules"
|
||||
|
|
Loading…
Reference in New Issue