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;
|
package io.github.vftdan.mcentityvisibilityrules;
|
||||||
|
|
||||||
|
import io.github.vftdan.mcentityvisibilityrules.rules.GloballyVisibleTeammates;
|
||||||
|
|
||||||
import net.fabricmc.api.ModInitializer;
|
import net.fabricmc.api.ModInitializer;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -19,6 +21,7 @@ public class EntityVisibilityRules implements ModInitializer {
|
||||||
// However, some things (like resources) may still be uninitialized.
|
// However, some things (like resources) may still be uninitialized.
|
||||||
// Proceed with mild caution.
|
// 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",
|
"package": "io.github.vftdan.mcentityvisibilityrules.mixin",
|
||||||
"compatibilityLevel": "JAVA_21",
|
"compatibilityLevel": "JAVA_21",
|
||||||
"mixins": [
|
"mixins": [
|
||||||
"ExampleMixin"
|
"ExampleMixin",
|
||||||
|
"EntityTrackerMixin"
|
||||||
],
|
],
|
||||||
"injectors": {
|
"injectors": {
|
||||||
"defaultRequire": 1
|
"defaultRequire": 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
},
|
},
|
||||||
"license": "CC0-1.0",
|
"license": "CC0-1.0",
|
||||||
"icon": "assets/entity-visibility-rules/icon.png",
|
"icon": "assets/entity-visibility-rules/icon.png",
|
||||||
"environment": "server",
|
"environment": "*",
|
||||||
"entrypoints": {
|
"entrypoints": {
|
||||||
"main": [
|
"main": [
|
||||||
"io.github.vftdan.mcentityvisibilityrules.EntityVisibilityRules"
|
"io.github.vftdan.mcentityvisibilityrules.EntityVisibilityRules"
|
||||||
|
|
Loading…
Reference in New Issue