Listening in both directions

This commit is contained in:
skippyall
2026-01-25 22:28:09 +01:00
parent 382b394523
commit 017b2ec590
44 changed files with 550 additions and 211 deletions
@@ -1,7 +1,7 @@
package io.github.skippyall.minions.client;
import eu.pb4.polymer.networking.api.client.PolymerClientNetworking;
import io.github.skippyall.minions.MinionBlocks;
import io.github.skippyall.minions.registration.MinionBlocks;
import io.github.skippyall.minions.util.PolymerUtil;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.rendering.v1.BlockRenderLayerMap;
@@ -12,6 +12,11 @@ import io.github.skippyall.minions.program.instruction.Instructions;
import io.github.skippyall.minions.program.supplier.ValueSuppliers;
import io.github.skippyall.minions.program.value.ValueTypes;
import io.github.skippyall.minions.reference.Reference;
import io.github.skippyall.minions.registration.MinionBlocks;
import io.github.skippyall.minions.registration.MinionCreativeTab;
import io.github.skippyall.minions.registration.MinionItems;
import io.github.skippyall.minions.registration.MinionRegistration;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.util.PolymerUtil;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
@@ -41,12 +46,12 @@ public class Minions implements ModInitializer {
MinionBlocks.register();
MinionItems.register();
MinionCreativeTab.registerGroup();
MinionRegistration.register();
PolymerUtil.register();
ServerLifecycleEvents.SERVER_STARTED.register(server -> {
MinionPersistentState.create(server);
MinionPersistentState.INSTANCE.getMinionData().forEach((uuid, data) -> {
MinionPersistentState.get(server).getMinionData().forEach((uuid, data) -> {
if(data.isSpawned()) {
MinionFakePlayer.spawnMinion(data, server.getOverworld(), null, null, true);
}
@@ -57,6 +62,12 @@ public class Minions implements ModInitializer {
MinionsCommand.register(commandDispatcher);
});
/*ServerBlockEntityEvents.BLOCK_ENTITY_LOAD.register((blockEntity, world) -> {
if(blockEntity instanceof MinionTriggerBlockEntity) {
world.updateComparators(blockEntity.getPos(), MinionBlocks.MINION_TRIGGER_BLOCK);
}
});*/
PolymerResourcePackUtils.addModAssets(Minions.MOD_ID);
}
}
@@ -0,0 +1,55 @@
package io.github.skippyall.minions.block;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.program.instruction.ConfiguredInstructionListener;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.registry.RegistryKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import java.util.Map;
import java.util.UUID;
public abstract class BlockEntityMinionInstructionListener<E extends BlockEntity> extends BlockEntityMinionListener<E> {
protected BlockEntityMinionInstructionListener(RegistryKey<World> worldKey, BlockPos pos, UUID minionUuid, BlockEntityType<E> type) {
super(worldKey, pos, minionUuid, type);
}
protected abstract Map<String, ConfiguredInstructionListener> getInstructionListeners();
@Override
public void onMinionSpawn(MinionFakePlayer minion) {
super.onMinionSpawn(minion);
registerInstructionListeners();
}
@Override
protected void add(MinecraftServer server) {
super.add(server);
if(minion != null) {
registerInstructionListeners();
}
}
@Override
public void remove(MinecraftServer server) {
super.remove(server);
if(minion != null) {
removeInstructionListeners();
}
}
public void registerInstructionListeners() {
for(Map.Entry<String, ConfiguredInstructionListener> listener : getInstructionListeners().entrySet()) {
minion.getInstructionManager().getInstruction(listener.getKey()).addListener(listener.getValue());
}
}
public void removeInstructionListeners() {
for(Map.Entry<String, ConfiguredInstructionListener> listener : getInstructionListeners().entrySet()) {
minion.getInstructionManager().getInstruction(listener.getKey()).removeListener(listener.getValue());
}
}
}
@@ -1,36 +1,67 @@
package io.github.skippyall.minions.block;
import com.mojang.datafixers.util.Function3;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.github.skippyall.minions.minion.MinionListener;
import io.github.skippyall.minions.minion.MinionPersistentState;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.registry.RegistryKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.Uuids;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import java.util.UUID;
import java.util.function.BiFunction;
public abstract class BlockEntityMinionListener<E extends BlockEntity> implements MinionListener {
protected RegistryKey<World> worldKey;
protected BlockPos pos;
protected UUID minionUuid;
protected BlockEntityType<E> type;
protected @Nullable MinionFakePlayer minion;
public BlockEntityMinionListener(RegistryKey<World> worldKey, BlockPos pos, BlockEntityType<E> type) {
protected BlockEntityMinionListener(RegistryKey<World> worldKey, BlockPos pos, UUID minionUuid, BlockEntityType<E> type) {
this.worldKey = worldKey;
this.pos = pos;
this.minionUuid = minionUuid;
this.type = type;
}
public static <L extends BlockEntityMinionListener<?>> Codec<L> getCodec(BiFunction<RegistryKey<World>, BlockPos, L> constructor) {
@Override
public void onMinionSpawn(MinionFakePlayer minion) {
MinionListener.super.onMinionSpawn(minion);
this.minion = minion;
removeIfBeRemoved(minion.getServer());
}
@Override
public void onMinionRemove(MinionFakePlayer minion) {
MinionListener.super.onMinionRemove(minion);
this.minion = null;
}
public static <T extends BlockEntityMinionListener<?>> T getListener(World world, BlockPos pos, UUID minionUuid, Class<T> clazz) {
if(minionUuid != null) {
for (MinionListener listener : MinionPersistentState.get(world.getServer()).getMinionData(minionUuid).listeners()) {
if (listener instanceof BlockEntityMinionListener<?> tl && tl.pos.equals(pos) && tl.worldKey.equals(world.getRegistryKey()) && clazz.isInstance(tl)) {
return clazz.cast(tl);
}
}
}
return null;
}
public static <L extends BlockEntityMinionListener<?>> Codec<L> getCodec(Function3<RegistryKey<World>, BlockPos, UUID, L> constructor) {
return RecordCodecBuilder.create(instance ->
instance.group(
World.CODEC.fieldOf("world").forGetter(listener -> listener.worldKey),
BlockPos.CODEC.fieldOf("pos").forGetter(listener -> listener.pos)
BlockPos.CODEC.fieldOf("pos").forGetter(listener -> listener.pos),
Uuids.CODEC.fieldOf("minionUuid").forGetter(listener -> listener.minionUuid)
).apply(instance, constructor));
}
@@ -55,22 +86,23 @@ public abstract class BlockEntityMinionListener<E extends BlockEntity> implement
return Optional.empty();
}
public boolean removeIfBeRemoved(MinecraftServer server, UUID minion) {
public boolean removeIfBeRemoved(MinecraftServer server) {
if(getBlockEntityState(server) == BlockEntityState.REMOVED) {
remove(minion);
remove(server);
return true;
}
return false;
}
public void add(UUID minion) {
MinionPersistentState.INSTANCE.getMinionData(minion).listeners().addListener(this);
MinionPersistentState.INSTANCE.markDirty();
protected void add(MinecraftServer server) {
MinionPersistentState.get(server).getMinionData(minionUuid).listeners().addListener(this);
MinionPersistentState.get(server).markDirty();
this.minion = (MinionFakePlayer) server.getPlayerManager().getPlayer(minionUuid);
}
public void remove(UUID minion) {
MinionPersistentState.INSTANCE.getMinionData(minion).listeners().removeListener(this);
MinionPersistentState.INSTANCE.markDirty();
public void remove(MinecraftServer server) {
MinionPersistentState.get(server).getMinionData(minionUuid).listeners().removeListener(this);
MinionPersistentState.get(server).markDirty();
}
public enum BlockEntityState {
@@ -1,32 +0,0 @@
package io.github.skippyall.minions.block;
import io.github.skippyall.minions.MinionBlocks;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.program.instruction.ConfiguredInstructionListener;
import net.minecraft.registry.RegistryKey;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import java.util.UUID;
public class MinionTriggerMinionListener extends BlockEntityMinionListener<MinionTriggerBlockEntity> implements ConfiguredInstructionListener {
boolean registeredListener;
String instructionName;
public MinionTriggerMinionListener(RegistryKey<World> worldKey, BlockPos pos, String instructionName, boolean registeredListener) {
super(worldKey, pos, MinionBlocks.MINION_TRIGGER_BE_TYPE);
this.instructionName = instructionName;
this.registeredListener = registeredListener;
}
@Override
public void onMinionSpawn(MinionFakePlayer minion) {
if(!registeredListener) {
minion.getInstructionManager().getInstruction(instructionName).addListener(this);
}
}
@Override
public void onMinionRemove(MinionFakePlayer minion) {
}
}
@@ -1,4 +1,4 @@
package io.github.skippyall.minions.block;
package io.github.skippyall.minions.block.miniontrigger;
import com.mojang.serialization.MapCodec;
import eu.pb4.polymer.core.api.block.PolymerBlock;
@@ -8,7 +8,7 @@ import eu.pb4.polymer.resourcepack.api.PolymerResourcePackUtils;
import eu.pb4.polymer.virtualentity.api.BlockWithElementHolder;
import eu.pb4.polymer.virtualentity.api.ElementHolder;
import eu.pb4.polymer.virtualentity.api.elements.ItemDisplayElement;
import io.github.skippyall.minions.MinionBlocks;
import io.github.skippyall.minions.registration.MinionBlocks;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.minion.MinionPersistentState;
import io.github.skippyall.minions.reference.InstructionReference;
@@ -22,8 +22,6 @@ import net.minecraft.block.Blocks;
import net.minecraft.block.ShapeContext;
import net.minecraft.block.SideShapeType;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityTicker;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
@@ -75,6 +73,12 @@ public class MinionTriggerBlock extends BlockWithEntity implements PolymerBlock,
return state.isSideSolid(world, pos, Direction.UP, SideShapeType.RIGID);
}
@Override
protected void onStateReplaced(BlockState state, ServerWorld world, BlockPos pos, boolean moved) {
super.onStateReplaced(state, world, pos, moved);
world.getBlockEntity(pos, MinionBlocks.MINION_TRIGGER_BE_TYPE).ifPresent(MinionTriggerBlockEntity::removeListener);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
builder.add(POWERED);
@@ -101,8 +105,7 @@ public class MinionTriggerBlock extends BlockWithEntity implements PolymerBlock,
}
world.getBlockEntity(pos, MinionBlocks.MINION_TRIGGER_BE_TYPE).ifPresent(be -> {
String name = MinionPersistentState.INSTANCE.getMinionData(be.getMinionUuid()).name();
String name = MinionPersistentState.get(world.getServer()).getMinionData(be.getMinionUuid()).name();
player.sendMessage(Text.translatable("minions.reference.instruction.tooltip", name, be.getInstructionName()), true);
});
return ActionResult.SUCCESS;
@@ -113,6 +116,7 @@ public class MinionTriggerBlock extends BlockWithEntity implements PolymerBlock,
if(!canPlaceAt(state, world, pos)) {
dropStacks(state, world, pos);
world.removeBlock(pos, false);
return;
}
boolean newPower = world.isReceivingRedstonePower(pos);
@@ -147,15 +151,6 @@ public class MinionTriggerBlock extends BlockWithEntity implements PolymerBlock,
return PolymerUtil.isOnClient(context) ? state : Blocks.COMPARATOR.getDefaultState().with(AbstractRedstoneGateBlock.POWERED, state.get(POWERED));
}
@Override
public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(World world, BlockState state, BlockEntityType<T> type) {
if(type == MinionBlocks.MINION_TRIGGER_BE_TYPE) {
return MinionTriggerBlockEntity::tick;
} else {
return null;
}
}
@Override
public @Nullable ElementHolder createElementHolder(ServerWorld world, BlockPos pos, BlockState initialBlockState) {
ElementHolder holder = new ElementHolder() {
@@ -1,7 +1,7 @@
package io.github.skippyall.minions.block;
package io.github.skippyall.minions.block.miniontrigger;
import io.github.skippyall.minions.MinionBlocks;
import io.github.skippyall.minions.minion.MinionPersistentState;
import io.github.skippyall.minions.block.BlockEntityMinionListener;
import io.github.skippyall.minions.registration.MinionBlocks;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.program.instruction.ConfiguredInstruction;
@@ -11,7 +11,6 @@ import net.minecraft.storage.ReadView;
import net.minecraft.storage.WriteView;
import net.minecraft.util.Uuids;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import java.util.Optional;
import java.util.UUID;
@@ -20,38 +19,34 @@ public class MinionTriggerBlockEntity extends BlockEntity {
private UUID minionUuid;
private String instructionName = "";
private boolean first = true;
private boolean runningCache = false;
public MinionTriggerBlockEntity(BlockPos pos, BlockState state) {
super(MinionBlocks.MINION_TRIGGER_BE_TYPE, pos, state);
}
public void setInstruction(UUID minionUuid, String instructionName) {
this.minionUuid = minionUuid;
this.instructionName = instructionName;
markDirty();
public void removeListener() {
MinionTriggerMinionListener.removeListener(world, pos, minionUuid, instructionName);
}
public static void tick(World world, BlockPos pos, BlockState state, BlockEntity blockEntity) {
if(!(blockEntity instanceof MinionTriggerBlockEntity triggerBlockEntity)) {
return;
}
if(triggerBlockEntity.first) {
triggerBlockEntity.first = false;
world.updateComparators(pos, MinionBlocks.MINION_TRIGGER_BLOCK);
triggerBlockEntity.runningCache = triggerBlockEntity.getInstruction().map(ConfiguredInstruction::isRunning).orElse(false);
} else {
boolean isRunning = triggerBlockEntity.getInstruction().map(ConfiguredInstruction::isRunning).orElse(false);
if (isRunning != triggerBlockEntity.runningCache) {
world.updateComparators(pos, MinionBlocks.MINION_TRIGGER_BLOCK);
triggerBlockEntity.runningCache = isRunning;
}
}
public void addListener() {
MinionTriggerMinionListener.addListener(world, pos, minionUuid, instructionName);
}
public void setInstruction(UUID minionUuid, String instructionName) {
removeListener();
this.minionUuid = minionUuid;
this.instructionName = instructionName;
addListener();
markDirty();
}
public void updatePower() {
boolean powered = getCachedState().get(MinionTriggerBlock.POWERED);
MinionTriggerMinionListener listener = getListener();
if(listener != null) {
listener.incomingPowerCache = powered;
}
getMinion().ifPresent(minion -> {
getInstruction().ifPresent(instruction -> {
if(powered) {
@@ -61,12 +56,11 @@ public class MinionTriggerBlockEntity extends BlockEntity {
}
});
});
}
public int getComparatorOutput() {
Optional<ConfiguredInstruction<MinionRuntime>> instruction = getInstruction();
if(instruction.isPresent() && instruction.get().isRunning()) {
MinionTriggerMinionListener listener = getListener();
if(listener != null && listener.runningCache) {
return 15;
}
return 0;
@@ -95,6 +89,10 @@ public class MinionTriggerBlockEntity extends BlockEntity {
return getMinion().flatMap(this::getInstruction);
}
public MinionTriggerMinionListener getListener() {
return BlockEntityMinionListener.getListener(world, pos, minionUuid, MinionTriggerMinionListener.class);
}
@Override
protected void readData(ReadView view) {
minionUuid = view.read("minionUuid", Uuids.CODEC).orElse(null);
@@ -1,4 +1,4 @@
package io.github.skippyall.minions.block;
package io.github.skippyall.minions.block.miniontrigger;
import eu.pb4.polymer.core.api.item.PolymerBlockItem;
import eu.pb4.polymer.resourcepack.api.PolymerResourcePackUtils;
@@ -0,0 +1,126 @@
package io.github.skippyall.minions.block.miniontrigger;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.block.BlockEntityMinionInstructionListener;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.registration.MinionBlocks;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.program.instruction.ConfiguredInstruction;
import io.github.skippyall.minions.program.instruction.ConfiguredInstructionListener;
import net.minecraft.registry.RegistryKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.Identifier;
import net.minecraft.util.Uuids;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
public class MinionTriggerMinionListener extends BlockEntityMinionInstructionListener<MinionTriggerBlockEntity> {
public static final Codec<MinionTriggerMinionListener> CODEC = RecordCodecBuilder.create(instance ->
instance.group(
World.CODEC.fieldOf("world").forGetter(listener -> listener.worldKey),
BlockPos.CODEC.fieldOf("pos").forGetter(listener -> listener.pos),
Uuids.CODEC.fieldOf("minionUuid").forGetter(listener -> listener.minionUuid),
Codec.STRING.fieldOf("instructionName").forGetter(listener -> listener.instructionName)
).apply(instance, MinionTriggerMinionListener::new));
String instructionName;
final TriggerInstructionListener listener = new TriggerInstructionListener();
boolean runningCache;
boolean incomingPowerCache;
private MinionTriggerMinionListener(RegistryKey<World> worldKey, BlockPos pos, UUID minionUuid, String instructionName) {
super(worldKey, pos, minionUuid, MinionBlocks.MINION_TRIGGER_BE_TYPE);
this.instructionName = Objects.requireNonNull(instructionName);
}
public static void addListener(World world, BlockPos pos, UUID minion, String instructionName) {
MinionTriggerMinionListener listener = new MinionTriggerMinionListener(world.getRegistryKey(), pos, minion, instructionName);
listener.add(world.getServer());
}
public static void removeListener(World world, BlockPos pos, UUID minion, String instructionName) {
MinionTriggerMinionListener old = getListener(world, pos, minion, MinionTriggerMinionListener.class);
if(old != null) {
old.remove(world.getServer());
}
}
@Override
protected Map<String, ConfiguredInstructionListener> getInstructionListeners() {
return Map.of(instructionName, listener);
}
@Override
public void onMinionSpawn(MinionFakePlayer minion) {
super.onMinionSpawn(minion);
runningCache = minion.getInstructionManager().getInstruction(instructionName).isRunning();
updateComparatorsIfLoaded(minion.getServer());
ConfiguredInstruction<MinionRuntime> instruction = minion.getInstructionManager().getInstruction(instructionName);
if(instruction.isRunning() && !incomingPowerCache) {
instruction.stop(minion.getInstructionManager());
} else if (!instruction.isRunning() && incomingPowerCache) {
instruction.run(minion.getInstructionManager());
}
}
@Override
public void onMinionRemove(MinionFakePlayer minion) {
super.onMinionRemove(minion);
runningCache = false;
updateComparatorsIfLoaded(minion.getServer());
}
@Override
public void onInstructionRename(MinionFakePlayer minion, ConfiguredInstruction<?> instruction, String oldName, String newName) {
super.onInstructionRename(minion, instruction, oldName, newName);
if(instructionName.equals(oldName)) {
instructionName = newName;
}
}
@Override
protected void add(MinecraftServer server) {
super.add(server);
runningCache = minion.getInstructionManager().getInstruction(instructionName).isRunning();
updateComparatorsIfLoaded(server);
}
@Override
public Optional<Identifier> getCodecId() {
return Optional.of(Identifier.of(Minions.MOD_ID, "minion_trigger"));
}
public void updateComparatorsIfLoaded(MinecraftServer server) {
World world = server.getWorld(worldKey);
if(world.isPosLoaded(pos)) {
world.updateComparators(pos, MinionBlocks.MINION_TRIGGER_BLOCK);
}
}
public boolean isRunning() {
return runningCache;
}
public class TriggerInstructionListener implements ConfiguredInstructionListener {
@Override
public void onRun(ConfiguredInstruction<?> instruction) {
runningCache = true;
updateComparatorsIfLoaded(minion.getServer());
}
@Override
public void onStop(ConfiguredInstruction<?> instruction) {
runningCache = false;
updateComparatorsIfLoaded(minion.getServer());
}
}
}
@@ -16,7 +16,7 @@ public class ListSubcommand {
.executes(ListSubcommand::list);
public static int list(CommandContext<ServerCommandSource> context) {
Collection<MinionData> minions = MinionPersistentState.INSTANCE.getMinionData().values();
Collection<MinionData> minions = MinionPersistentState.get(context.getSource().getServer()).getMinionData().values();
for (MinionData minion : minions) {
context.getSource().sendFeedback(() -> Text.literal(minion.name() + "(" + minion.uuid() + "):" + minion.isSpawned()), false);
}
@@ -9,6 +9,7 @@ import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import io.github.skippyall.minions.minion.MinionData;
import io.github.skippyall.minions.minion.MinionPersistentState;
import io.github.skippyall.minions.minion.MinionProfileUtils;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.Text;
@@ -21,13 +22,13 @@ public class MinionArgument {
public static final MinionSuggestionProvider SUGGESTION_PROVIDER = new MinionSuggestionProvider();
public static MinionData parse(String argument) throws CommandSyntaxException {
public static MinionData parse(MinecraftServer server, String argument) throws CommandSyntaxException {
Optional<MinionData> data = Optional.empty();
if(argument.startsWith(MinionProfileUtils.PREFIX)) {
data = MinionPersistentState.INSTANCE.getMinionWithName(argument);
data = MinionPersistentState.get(server).getMinionWithName(argument);
} else {
try {
data = Optional.ofNullable(MinionPersistentState.INSTANCE.getMinionData(UUID.fromString(argument)));
data = Optional.ofNullable(MinionPersistentState.get(server).getMinionData(UUID.fromString(argument)));
} catch (IllegalArgumentException ignored) {}
}
@@ -40,7 +41,7 @@ public class MinionArgument {
public static class MinionSuggestionProvider implements SuggestionProvider<ServerCommandSource> {
@Override
public CompletableFuture<Suggestions> getSuggestions(CommandContext<ServerCommandSource> context, SuggestionsBuilder builder) throws CommandSyntaxException {
for (MinionData data : MinionPersistentState.INSTANCE.getMinionDataList()) {
for (MinionData data : MinionPersistentState.get(context.getSource().getServer()).getMinionDataList()) {
builder.suggest(data.name());
}
@@ -48,7 +48,7 @@ public class SpawnSubcommand {
);
public static int spawnCommand(ServerCommandSource source, String minion, PosArgument pos, boolean force) throws CommandSyntaxException {
MinionData data = MinionArgument.parse(minion);
MinionData data = MinionArgument.parse(source.getServer(), minion);
MinionFakePlayer.spawnMinion(data, source.getWorld(), pos != null ? pos.getPos(source) : null, pos != null ? pos.getRotation(source) : null, force);
return 0;
}
@@ -70,7 +70,7 @@ public class ConfigureInstructionGui extends InstructionBoundSimpleGui {
}
@Override
public void onInstructionRename(MinionFakePlayer minion, ConfiguredInstruction<?> instruction, String newName) {
public void onInstructionRename(MinionFakePlayer minion, ConfiguredInstruction<?> instruction, String oldName, String newName) {
this.setTitle(Text.literal(newName));
name = newName;
}
@@ -2,7 +2,7 @@ package io.github.skippyall.minions.gui;
import com.mojang.authlib.properties.PropertyMap;
import com.mojang.serialization.Codec;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.util.TranslationUtil;
import it.unimi.dsi.fastutil.objects.ReferenceSortedSets;
@@ -15,7 +15,6 @@ import net.minecraft.item.Items;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.util.Rarity;
@@ -2,7 +2,7 @@ package io.github.skippyall.minions.gui;
import eu.pb4.sgui.api.elements.GuiElementBuilder;
import eu.pb4.sgui.api.gui.SimpleGui;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.gui.input.Result;
import io.github.skippyall.minions.gui.input.TextInput;
import io.github.skippyall.minions.minion.MinionRuntime;
@@ -164,6 +164,7 @@ public class InstructionGui {
if (!future.isDone()) {
future.cancel(false);
}
super.onClose();
}
};
gui.setTitle(Text.translatable("minions.gui.instruction.select_instruction"));
@@ -193,6 +194,7 @@ public class InstructionGui {
if (!future.isDone()) {
future.cancel(false);
}
super.onClose();
}
};
gui.setTitle(Text.translatable("minions.gui.instruction.select_instruction"));
@@ -2,7 +2,7 @@ package io.github.skippyall.minions.gui;
import eu.pb4.sgui.api.elements.GuiElementBuilder;
import eu.pb4.sgui.api.gui.SimpleGui;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.gui.input.TextInput;
import io.github.skippyall.minions.minion.MinionData;
import io.github.skippyall.minions.minion.MinionItem;
@@ -49,9 +49,9 @@ public class MinionLookGui extends SimpleGui {
GuiElementBuilder builder = new GuiElementBuilder()
.setItem(Items.PLAYER_HEAD)
.setCallback(() -> currentSkinProvider.openSkinMenu(player).thenAccept(skin -> {
MinionItem.setData(getData().withSkin(skin), minionItem);
MinionItem.setData(player.getServer(), getData().withSkin(skin), minionItem);
}));
if(MinionItem.getData(minionItem) != null && MinionItem.getData(minionItem).skin().isPresent()) {
if(MinionItem.getData(player.getServer(), minionItem) != null && MinionItem.getData(player.getServer(), minionItem).skin().isPresent()) {
builder.setComponent(DataComponentTypes.PROFILE, new ProfileComponent(Optional.empty(), Optional.empty(), getData().skin().get()));
}
setSlot(16, builder);
@@ -77,7 +77,7 @@ public class MinionLookGui extends SimpleGui {
}
private MinionData getData() {
return MinionItem.getDataOrDefault(minionItem);
return MinionItem.getDataOrDefault(player.getServer(), minionItem);
}
public static void open(ServerPlayerEntity player, ItemStack minionItem) {
@@ -87,9 +87,9 @@ public class MinionLookGui extends SimpleGui {
}
public void openRenameGui(ServerPlayerEntity player, ItemStack minionItem) {
TextInput.inputSync(player, Text.translatable("minions.gui.look.rename.title"), "Minion", MinionProfileUtils::checkMinionNameWithoutPrefix)
TextInput.inputSync(player, Text.translatable("minions.gui.look.rename.title"), "Minion", name -> MinionProfileUtils.checkMinionNameWithoutPrefix(player.getServer(), name))
.thenAccept(name -> {
MinionItem.setData(getData().withName(MinionProfileUtils.PREFIX + name), minionItem);
MinionItem.setData(player.getServer(), getData().withName(MinionProfileUtils.PREFIX + name), minionItem);
open();
});
}
@@ -1,22 +1,19 @@
package io.github.skippyall.minions.minion;
import com.mojang.authlib.properties.PropertyMap;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import eu.pb4.polymer.core.api.other.PolymerComponent;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.util.SerializableListenerManager;
import net.minecraft.component.ComponentType;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtOps;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.Identifier;
import net.minecraft.util.Uuids;
import net.minecraft.util.dynamic.Codecs;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import java.util.UUID;
@@ -28,14 +25,17 @@ public record MinionData(UUID uuid, String name, Optional<PropertyMap> skin, boo
Codec.STRING.fieldOf("name").forGetter(MinionData::name),
Codecs.GAME_PROFILE_PROPERTY_MAP.optionalFieldOf("skin").forGetter(MinionData::skin),
Codec.BOOL.optionalFieldOf("isSpawned", false).forGetter(MinionData::isSpawned),
SerializableListenerManager.getCodec(MinionRegistries.MINION_LISTENER_CODECS).optionalFieldOf("listeners", new SerializableListenerManager<>(MinionRegistries.MINION_LISTENER_CODECS)).forGetter(MinionData::listeners)
SerializableListenerManager.getCodec(MinionRegistries.MINION_LISTENER_CODECS).optionalFieldOf("listeners").xmap(
optional -> optional.orElseGet(() -> new SerializableListenerManager<>(MinionRegistries.MINION_LISTENER_CODECS)),
Optional::of
).forGetter(MinionData::listeners)
).apply(instance, MinionData::new)
);
public static final ComponentType<UUID> COMPONENT = Registry.register(Registries.DATA_COMPONENT_TYPE, Identifier.of(Minions.MOD_ID, "minion_data"), ComponentType.<UUID>builder().codec(Uuids.CODEC).build());
public static MinionData createDefault() {
return new MinionData(UUID.randomUUID(), MinionProfileUtils.newDefaultMinionName(), Optional.empty(), false, new SerializableListenerManager<>(MinionRegistries.MINION_LISTENER_CODECS));
public static MinionData createDefault(MinecraftServer server) {
return new MinionData(UUID.randomUUID(), MinionProfileUtils.newDefaultMinionName(server), Optional.empty(), false, new SerializableListenerManager<>(MinionRegistries.MINION_LISTENER_CODECS));
}
public MinionData withName(String name) {
@@ -50,14 +50,6 @@ public record MinionData(UUID uuid, String name, Optional<PropertyMap> skin, boo
return new MinionData(uuid, name, skin, isSpawned, listeners);
}
public NbtCompound writeNbt() {
return (NbtCompound) MinionData.CODEC.encode(this, NbtOps.INSTANCE, null).result().orElse(new NbtCompound());
}
public static MinionData readNbt(NbtCompound nbt) {
return MinionData.CODEC.decode(NbtOps.INSTANCE, nbt).resultOrPartial().map(Pair::getFirst).orElseGet(MinionData::createDefault);
}
public static void register() {
PolymerComponent.registerDataComponent(COMPONENT);
}
@@ -12,6 +12,7 @@ import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUsageContext;
import net.minecraft.item.Items;
import net.minecraft.item.tooltip.TooltipType;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text;
@@ -23,7 +24,6 @@ import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import xyz.nucleoid.packettweaker.PacketContext;
import java.util.List;
import java.util.function.Consumer;
public class MinionItem extends Item implements PolymerItem {
@@ -49,8 +49,8 @@ public class MinionItem extends Item implements PolymerItem {
}
@Override
public void appendTooltip(ItemStack stack, TooltipContext context, TooltipDisplayComponent component, Consumer<Text> tooltip, TooltipType type) {
MinionData data = getData(stack);
public void appendTooltip(ItemStack stack, TooltipContext context, TooltipDisplayComponent component, Consumer<Text> tooltip, TooltipType type) {
MinionData data = null /*getData(stack)*/;
if(data != null) {
tooltip.accept(Text.translatable("minions.minion_item.tooltip", data.name()));
}
@@ -70,31 +70,31 @@ public class MinionItem extends Item implements PolymerItem {
@Override
public ActionResult useOnBlock(ItemUsageContext context) {
if(!context.getWorld().isClient) {
MinionData data = getDataOrDefault(context.getStack());
MinionData data = getDataOrDefault(context.getWorld().getServer(), context.getStack());
MinionFakePlayer.spawnMinion(data, (ServerWorld) context.getWorld(), context.getBlockPos().toCenterPos().add(0,0.5,0), new Vec2f(0, 0));
}
context.getStack().decrement(1);
return ActionResult.SUCCESS;
}
public static void setData(MinionData data, ItemStack item) {
public static void setData(MinecraftServer server, MinionData data, ItemStack item) {
item.set(MinionData.COMPONENT, data.uuid());
MinionPersistentState.INSTANCE.updateMinionData(data);
MinionPersistentState.get(server).updateMinionData(data);
}
@Nullable
public static MinionData getData(ItemStack item) {
public static MinionData getData(MinecraftServer server, ItemStack item) {
if(item.contains(MinionData.COMPONENT)) {
return MinionPersistentState.INSTANCE.getMinionData(item.get(MinionData.COMPONENT));
return MinionPersistentState.get(server).getMinionData(item.get(MinionData.COMPONENT));
}
return null;
}
public static MinionData getDataOrDefault(ItemStack item) {
MinionData data = getData(item);
public static MinionData getDataOrDefault(MinecraftServer server, ItemStack item) {
MinionData data = getData(server, item);
if(data == null) {
data = MinionData.createDefault();
setData(data, item);
data = MinionData.createDefault(server);
setData(server, data, item);
}
return data;
}
@@ -12,7 +12,7 @@ public interface MinionListener extends SerializableListenerManager.Serializable
default void onInstructionsUpdate(MinionFakePlayer minion) {}
default void onInstructionRename(MinionFakePlayer minion, ConfiguredInstruction<?> instruction, String newName) {}
default void onInstructionRename(MinionFakePlayer minion, ConfiguredInstruction<?> instruction, String oldName, String newName) {}
interface Delegating extends MinionListener {
@Nullable MinionListener getBacking();
@@ -34,14 +34,14 @@ public interface MinionListener extends SerializableListenerManager.Serializable
@Override
default void onInstructionsUpdate(MinionFakePlayer minion) {
if(getBacking() != null) {
getBacking().onMinionSpawn(minion);
getBacking().onInstructionsUpdate(minion);
}
}
@Override
default void onInstructionRename(MinionFakePlayer minion, ConfiguredInstruction<?> instruction, String newName) {
default void onInstructionRename(MinionFakePlayer minion, ConfiguredInstruction<?> instruction, String oldName, String newName) {
if(getBacking() != null) {
getBacking().onInstructionRename(minion, instruction, newName);
getBacking().onInstructionRename(minion, instruction, oldName, newName);
}
}
}
@@ -17,8 +17,6 @@ public class MinionPersistentState extends PersistentState {
public static PersistentStateType<MinionPersistentState> TYPE = new PersistentStateType<>("minion", MinionPersistentState::new, MinionPersistentState.CODEC, null);
public static MinionPersistentState INSTANCE;
private final Map<UUID, MinionData> minionData = new HashMap<>();
public MinionPersistentState() {
@@ -62,7 +60,7 @@ public class MinionPersistentState extends PersistentState {
.findFirst();
}
public static void create(MinecraftServer server) {
INSTANCE = server.getWorld(World.OVERWORLD).getPersistentStateManager().getOrCreate(TYPE);
public static MinionPersistentState get(MinecraftServer server) {
return server.getWorld(World.OVERWORLD).getPersistentStateManager().getOrCreate(TYPE);
}
}
@@ -4,6 +4,7 @@ import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.PropertyMap;
import com.mojang.brigadier.StringReader;
import io.github.skippyall.minions.gui.input.Result;
import net.minecraft.server.MinecraftServer;
import net.minecraft.text.Text;
import net.minecraft.util.StringHelper;
@@ -27,7 +28,7 @@ public class MinionProfileUtils {
return newProfile;
}
public static Result<String, Text> checkMinionNameWithoutPrefix(String name) {
public static Result<String, Text> checkMinionNameWithoutPrefix(MinecraftServer server, String name) {
for(char c : name.toCharArray()) {
if(!StringReader.isAllowedInUnquotedString(c)) {
return new Result.Error<>(Text.translatable("minions.generic.name.invalid_char"));
@@ -42,22 +43,22 @@ public class MinionProfileUtils {
return new Result.Error<>(Text.translatable("minions.generic.name.invalid"));
}
if(MinionPersistentState.INSTANCE.isMinionNameTaken(PREFIX + name)) {
if(MinionPersistentState.get(server).isMinionNameTaken(PREFIX + name)) {
return new Result.Error<>(Text.translatable("minions.generic.name.taken"));
}
return new Result.Success<>(name);
}
public static String newDefaultMinionName() {
public static String newDefaultMinionName(MinecraftServer server) {
int i = 0;
while (MinionPersistentState.INSTANCE.isMinionNameTaken("+Minion" + i)) {
while (MinionPersistentState.get(server).isMinionNameTaken("+Minion" + i)) {
i++;
}
return "+Minion" + i;
}
public static boolean isMinion(UUID uuid) {
return MinionPersistentState.INSTANCE.isMinion(uuid);
public static boolean isMinion(MinecraftServer server, UUID uuid) {
return MinionPersistentState.get(server).isMinion(uuid);
}
}
@@ -1,6 +1,6 @@
package io.github.skippyall.minions.minion;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.program.InstructionRuntime;
@@ -83,7 +83,7 @@ public class MinionRuntime implements InstructionRuntime<MinionRuntime> {
minion.forEachMinionListener(minionListener -> {
minionListener.onInstructionRename(minion , instruction, newName);
minionListener.onInstructionRename(minion, instruction, oldName, newName);
minionListener.onInstructionsUpdate(minion);
});
}
@@ -3,8 +3,7 @@ package io.github.skippyall.minions.minion.fakeplayer;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.PropertyMap;
import io.github.skippyall.minions.MinionItems;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.registration.MinionItems;
import io.github.skippyall.minions.minion.MinionListener;
import io.github.skippyall.minions.minion.MinionData;
import io.github.skippyall.minions.gui.MinionGui;
@@ -12,7 +11,6 @@ import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.minion.MinionItem;
import io.github.skippyall.minions.minion.MinionPersistentState;
import io.github.skippyall.minions.minion.MinionProfileUtils;
import io.github.skippyall.minions.module.MobSpawningAbility;
import io.github.skippyall.minions.module.ModuleInventory;
import io.github.skippyall.minions.module.SpecialAbilities;
import io.github.skippyall.minions.util.SerializableListenerManager;
@@ -57,12 +55,9 @@ public class MinionFakePlayer extends ServerPlayerEntity {
private EntityPlayerActionPack actionPack;
private final SerializableListenerManager<MinionListener> minionListeners = new SerializableListenerManager<>(MinionRegistries.MINION_LISTENER_CODECS);
private final ModuleInventory moduleInventory = new ModuleInventory(this);
private final MinionRuntime instructionManager = new MinionRuntime(this);
private final MinionData data;
public static void spawnMinion(MinionData data, ServerWorld level, @Nullable Vec3d pos, @Nullable Vec2f rot) {
spawnMinion(data, level, pos, rot, false);
}
@@ -79,8 +74,8 @@ public class MinionFakePlayer extends ServerPlayerEntity {
}
private static void doSpawn(MinionData data, GameProfile profile, MinecraftServer server, ServerWorld level, @Nullable Vec3d pos, @Nullable Vec2f rot) {
MinionFakePlayer instance = new MinionFakePlayer(server, level, profile, SyncedClientOptions.createDefault(), data);
MinionPersistentState.INSTANCE.updateMinionData(data.withSpawned(true));
MinionFakePlayer instance = new MinionFakePlayer(server, level, profile, SyncedClientOptions.createDefault());
MinionPersistentState.get(server).updateMinionData(data.withSpawned(true));
if(pos != null && rot != null) {
instance.fixStartingPosition = () -> instance.refreshPositionAndAngles(pos.x, pos.y, pos.z, rot.x, rot.y);
@@ -101,18 +96,17 @@ public class MinionFakePlayer extends ServerPlayerEntity {
instance.dataTracker.set(PLAYER_MODEL_PARTS, (byte) 0x7f); // show all model layers (incl. capes)
instance.getAbilities().flying = false;
instance.minionListeners.forEachListener(listener -> listener.onMinionSpawn(instance));
instance.listeners().forEach(listener -> listener.onMinionSpawn(instance));
}
public static MinionFakePlayer respawnFake(MinecraftServer server, ServerWorld level, GameProfile profile, SyncedClientOptions cli, MinionData data)
public static MinionFakePlayer respawnFake(MinecraftServer server, ServerWorld level, GameProfile profile, SyncedClientOptions cli)
{
return new MinionFakePlayer(server, level, profile, cli, data);
return new MinionFakePlayer(server, level, profile, cli);
}
private MinionFakePlayer(MinecraftServer server, ServerWorld worldIn, GameProfile profile, SyncedClientOptions cli, MinionData data)
private MinionFakePlayer(MinecraftServer server, ServerWorld worldIn, GameProfile profile, SyncedClientOptions cli)
{
super(server, worldIn, profile, cli);
this.data = data;
actionPack = new EntityPlayerActionPack(this);
}
@@ -129,19 +123,23 @@ public class MinionFakePlayer extends ServerPlayerEntity {
}
public MinionData getData() {
return data;
return MinionPersistentState.get(getServer()).getMinionData(getUuid());
}
public SerializableListenerManager<MinionListener> listeners() {
return getData().listeners();
}
public void addMinionListener(MinionListener listener) {
minionListeners.addListener(listener);
listeners().addListener(listener);
}
public void removeMinionListener(MinionListener listener) {
minionListeners.removeListener(listener);
listeners().removeListener(listener);
}
public void forEachMinionListener(Consumer<MinionListener> listenerConsumer) {
minionListeners.forEachListener(listenerConsumer);
listeners().forEach(listenerConsumer);
}
public boolean canSpawnMobs() {
@@ -173,7 +171,7 @@ public class MinionFakePlayer extends ServerPlayerEntity {
public void kill(Text reason)
{
minionListeners.forEachListener(listener -> listener.onMinionRemove(this));
listeners().forEach(listener -> listener.onMinionRemove(this));
shakeOff();
@@ -185,7 +183,7 @@ public class MinionFakePlayer extends ServerPlayerEntity {
}));
}
MinionPersistentState.INSTANCE.updateMinionData(data.withSpawned(false));
MinionPersistentState.get(getServer()).updateMinionData(getData().withSpawned(false));
}
@Override
@@ -266,15 +264,15 @@ public class MinionFakePlayer extends ServerPlayerEntity {
@Override
public void drop(ServerWorld world, DamageSource damageSource) {
super.drop(world, damageSource);
ItemEntity entity = dropStack(world, toItemStack());
ItemEntity entity = dropStack(world, toItemStack(getServer()));
if (entity != null) {
entity.setNeverDespawn();
}
}
private ItemStack toItemStack() {
private ItemStack toItemStack(MinecraftServer server) {
ItemStack stack = new ItemStack(MinionItems.MINION_ITEM);
MinionItem.setData(data, stack);
MinionItem.setData(server, getData(), stack);
return stack;
}
@@ -283,7 +281,6 @@ public class MinionFakePlayer extends ServerPlayerEntity {
super.writeCustomData(view);
moduleInventory.writeData(view.get("modules"));
instructionManager.save(view.get("instructionManager"));
minionListeners.save(view);
}
@Override
@@ -291,6 +288,5 @@ public class MinionFakePlayer extends ServerPlayerEntity {
super.readCustomData(view);
moduleInventory.readData(view.getReadView("modules"));
instructionManager.load(view.getReadView("instructionManager"));
minionListeners.load(view);
}
}
@@ -1,6 +1,6 @@
package io.github.skippyall.minions.minion.skin;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.Minions;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
@@ -54,7 +54,7 @@ public class PlayerListMixin {
@WrapOperation(method = "respawnPlayer", at = @At(value = "NEW", target = "(Lnet/minecraft/server/MinecraftServer;Lnet/minecraft/server/world/ServerWorld;Lcom/mojang/authlib/GameProfile;Lnet/minecraft/network/packet/c2s/common/SyncedClientOptions;)Lnet/minecraft/server/network/ServerPlayerEntity;"))
public ServerPlayerEntity makePlayerForRespawn(MinecraftServer minecraftServer, ServerWorld serverLevel, GameProfile gameProfile, SyncedClientOptions clientInformation, Operation<ServerPlayerEntity> original, ServerPlayerEntity serverPlayer, boolean bl) {
if (serverPlayer instanceof MinionFakePlayer minion) {
return MinionFakePlayer.respawnFake(minecraftServer, serverLevel, gameProfile, clientInformation, minion.getData());
return MinionFakePlayer.respawnFake(minecraftServer, serverLevel, gameProfile, clientInformation);
}
return original.call(minecraftServer, serverLevel, gameProfile, clientInformation);
}
@@ -3,7 +3,7 @@ package io.github.skippyall.minions.module;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import eu.pb4.polymer.core.api.other.PolymerComponent;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.program.instruction.InstructionType;
@@ -1,6 +1,6 @@
package io.github.skippyall.minions.module;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.Minions;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
@@ -1,7 +1,7 @@
package io.github.skippyall.minions.program.consumer;
import com.mojang.serialization.Codec;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.program.InstructionRuntime;
import io.github.skippyall.minions.program.value.ValueType;
import org.jetbrains.annotations.Nullable;
@@ -1,6 +1,6 @@
package io.github.skippyall.minions.program.instruction;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.program.InstructionRuntime;
import io.github.skippyall.minions.program.supplier.Parameter;
@@ -66,7 +66,7 @@ public class ConfiguredInstruction<R extends InstructionRuntime<R>> {
Minions.LOGGER.error("An error occurred while executing configured Instruction", e);
}
listeners.forEachListener(listener -> listener.onRun(this));
listeners.forEach(listener -> listener.onRun(this));
}
}
@@ -87,20 +87,20 @@ public class ConfiguredInstruction<R extends InstructionRuntime<R>> {
if(execution != null) {
execution.stop(minion, valueConsumers);
execution = null;
listeners.forEachListener(listener -> listener.onStop(this));
listeners.forEach(listener -> listener.onStop(this));
}
}
private void onSupplierChange(Parameter<?> parameter) {
listeners.forEachListener(listener -> listener.onSupplierChange(this, parameter));
listeners.forEach(listener -> listener.onSupplierChange(this, parameter));
}
private void onConsumerChange(Parameter<?> parameter) {
listeners.forEachListener(listener -> listener.onConsumerChange(this, parameter));
listeners.forEach(listener -> listener.onConsumerChange(this, parameter));
}
public void onInstructionRemove() {
listeners.forEachListener(listener -> listener.onInstructionRemove(this));
listeners.forEach(listener -> listener.onInstructionRemove(this));
}
public void addListener(ConfiguredInstructionListener listener) {
@@ -1,6 +1,6 @@
package io.github.skippyall.minions.program.instruction;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.minion.fakeplayer.EntityPlayerActionPack;
@@ -0,0 +1,118 @@
package io.github.skippyall.minions.program.instruction.execution.inventory;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import io.github.skippyall.minions.program.consumer.ValueConsumerList;
import io.github.skippyall.minions.program.instruction.InstructionExecution;
import io.github.skippyall.minions.program.supplier.ValueSupplierList;
import net.minecraft.component.EnchantmentEffectComponentTypes;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.screen.slot.Slot;
import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.storage.ReadView;
import net.minecraft.storage.WriteView;
public class SwapItemExecution implements InstructionExecution<MinionRuntime> {
private int fromSlot;
private boolean fromScreen;
private int toSlot;
private boolean toScreen;
@Override
public void start(MinionRuntime runtime) {
MinionFakePlayer minion = runtime.getMinion();
if((fromScreen || toScreen) && minion.currentScreenHandler == null) {
return;
}
if(!(checkBounds(minion, fromSlot, fromScreen) && checkBounds(minion, toSlot, toScreen))) {
return;
}
ItemStack fromStack = getStack(minion, fromSlot, fromScreen);
ItemStack toStack = getStack(minion, toSlot, toScreen);
if(!(canExchange(minion, fromSlot, fromScreen, toStack) && canExchange(minion, toSlot, toScreen, fromStack))) {
return;
}
}
private boolean checkBounds(MinionFakePlayer minion, int slot, boolean screen) {
if(screen) {
return slot >= 0 && slot < minion.currentScreenHandler.slots.size();
} else {
return slot >= 0 && slot <= PlayerInventory.OFF_HAND_SLOT;
}
}
private ItemStack getStack(MinionFakePlayer minion, int slot, boolean screen) {
if(screen) {
return minion.currentScreenHandler.getSlot(slot).getStack();
} else {
return minion.getInventory().getStack(slot);
}
}
private boolean canExchange(MinionFakePlayer minion, int slotIndex, boolean screen, ItemStack newStack) {
if(screen) {
Slot slot = minion.currentScreenHandler.getSlot(slotIndex);
if(!slot.getStack().isEmpty() && !slot.canTakeItems(minion)) {
return false;
}
if(!newStack.isEmpty() && !slot.canInsert(newStack)) {
return false;
}
} else {
if(slotIndex >= PlayerInventory.MAIN_SIZE && slotIndex < PlayerInventory.OFF_HAND_SLOT) {
if(!minion.canEquip(newStack, PlayerInventory.EQUIPMENT_SLOTS.get(slotIndex))) {
return false;
}
if(EnchantmentHelper.hasAnyEnchantmentsWith(minion.getInventory().getStack(slotIndex), EnchantmentEffectComponentTypes.PREVENT_ARMOR_CHANGE)) {
return false;
}
}
}
return true;
}
private void simulateClick(MinionFakePlayer minion, int slotIndex, boolean screen) {
if(screen) {
minion.currentScreenHandler.onSlotClick(slotIndex, 0, SlotActionType.SWAP, minion);
} else {
}
}
@Override
public void tick(MinionRuntime runtime) {
InstructionExecution.super.tick(runtime);
}
@Override
public boolean isDone(MinionRuntime runtime) {
return false;
}
@Override
public void stop(MinionRuntime runtime, ValueConsumerList<MinionRuntime> valueConsumers) {
InstructionExecution.super.stop(runtime, valueConsumers);
}
@Override
public void readArguments(ValueSupplierList<MinionRuntime> arguments, MinionRuntime runtime) {
}
@Override
public void save(WriteView view, MinionRuntime runtime) {
}
@Override
public void load(ReadView view, MinionRuntime runtime) {
}
}
@@ -2,7 +2,7 @@ package io.github.skippyall.minions.program.supplier;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.program.value.ValueType;
import org.jetbrains.annotations.Nullable;
@@ -1,7 +1,7 @@
package io.github.skippyall.minions.program.supplier;
import com.mojang.serialization.Codec;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.program.InstructionRuntime;
import io.github.skippyall.minions.program.value.ValueType;
import org.jetbrains.annotations.Nullable;
@@ -1,6 +1,6 @@
package io.github.skippyall.minions.program.supplier;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.minion.MinionRuntime;
import net.minecraft.registry.Registry;
@@ -1,7 +1,7 @@
package io.github.skippyall.minions.program.value;
import com.mojang.serialization.Codec;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.gui.input.ChoiceInput;
import io.github.skippyall.minions.gui.input.TextInput;
@@ -3,7 +3,7 @@ package io.github.skippyall.minions.reference;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import eu.pb4.polymer.core.api.other.PolymerComponent;
import io.github.skippyall.minions.MinionRegistries;
import io.github.skippyall.minions.registration.MinionRegistries;
import io.github.skippyall.minions.Minions;
import net.fabricmc.fabric.api.item.v1.ComponentTooltipAppenderRegistry;
import net.minecraft.component.ComponentType;
@@ -1,7 +1,7 @@
package io.github.skippyall.minions.reference;
import eu.pb4.polymer.core.api.item.PolymerItem;
import io.github.skippyall.minions.MinionItems;
import io.github.skippyall.minions.registration.MinionItems;
import io.github.skippyall.minions.minion.fakeplayer.MinionFakePlayer;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.item.Item;
@@ -1,8 +1,9 @@
package io.github.skippyall.minions;
package io.github.skippyall.minions.registration;
import eu.pb4.polymer.core.api.block.PolymerBlockUtils;
import io.github.skippyall.minions.block.MinionTriggerBlock;
import io.github.skippyall.minions.block.MinionTriggerBlockEntity;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.block.miniontrigger.MinionTriggerBlock;
import io.github.skippyall.minions.block.miniontrigger.MinionTriggerBlockEntity;
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.entity.BlockEntityType;
@@ -1,6 +1,7 @@
package io.github.skippyall.minions;
package io.github.skippyall.minions.registration;
import eu.pb4.polymer.core.api.item.PolymerItemGroupUtils;
import io.github.skippyall.minions.Minions;
import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
@@ -1,12 +1,11 @@
package io.github.skippyall.minions;
package io.github.skippyall.minions.registration;
import eu.pb4.polymer.core.api.item.PolymerBlockItem;
import eu.pb4.polymer.core.api.item.SimplePolymerItem;
import io.github.skippyall.minions.block.MinionTriggerBlockItem;
import io.github.skippyall.minions.block.miniontrigger.MinionTriggerBlockItem;
import io.github.skippyall.minions.minion.MinionItem;
import io.github.skippyall.minions.minion.MinionRuntime;
import io.github.skippyall.minions.module.MinionModule;
import io.github.skippyall.minions.module.MobSpawningAbility;
import io.github.skippyall.minions.module.SpecialAbilities;
import io.github.skippyall.minions.module.SpecialAbility;
import io.github.skippyall.minions.program.instruction.InstructionType;
@@ -75,7 +74,8 @@ public class MinionItems {
public static final PolymerBlockItem MINION_TRIGGER_ITEM =
registerItem(
MinionBlocks.MINION_TRIGGER_ID,
settings -> new MinionTriggerBlockItem(MinionBlocks.MINION_TRIGGER_BLOCK, settings, Items.COMPARATOR)
settings -> new MinionTriggerBlockItem(MinionBlocks.MINION_TRIGGER_BLOCK, settings, Items.COMPARATOR),
new Item.Settings().useBlockPrefixedTranslationKey()
);
public static final ReferenceItem REFERENCE_ITEM = registerItem(Identifier.of(MOD_ID, "reference"), ReferenceItem::new);
@@ -0,0 +1,13 @@
package io.github.skippyall.minions.registration;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.block.miniontrigger.MinionTriggerMinionListener;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
public class MinionRegistration {
public static void register() {
Registry.register(MinionRegistries.MINION_LISTENER_CODECS, Identifier.of(Minions.MOD_ID, "minion_trigger"), MinionTriggerMinionListener.CODEC);
}
}
@@ -1,7 +1,8 @@
package io.github.skippyall.minions;
package io.github.skippyall.minions.registration;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import io.github.skippyall.minions.Minions;
import io.github.skippyall.minions.docs.ReferenceEntry;
import io.github.skippyall.minions.gui.GuiDisplay;
import io.github.skippyall.minions.minion.MinionListener;
@@ -1,10 +1,12 @@
package io.github.skippyall.minions.util;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
public class ListenerManager<T> {
public class ListenerManager<T> implements Iterable<T> {
protected final List<T> listeners;
public ListenerManager() {
@@ -15,17 +17,44 @@ public class ListenerManager<T> {
this.listeners = listeners;
}
public void forEachListener(Consumer<T> listenerConsumer) {
for(T listener : listeners) {
listenerConsumer.accept(listener);
}
}
public void addListener(T listener) {
listeners.add(listener);
}
public void removeListener(T listener) {
listeners.remove(listener);
onRemove(listener);
}
protected void onRemove(T listener) {}
@Override
public @NotNull Iterator<T> iterator() {
return new Iterator<>() {
final Iterator<T> backing = listeners.iterator();
T last;
@Override
public boolean hasNext() {
return backing.hasNext();
}
@Override
public T next() {
last = backing.next();
return last;
}
@Override
public void remove() {
backing.remove();
onRemove(last);
}
};
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
}
@@ -81,6 +81,8 @@
"item.minions.advanced_upgrade_base": "Advanced Upgrade Base",
"item.minions.reference": "Reference",
"block.minions.minion_trigger": "Minion Trigger",
"minions.minion_item.tooltip": "Name: %s",
"minions.generic.name.invalid_char": "Name contains invalid character",
"minions.generic.name.too_long": "Name is too long",