Skip to content

Add entity API #549

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 37 commits into
base: dev/0.8
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
44c982a
Add wandering trader config
lonevox Jun 28, 2024
b11b42b
Fix config definition
lonevox Jun 28, 2024
b9bf3bf
Merge pull request #638 from lonevox/wandering-trader-config
SirEndii Jun 28, 2024
c1abd46
Add entity API
lonevox Jan 13, 2024
c58640c
Fix method name
lonevox Jan 13, 2024
0ef4e02
Change tabs to spaces
lonevox Jan 15, 2024
4c49819
Return UUID from more functions
lonevox Feb 4, 2024
9a0ca40
Move EntityAPI access to Environment Detector
lonevox Jun 28, 2024
1d9882d
Fix rebase issues
lonevox Jun 28, 2024
59cc2eb
Make nullableValue more generic
lonevox Jun 28, 2024
ee43762
Fix another rebase issue
lonevox Jun 28, 2024
073e14d
Fixed UUID fields in entity to lua conversions
lonevox Jun 28, 2024
0cca2a2
Add lastHurtByMob to livingEntityToLua
lonevox Jun 28, 2024
bb4ce77
Merge remote-tracking branch 'upstream/dev/0.8' into 1.19.2
lonevox Jun 28, 2024
6f1097b
Fix wandering trader config
lonevox Jun 28, 2024
bea8f88
Make more fields detailed
lonevox Jun 28, 2024
492afb3
Merge remote-tracking branch 'origin/1.19.2' into 1.19.2
lonevox Jun 28, 2024
826805b
Add detailed in entityToLua
lonevox Jun 28, 2024
8538430
only provide shearable field when needed
zyxkad Jun 28, 2024
13eb097
Fix calls to entityToLua
lonevox Jun 28, 2024
4d6c080
Change UUIDs back to strings
lonevox Jun 28, 2024
b69a45d
Make hasContainerOpen require detailed to be true
lonevox Jun 28, 2024
db19c51
Replace enablePlayerUUIDFunction with enablePlayerAccess
lonevox Jun 28, 2024
e44aafe
Remove unused imports
lonevox Jun 28, 2024
762fb36
Merge remote-tracking branch 'origin/1.19.2' into 1.19.2
lonevox Jun 29, 2024
fe7a743
Keep it simple
lonevox Jun 29, 2024
de46148
Return error for player access instead of throwing
lonevox Jun 29, 2024
b351587
Fix getting entity position
lonevox Jun 29, 2024
7632c49
Simplify searchAnimals
lonevox Jun 29, 2024
b78fbee
Reintroduce id and uuid to entityToLua
lonevox Jun 29, 2024
e8566c8
Add detailed argument
lonevox Jun 29, 2024
0d200ce
Fix return
lonevox Jun 29, 2024
5141dde
Fix checkstyle violations
lonevox Jun 29, 2024
fa7e08c
Merge remote-tracking branch 'origin/1.19.2' into 1.19.2
lonevox Jun 29, 2024
a5e691c
Fix getting argument in getData
lonevox Jun 29, 2024
e930d21
Handle error when entity doesn't exist
lonevox Jun 29, 2024
f99e718
Improve error handling in EntityAPI
lonevox Jun 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package de.srendi.advancedperipherals.common.addons.computercraft.apis;

import dan200.computercraft.api.lua.*;
import dan200.computercraft.shared.util.NBTUtil;
import de.srendi.advancedperipherals.common.configuration.APConfig;
import de.srendi.advancedperipherals.common.util.EntityUtil;
import de.srendi.advancedperipherals.common.util.LuaConverter;
import de.srendi.advancedperipherals.common.util.Pair;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;

import java.util.UUID;

