Skip to content

Commit

Permalink
Support for 1.20.5 and Mojang mapped server
Browse files Browse the repository at this point in the history
  • Loading branch information
froobynooby committed Apr 28, 2024
1 parent 301d8a0 commit 34fb975
Show file tree
Hide file tree
Showing 15 changed files with 377 additions and 115 deletions.
2 changes: 1 addition & 1 deletion src/main/java/com/froobworld/farmcontrol/FarmControl.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public void onEnable() {
registerCommands();

new FcMetrics(this, 9692);
hookManager.getSchedulerHook().runRepeatingTask(() -> RemoveRandomMovementAction.cleanUp(this), 1200, 1200); // Hack to fix leaking entities
hookManager.getSchedulerHook().runRepeatingTask(() -> RemoveRandomMovementAction.cleanUp(this), 1200, 1200);
}

public void reload() throws Exception {
Expand Down
11 changes: 10 additions & 1 deletion src/main/java/com/froobworld/farmcontrol/HookManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.froobworld.farmcontrol.hook.entitygetter.BukkitEntityGetterHook;
import com.froobworld.farmcontrol.hook.entitygetter.EntityGetterHook;
import com.froobworld.farmcontrol.hook.entitygetter.RegionisedEntityGetterHook;
import com.froobworld.farmcontrol.hook.nms.NmsHooks;
import com.froobworld.farmcontrol.hook.scheduler.BukkitSchedulerHook;
import com.froobworld.farmcontrol.hook.scheduler.RegionisedSchedulerHook;
import com.froobworld.farmcontrol.hook.scheduler.SchedulerHook;
Expand All @@ -13,13 +14,15 @@

public class HookManager {
private final FarmControl farmControl;
private final NmsHooks nmsHooks;
private final TickHook tickHook;
private final SchedulerHook schedulerHook;
private final EntityGetterHook entityGetterHook;
private MsptTracker msptTracker;

public HookManager(FarmControl farmControl) {
this.farmControl = farmControl;
nmsHooks = new NmsHooks();
if (RegionisedSchedulerHook.isCompatible()) {
schedulerHook = new RegionisedSchedulerHook(farmControl);
} else {
Expand All @@ -29,8 +32,10 @@ public HookManager(FarmControl farmControl) {
tickHook = null;
} else if (PaperTickHook.isCompatible()) {
tickHook = new PaperTickHook();
} else if (nmsHooks.getTickTimeNmsHook() != null) {
tickHook = new BukkitTickHook(schedulerHook, nmsHooks.getTickTimeNmsHook());
} else {
tickHook = new BukkitTickHook(schedulerHook);
tickHook = null;
}
if (RegionisedEntityGetterHook.isCompatible()) {
entityGetterHook = new RegionisedEntityGetterHook(farmControl);
Expand Down Expand Up @@ -74,4 +79,8 @@ public SchedulerHook getSchedulerHook() {
public EntityGetterHook getEntityGetterHook() {
return entityGetterHook;
}

public NmsHooks getNmsHooks() {
return nmsHooks;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.froobworld.farmcontrol.FarmControl;
import com.froobworld.farmcontrol.controller.action.*;
import com.froobworld.farmcontrol.controller.breeding.BreedingBlocker;
import com.froobworld.farmcontrol.utils.NmsUtils;

import java.util.HashMap;
import java.util.HashSet;
Expand All @@ -20,8 +19,8 @@ public void addDefaults(FarmControl farmControl) {
addAction(new RemoveAiAction());
addAction(new RemoveAwarenessAction());
addAction(new DisableBreedingAction(new BreedingBlocker(farmControl)));
if (NmsUtils.GoalSelectorHelper.isCompatible()) {
addAction(new RemoveRandomMovementAction());
if (farmControl.getHookManager().getNmsHooks().getMobGoalNmsHook() != null) {
addAction(new RemoveRandomMovementAction(farmControl.getHookManager().getNmsHooks().getMobGoalNmsHook()));
} else {
farmControl.getLogger().warning("The remove-random-movement action is not available on this version.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@
import com.froobworld.farmcontrol.controller.task.UntriggerPerformTask;
import com.froobworld.farmcontrol.controller.tracker.CycleHistoryManager;
import com.froobworld.farmcontrol.controller.trigger.Trigger;
import com.froobworld.farmcontrol.hook.scheduler.RegionisedSchedulerHook;
import com.froobworld.farmcontrol.hook.scheduler.ScheduledTask;
import com.froobworld.farmcontrol.utils.Actioner;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.*;

import java.util.*;
import java.util.concurrent.CompletableFuture;

public class FarmController {
public static final Class<?>[] ENTITY_CLASSES = List.of(Mob.class, Vehicle.class, Projectile.class, Item.class).toArray(new Class[0]);
Expand Down Expand Up @@ -66,7 +64,7 @@ public void addWorld(World world) {
}
triggerProfileMap.computeIfAbsent(proactiveTrigger, trigger -> new HashSet<>()).add(actionProfile);
}
if (!RegionisedSchedulerHook.isCompatible()) {
if (farmControl.getHookManager().getMsptTracker() != null) {
Trigger reactiveTrigger = farmControl.getTriggerManager().getTrigger("reactive");
for (String profileName : farmControl.getFcConfig().worldSettings.of(world).profiles.reactive.get()) {
ActionProfile actionProfile = farmControl.getProfileManager().getActionProfile(profileName.toLowerCase());
Expand All @@ -78,7 +76,7 @@ public void addWorld(World world) {
}
} else {
if (!farmControl.getFcConfig().worldSettings.of(world).profiles.proactive.get().isEmpty()) {
farmControl.getLogger().warning("Reactive mode is not supported on Folia - ignoring reactive profiles for world '" + world.getName() + "'");
farmControl.getLogger().warning("Reactive mode is not supported on your version - ignoring reactive profiles for world '" + world.getName() + "'");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,17 @@
package com.froobworld.farmcontrol.controller.action;

import com.froobworld.farmcontrol.FarmControl;
import com.froobworld.farmcontrol.utils.NmsUtils;
import com.froobworld.farmcontrol.hook.nms.mobgoal.MobGoalNmsHook;
import com.google.common.collect.MapMaker;
import com.google.common.collect.Sets;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Mob;

import java.util.*;

import static org.joor.Reflect.*;

public class RemoveRandomMovementAction extends Action {
private final static Map<Mob, Set<Object>> entityRemovedGoalsMap = new MapMaker().weakKeys().makeMap();
private final static Set<Class<?>> randomMovementGoals = new HashSet<>();

static {
try {
randomMovementGoals.add(Class.forName(NmsUtils.getFullyQualifiedClassName("PathfinderGoalRandomFly", "world.entity.ai.goal")));
randomMovementGoals.add(Class.forName(NmsUtils.getFullyQualifiedClassName("PathfinderGoalRandomStroll", "world.entity.ai.goal")));
randomMovementGoals.add(Class.forName(NmsUtils.getFullyQualifiedClassName("PathfinderGoalRandomStrollLand", "world.entity.ai.goal")));
randomMovementGoals.add(Class.forName(NmsUtils.getFullyQualifiedClassName("PathfinderGoalRandomSwim", "world.entity.ai.goal")));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private final static Map<Mob, Map<Object, Set<Object>>> entityRemovedGoalsMap = new MapMaker().weakKeys().makeMap();
private final MobGoalNmsHook nmsHook;

public static void cleanUp(FarmControl farmControl) {
List<Mob> mobs = new ArrayList<>(entityRemovedGoalsMap.keySet());
Expand All @@ -37,8 +24,9 @@ public static void cleanUp(FarmControl farmControl) {
}
}

public RemoveRandomMovementAction() {
public RemoveRandomMovementAction(MobGoalNmsHook nmsHook) {
super("remove-random-movement", Mob.class, false, false, true);
this.nmsHook = nmsHook;
}

@Override
Expand All @@ -47,39 +35,44 @@ public void doAction(Entity entity) {
return;
}

Object entityObject = on(mob).call("getHandle").get();
Set<?> wrappedGoals = on(entityObject)
.field(NmsUtils.GoalSelectorHelper.getGoalSelectorFieldName())
.field("d")
.as(Set.class);
Iterator<?> goalIterator = wrappedGoals.iterator();
Set<Object> removedGoals = new HashSet<>();
while (goalIterator.hasNext()) {
Object next = goalIterator.next();
Class<?> nextClass = on(next)
.field("a")
.get().getClass();
if (randomMovementGoals.contains(nextClass)) {
goalIterator.remove();
removedGoals.add(next);
for (Object goalSelector : nmsHook.getGoalSelectors(mob)) {
Set<Object> wrappedGoals = nmsHook.getWrappedGoals(goalSelector);
Set<Object> removedGoals = new HashSet<>();

Iterator<Object> goalIterator = wrappedGoals.iterator();
while (goalIterator.hasNext()) {
Object nextGoal = goalIterator.next();
Class<?> nextClass = nmsHook.unwrapGoal(nextGoal).getClass();

if (nmsHook.getRandomMovementGoalClasses().contains(nextClass)) {
goalIterator.remove();
removedGoals.add(nextGoal);
}
}
entityRemovedGoalsMap
.compute(mob, (k, v) -> v == null ? new HashMap<>() : v)
.compute(goalSelector, (k, v) -> v == null ? removedGoals : Sets.union(removedGoals, v));
}
entityRemovedGoalsMap.compute(mob, (k, v) -> v == null ? removedGoals : Sets.union(removedGoals, v));
}

@Override
public void undoAction(Entity entity) {
if (!(entity instanceof Mob mob)) {
return;
}
Object entityObject = on(mob).call("getHandle").get();
Set<Object> wrappedGoals = on(entityObject)
.field(NmsUtils.GoalSelectorHelper.getGoalSelectorFieldName())
.field("d")
.as(Set.class);
Set<Object> removedGoals = entityRemovedGoalsMap.remove(mob);
if (removedGoals != null) {
wrappedGoals.addAll(removedGoals);
Map<Object, Set<Object>> removedGoalsMap = entityRemovedGoalsMap.remove(mob);
if (removedGoalsMap == null) {
return;
}
Iterator<Object> goalSelectorIterator = removedGoalsMap.keySet().iterator();
while (goalSelectorIterator.hasNext()) {
Object goalSelector = goalSelectorIterator.next();
Set<Object> wrappedGoals = nmsHook.getWrappedGoals(goalSelector);
Set<Object> removedGoals = removedGoalsMap.get(goalSelector);
if (removedGoals != null) {
wrappedGoals.addAll(removedGoals);
}
goalSelectorIterator.remove();
}
}

Expand Down
38 changes: 38 additions & 0 deletions src/main/java/com/froobworld/farmcontrol/hook/nms/NmsHooks.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.froobworld.farmcontrol.hook.nms;

import com.froobworld.farmcontrol.hook.nms.mobgoal.MobGoalNmsHook;
import com.froobworld.farmcontrol.hook.nms.mobgoal.Bukkit1_16MobGoalNmsHook;
import com.froobworld.farmcontrol.hook.nms.mobgoal.Bukkit1_20_5MobGoalNmsHook;
import com.froobworld.farmcontrol.hook.nms.mobgoal.Mojmap1_20MobGoalNmsHook;
import com.froobworld.farmcontrol.hook.nms.tick.Bukkit1_16TickTimesHook;
import com.froobworld.farmcontrol.hook.nms.tick.TickTimesNmsHook;

import java.util.stream.Stream;

public class NmsHooks {
private final MobGoalNmsHook mobGoalNmsHook;
private final TickTimesNmsHook tickTimesNmsHook;

public NmsHooks() {
mobGoalNmsHook = Stream.of(
new Mojmap1_20MobGoalNmsHook(),
new Bukkit1_20_5MobGoalNmsHook(),
new Bukkit1_16MobGoalNmsHook()
).filter(MobGoalNmsHook::isCompatible)
.findFirst()
.orElse(null);
tickTimesNmsHook = Stream.of(
new Bukkit1_16TickTimesHook()
).filter(TickTimesNmsHook::isCompatible)
.findFirst()
.orElse(null);
}

public MobGoalNmsHook getMobGoalNmsHook() {
return mobGoalNmsHook;
}

public TickTimesNmsHook getTickTimeNmsHook() {
return tickTimesNmsHook;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package com.froobworld.farmcontrol.hook.nms.mobgoal;

import org.bukkit.entity.Mob;
import org.joor.Reflect;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static org.joor.Reflect.*;

public abstract class BaseMobGoalNmsHook implements MobGoalNmsHook {
private final String wrappedGoalsFieldName; // Set<WrappedGoal> in GoalSelector
private final String unwrappedGoalFieldName; // Goal in WrappedGoal
private final String mobClassName; // NMS class for mobs
private final String goalSelectorClassName; // NMS class for goal selector
private final String wrappedGoalClassName; // NMS class for wrapped goals
private final String unwrappedGoalClassName; // NMS class for unwrapped goals (i.e. just "goals")
private final List<String> randomMovementClassNames; // NMS classes for random movement goals
private final List<String> goalSelectorFieldNames = new ArrayList<>(); // mobs have two goal selectors (one for goals, one for targets)
private final Set<Class<?>> randomMovementGoalClasses = new HashSet<>();
private boolean compatible = true;

public BaseMobGoalNmsHook(String wrappedGoalsFieldName, String unwrappedGoalFieldName, String mobClassName, String goalSelectorClassName, String wrappedGoalClassName, String unwrappedGoalClassName, List<String> randomMovementClassNames) {
this.wrappedGoalsFieldName = wrappedGoalsFieldName;
this.unwrappedGoalFieldName = unwrappedGoalFieldName;
this.mobClassName = mobClassName;
this.goalSelectorClassName = goalSelectorClassName;
this.wrappedGoalClassName = wrappedGoalClassName;
this.unwrappedGoalClassName = unwrappedGoalClassName;
this.randomMovementClassNames = randomMovementClassNames;
initWrappedGoalsGetter();
initGoalUnwrapper();
initWrappedGoalsGetter();
initRandomMovementGoalClasses();
}

@Override
public Object[] getGoalSelectors(Mob mob) {
Reflect mobHandle = on(mob).call("getHandle");
Object[] goalSelectors = new Object[goalSelectorFieldNames.size()];
for (int i = 0; i < goalSelectorFieldNames.size(); i++) {
goalSelectors[i] = mobHandle.get(goalSelectorFieldNames.get(i));
}
return goalSelectors;
}

@Override
public Set<Object> getWrappedGoals(Object goalSelector) {
return on(goalSelector).get(wrappedGoalsFieldName);
}

@Override
public Object unwrapGoal(Object wrappedGoal) {
return on(wrappedGoal).get(unwrappedGoalFieldName);
}

@Override
public Set<Class<?>> getRandomMovementGoalClasses() {
return randomMovementGoalClasses;
}

@Override
public boolean isCompatible() {
return compatible;
}

private void initWrappedGoalsGetter() {
try {
Class<?> entityInsentientClass = Class.forName(mobClassName);
Class<?> goalSelectorClass = Class.forName(goalSelectorClassName);
for (Field field : entityInsentientClass.getDeclaredFields()) {
if (field.getType().equals(goalSelectorClass)) {
goalSelectorFieldNames.add(field.getName());
}
}
if (goalSelectorFieldNames.isEmpty()) {
compatible = false; // found no goal selectors
return;
}
boolean foundField = false;
for (Field field : goalSelectorClass.getDeclaredFields()) {
if (field.getName().equals(wrappedGoalsFieldName)) {
if (!Set.class.isAssignableFrom(field.getType())) {
compatible = false; // found the field, but it's the wrong type
break;
}
foundField = true;
break;
}
}
if (!foundField) {
compatible = false; // couldn't find a matching field
}
} catch (ClassNotFoundException ignored) {}
}

private void initGoalUnwrapper() {
try {
Class<?> wrappedGoalClass = Class.forName(wrappedGoalClassName);
Class<?> unwrappedGoalClass = Class.forName(unwrappedGoalClassName);
boolean foundField = false;
for (Field field : wrappedGoalClass.getDeclaredFields()) {
if (field.getName().equals(unwrappedGoalFieldName)) {
if (!unwrappedGoalClass.isAssignableFrom(field.getType())) {
compatible = false; // found the field, but it's the wrong type
break;
}
foundField = true;
break;
}
}
if (!foundField) {
compatible = false; // couldn't find a matching field
}
} catch (ClassNotFoundException ignored) {}
}

private void initRandomMovementGoalClasses() {
try {
for (String className : randomMovementClassNames) {
randomMovementGoalClasses.add(Class.forName(className));
}

} catch (Exception ignored) {}
if (randomMovementGoalClasses.isEmpty()) {
compatible = false; // none of our random movement classes exist
}
}

}
Loading

0 comments on commit 34fb975

Please sign in to comment.