public class EntityAPI {

public static final EntityAPI ENTITY_API = new EntityAPI();

@LuaFunction(mainThread = true)
public final Object getNBT(IArguments arguments) throws LuaException {
if (!APConfig.API_CONFIG.enableGetNBT.get())
throw new LuaException("This function is disabled in the config. Activate it or ask an admin if they can activate it.");

Pair<Entity, MethodResult> result = validateAPICall(arguments.getString(0));
if (result.rightPresent())
return result.getRight();
Entity entity = result.getLeft();

return NBTUtil.toLua(entity.serializeNBT());
}

@LuaFunction(mainThread = true)
public final Object getName(IArguments arguments) throws LuaException {
Pair<Entity, MethodResult> result = validateAPICall(arguments.getString(0));
if (result.rightPresent())
return result.getRight();
Entity entity = result.getLeft();

return entity.getName().getString();
}

@LuaFunction(mainThread = true)
public final Object getBoundingBox(IArguments arguments) throws LuaException {
if (!APConfig.API_CONFIG.enableGetBoundingBox.get())
throw new LuaException("This function is disabled in the config. Activate it or ask an admin if they can activate it.");

Pair<Entity, MethodResult> result = validateAPICall(arguments.getString(0));
if (result.rightPresent())
return result.getRight();
Entity entity = result.getLeft();

return LuaConverter.aabbToObject(entity.getBoundingBox());
}

@LuaFunction(mainThread = true)
public final Object getPos(IArguments arguments) throws LuaException {
if (!APConfig.API_CONFIG.enableGetPos.get())
throw new LuaException("This function is disabled in the config. Activate it or ask an admin if they can activate it.");

Pair<Entity, MethodResult> result = validateAPICall(arguments.getString(0));
if (result.rightPresent())
return result.getRight();
Entity entity = result.getLeft();

return LuaConverter.vec3ToLua(entity.position());
}

/**
* Returns entity data depending on the class of the entity. Included data is either not included in the entities
* NBT data, or is especially useful so is included for convenience. Because most of this data
* doesn't overlap with NBT, it ends up mainly being values that exist only at runtime.
*/
@LuaFunction(mainThread = true)
public final Object getData(IArguments arguments) throws LuaException {
if (!APConfig.API_CONFIG.enableGetData.get())
throw new LuaException("This function is disabled in the config. Activate it or ask an admin if they can activate it.");

Pair<Entity, MethodResult> result = validateAPICall(arguments.getString(0));
if (result.rightPresent())
return result.getRight();
Entity entity = result.getLeft();

boolean detailed = arguments.count() > 1 && arguments.getBoolean(1);
return LuaConverter.completeEntityToLua(entity, detailed);
}

/**
* Returns persistent data added to entities by mods.
*/
@LuaFunction(mainThread = true)
public final Object getPersistentData(IArguments arguments) throws LuaException {
if (!APConfig.API_CONFIG.enableGetPersistentData.get())
throw new LuaException("This function is disabled in the config. Activate it or ask an admin if they can activate it.");

Pair<Entity, MethodResult> result = validateAPICall(arguments.getString(0));
if (result.rightPresent())
return result.getRight();
Entity entity = result.getLeft();

return NBTUtil.toLua(entity.getPersistentData());
}

/**
* Returns a Pair which either contains the valid Entity from the given uuidString, or an error in the form
* of a MethodResult. The other value in the Pair will always be null.
* @param uuidString The UUID in the form of a string to get an entity from.
*/
private static Pair<Entity, MethodResult> validateAPICall(String uuidString) {
UUID uuid = UUID.fromString(uuidString);
Entity entity = EntityUtil.getEntityFromUUID(uuid);
if (entity == null)
return new Pair<>(null, MethodResult.of(null, "ENTITY_DOESNT_EXIST"));
if (!APConfig.API_CONFIG.enablePlayerAccess.get() && entity instanceof Player)
return new Pair<>(null, MethodResult.of(null, "Using players in EntityAPI is disabled in the config. Activate it or ask an admin if they can activate it."));
return new Pair<>(entity, null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
import dan200.computercraft.api.pocket.IPocketAccess;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleSide;
import de.srendi.advancedperipherals.common.addons.computercraft.apis.EntityAPI;
import de.srendi.advancedperipherals.common.addons.computercraft.operations.SphereOperationContext;
import de.srendi.advancedperipherals.common.addons.computercraft.owner.BlockEntityPeripheralOwner;
import de.srendi.advancedperipherals.common.addons.computercraft.owner.IPeripheralOwner;
import de.srendi.advancedperipherals.common.addons.computercraft.owner.PocketPeripheralOwner;
import de.srendi.advancedperipherals.common.addons.computercraft.owner.TurtlePeripheralOwner;
import de.srendi.advancedperipherals.common.blocks.base.PeripheralBlockEntity;
import de.srendi.advancedperipherals.common.configuration.APConfig;
import de.srendi.advancedperipherals.common.util.LuaConverter;
import de.srendi.advancedperipherals.lib.peripherals.BasePeripheral;
import de.srendi.advancedperipherals.lib.peripherals.IPeripheralPlugin;
import net.minecraft.core.BlockPos;
Expand All @@ -40,6 +40,7 @@
import java.util.*;
import java.util.function.Function;

import static de.srendi.advancedperipherals.common.addons.computercraft.apis.EntityAPI.ENTITY_API;
import static de.srendi.advancedperipherals.common.addons.computercraft.operations.SphereOperation.SCAN_ENTITIES;

public class EnvironmentDetectorPeripheral extends BasePeripheral<IPeripheralOwner> {
Expand Down Expand Up @@ -198,16 +199,17 @@ public final boolean isSunny() {
@LuaFunction(mainThread = true)
public final MethodResult scanEntities(@NotNull IComputerAccess access, @NotNull IArguments arguments) throws LuaException {
int radius = arguments.getInt(0);
boolean detailed = arguments.count() > 1 ? arguments.getBoolean(1) : false;
return withOperation(SCAN_ENTITIES, new SphereOperationContext(radius), context -> {
if (radius > SCAN_ENTITIES.getMaxCostRadius())
return MethodResult.of(null, "Radius exceeds max value");
return null;
}, context -> {
BlockPos pos = owner.getPos();
AABB box = new AABB(pos);
List<Map<String, Object>> entities = getLevel().getEntities((Entity) null, box.inflate(radius), entity -> entity instanceof LivingEntity && entity.isAlive()).stream().map(entity -> LuaConverter.completeEntityWithPositionToLua(entity, pos, detailed)).toList();
return MethodResult.of(entities);
List<String> entityUUIDs = getLevel().getEntities((Entity) null, box.inflate(radius), LivingEntity.class::isInstance).stream()
.map(entity -> entity.getUUID().toString())
.toList();
return MethodResult.of(entityUUIDs);
}, null);
}

Expand Down Expand Up @@ -243,4 +245,9 @@ public final MethodResult canSleepPlayer(String playername) {
return MethodResult.of(canContinueSleep == Event.Result.ALLOW);
}
}

@LuaFunction
public final EntityAPI getEntityAPI() {
return ENTITY_API;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ public final boolean isPlayerInRange(int range, String username) {

@LuaFunction(value = {"getPlayerPos", "getPlayer"}, mainThread = true)
public final Map<String, Object> getPlayerPos(IArguments arguments) throws LuaException {
if (!APConfig.PERIPHERALS_CONFIG.playerSpy.get())
if (!APConfig.PERIPHERALS_CONFIG.enablePlayerPosFunction.get())
throw new LuaException("This function is disabled in the config. Activate it or ask an admin if he can activate it.");
ResourceKey<Level> dimension = getLevel().dimension();

Expand Down Expand Up @@ -271,6 +271,19 @@ public final Map<String, Object> getPlayerPos(IArguments arguments) throws LuaEx
return info;
}

@LuaFunction(mainThread = true)
public final String getPlayerUUID(String username) throws LuaException {
ResourceKey<Level> dimension = getLevel().dimension();

for (Player player : getPlayers()) {
if (!isAllowedMultiDimensional() && player.getLevel().dimension() != dimension)
continue;
if(player.getName().getString().equals(username))
return player.getUUID().toString();
}
return null;
}

private List<ServerPlayer> getPlayers() {
return ServerLifecycleHooks.getCurrentServer().getPlayerList().getPlayers();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ public final MethodResult searchAnimals(IArguments args) throws LuaException {
BlockPos currentPos = owner.getPos();
AABB box = new AABB(currentPos);
ItemStack itemInHand = owner.getToolInMainHand();
List<Map<String, Object>> entities = owner.getLevel().getEntities((Entity) null, box.inflate(automataCore.getInteractionRadius()), suitableEntity).stream().map(entity -> LuaConverter.completeEntityWithPositionToLua(entity, itemInHand, currentPos, detailed)).toList();
List<Map<String, Object>> entities = owner.getLevel().getEntities((Entity) null, box.inflate(automataCore.getInteractionRadius()), suitableEntity).stream()
.map(entity -> LuaConverter.completeEntityWithRelativePositionToLua(entity, itemInHand, currentPos, detailed))
.toList();
return MethodResult.of(entities);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class APConfig extends ModConfig {
public static final PeripheralsConfig PERIPHERALS_CONFIG = new PeripheralsConfig();
public static final MetaphysicsConfig METAPHYSICS_CONFIG = new MetaphysicsConfig();
public static final WorldConfig WORLD_CONFIG = new WorldConfig();
public static final APIConfig API_CONFIG = new APIConfig();

public APConfig(IAPConfig config, ModContainer container) {
super(config.getType(), config.getConfigSpec(), container, "Advancedperipherals/" + config.getFileName() + ".toml");
Expand All @@ -32,6 +33,7 @@ public static void register(ModLoadingContext context) {
modContainer.addConfig(new APConfig(PERIPHERALS_CONFIG, modContainer));
modContainer.addConfig(new APConfig(METAPHYSICS_CONFIG, modContainer));
modContainer.addConfig(new APConfig(WORLD_CONFIG, modContainer));
modContainer.addConfig(new APConfig(API_CONFIG, modContainer));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package de.srendi.advancedperipherals.common.configuration;

import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.fml.config.ModConfig;

public class APIConfig implements IAPConfig {
//Entity
public final ForgeConfigSpec.BooleanValue enableEntityAPI;
public final ForgeConfigSpec.BooleanValue enableGetNBT;
public final ForgeConfigSpec.BooleanValue enableGetBoundingBox;
public final ForgeConfigSpec.BooleanValue enableGetPos;
public final ForgeConfigSpec.BooleanValue enableGetData;
public final ForgeConfigSpec.BooleanValue enableGetPersistentData;
public final ForgeConfigSpec.BooleanValue enablePlayerAccess;
private final ForgeConfigSpec configSpec;

public APIConfig() {
ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder();

builder.comment("APIs config").push("APIs");

builder.push("entity");

enableEntityAPI = builder.comment("Enables the \"entity\" API").define("enableEntityAPI", true);
enableGetNBT = builder.comment("Activates the \"getNBT\" function of the entity API").define("enableGetNBT", true);
enableGetBoundingBox = builder.comment("Activates the \"getBoundingBox\" function of the entity API").define("enableGetBoundingBox", true);
enableGetPos = builder.comment("Activates the \"getPos\" function of the entity API").define("enableGetPos", true);
enableGetData = builder.comment("Activates the \"getData\" function of the entity API").define("enableGetData", true);
enableGetPersistentData = builder.comment("Activates the \"getPersistentData\" function of the entity API").define("enableGetPersistentData", true);
enablePlayerAccess = builder.comment("Allows player entities to be used with the EntityAPI.").define("enablePlayerAccess", true);

builder.pop();

configSpec = builder.build();
}

@Override
public ForgeConfigSpec getConfigSpec() {
return configSpec;
}

@Override
public String getFileName() {
return "APIs";
}

@Override
public ModConfig.Type getType() {
return ModConfig.Type.COMMON;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class PeripheralsConfig implements IAPConfig {

// Player Detector
public final ForgeConfigSpec.IntValue playerDetMaxRange;
public final ForgeConfigSpec.BooleanValue playerSpy;
public final ForgeConfigSpec.BooleanValue enablePlayerPosFunction;
public final ForgeConfigSpec.BooleanValue morePlayerInformation;
public final ForgeConfigSpec.BooleanValue enablePlayerDetector;
public final ForgeConfigSpec.BooleanValue playerDetMultiDimensional;
Expand Down Expand Up @@ -136,7 +136,7 @@ public PeripheralsConfig() {

enablePlayerDetector = builder.comment("Enable the Player Detector or not.").define("enablePlayerDetector", true);
playerDetMaxRange = builder.comment("The max range of the player detector functions. If anyone use a higher range, the detector will use this max range. -1 for unlimited").defineInRange("playerDetMaxRange", -1, -1, Integer.MAX_VALUE);
playerSpy = builder.comment("Activates the \"getPlayerPos\" function of the Player Detector").define("enablePlayerPosFunction", true);
enablePlayerPosFunction = builder.comment("Activates the \"getPlayerPos\" function of the Player Detector").define("enablePlayerPosFunction", true);
morePlayerInformation = builder.comment("Adds more information to `getPlayerPos` of the Player Detector. Like rotation and dimension").define("morePlayerInformation", true);
playerDetMultiDimensional = builder.comment("If true, the player detector can observe players which aren't in the same dimension as the detector itself. `playerDetMaxRange` needs to be infinite(-1) for it to work.").define("chatBoxMultiDimensional", true);
playerSpyRandError = builder.comment("If true, add random error to `getPlayerPos` player position that varies based on how far the player is from the detector. Prevents getting the exact position of players far from the detector.").define("enablePlayerPosRandomError", false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class WorldConfig implements IAPConfig {
public final ForgeConfigSpec.BooleanValue enableVillagerStructures;
public final ForgeConfigSpec.BooleanValue givePlayerBookOnJoin;
public final ForgeConfigSpec.IntValue villagerStructureWeight;
public final ForgeConfigSpec.BooleanValue enableWanderingTraderTrades;
private final ForgeConfigSpec configSpec;

public WorldConfig() {
Expand All @@ -20,6 +21,7 @@ public WorldConfig() {
enableVillagerStructures = builder.comment("Enable the villager structures for the computer scientist.").define("enableVillagerStructures", true);
givePlayerBookOnJoin = builder.comment("Gives the ap documentation to new players.").define("givePlayerBookOnJoin", true);
villagerStructureWeight = builder.comment("The weight of the villager structures.").defineInRange("villagerStructureWeight", 10, 0, 16000);
enableWanderingTraderTrades = builder.comment("Enable new wandering trader trades.").define("enableWanderingTraderTrades", true);

builder.pop();
configSpec = builder.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package de.srendi.advancedperipherals.common.util;

import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraftforge.server.ServerLifecycleHooks;

import java.util.UUID;

public class EntityUtil {
/**
* Searches all levels for an {@link Entity} with the given {@link UUID}.
* @param uuid the unique identifier of the {@code Entity}
* @return an {@code Entity} that has the given {@code UUID} if one is found, null otherwise
*/
public static Entity getEntityFromUUID(UUID uuid) {
for (ServerLevel level : ServerLifecycleHooks.getCurrentServer().getAllLevels()) {
Entity entity = level.getEntity(uuid);
if (entity != null)
return entity;
}
return null;
}
}
Loading
Loading