diff --git a/build.gradle b/build.gradle index de2194f..6407b2d 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,7 @@ dependencies { modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" modImplementation "eu.pb4:polymer-core:${project.polymer_version}" modImplementation include("eu.pb4:sgui:${project.sgui_version}") + modImplementation include("xyz.nucleoid:server-translations-api:${project.server_translations_version}") } processResources { diff --git a/gradle.properties b/gradle.properties index 43547c9..c3ef94e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,3 +18,4 @@ org.gradle.jvmargs=-Xmx1G polymer_version=0.9.12+1.21.1 sgui_version=1.6.0+1.21 + server_translations_version=2.3.1+1.21-pre2 diff --git a/src/main/java/io/github/skippyall/minions/fakeplayer/MinionFakePlayer.java b/src/main/java/io/github/skippyall/minions/fakeplayer/MinionFakePlayer.java index dc41f04..849ce0f 100644 --- a/src/main/java/io/github/skippyall/minions/fakeplayer/MinionFakePlayer.java +++ b/src/main/java/io/github/skippyall/minions/fakeplayer/MinionFakePlayer.java @@ -1,15 +1,15 @@ package io.github.skippyall.minions.fakeplayer; import com.mojang.authlib.GameProfile; -import com.mojang.authlib.yggdrasil.ProfileResult; import io.github.skippyall.minions.Minions; -import io.github.skippyall.minions.minion.*; -import io.github.skippyall.minions.mixins.GameProfileMixin; +import io.github.skippyall.minions.minion.MinionData; +import io.github.skippyall.minions.gui.MinionGui; +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.gui.ModuleInventory; import io.github.skippyall.minions.program.runtime.MinionRuntime; import net.minecraft.block.BlockState; -import net.minecraft.block.PistonBlock; -import net.minecraft.command.EntitySelector; -import net.minecraft.command.argument.EntityArgumentType; import net.minecraft.component.DataComponentTypes; import net.minecraft.entity.Entity; import net.minecraft.entity.EquipmentSlot; @@ -26,13 +26,11 @@ import net.minecraft.network.packet.c2s.common.SyncedClientOptions; import net.minecraft.network.packet.c2s.play.ClientStatusC2SPacket; import net.minecraft.network.packet.s2c.play.EntityPositionS2CPacket; import net.minecraft.network.packet.s2c.play.EntitySetHeadYawS2CPacket; -import net.minecraft.network.packet.s2c.play.PlayerListS2CPacket; import net.minecraft.server.MinecraftServer; import net.minecraft.server.ServerTask; import net.minecraft.server.network.ConnectedClientData; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; -import net.minecraft.text.Decoration; import net.minecraft.text.Text; import net.minecraft.text.TranslatableTextContent; import net.minecraft.util.ActionResult; @@ -45,6 +43,7 @@ import net.minecraft.world.TeleportTarget; import org.jetbrains.annotations.Nullable; import java.util.UUID; +import java.util.concurrent.CompletableFuture; public class MinionFakePlayer extends ServerPlayerEntity { public Runnable fixStartingPosition = () -> {}; @@ -56,72 +55,65 @@ public class MinionFakePlayer extends ServerPlayerEntity { private final ModuleInventory moduleInventory = new ModuleInventory(); private final MinionRuntime runtime = new MinionRuntime(this); - public static void createMinion(String username, ServerWorld level, ServerPlayerEntity owner, boolean canProgram, Vec3d pos, double yaw, double pitch) { + private UUID skinUuid = null; + + public static void createMinion(MinionData data, ServerWorld level, ServerPlayerEntity owner, boolean canProgram, Vec3d pos, double yaw, double pitch) { MinecraftServer server = level.getServer(); - server.getUserCache().findByNameAsync(username).thenAcceptAsync((optional) -> { - try { - GameProfile profile = null; - if(optional.isPresent()){ - UUID uuid = optional.get().getId(); - ProfileResult result = server.getSessionService().fetchProfile(uuid, true); - if(result != null) { - profile = result.profile(); - } + + CompletableFuture future; + if(data.skinUuid != null) { + future = MinionProfileUtils.getSkinOwnerProfile(server, data.skinUuid); + } else { + future = MinionProfileUtils.lookupSkinOwnerProfile(server, data.name); + } + + future.thenAccept(skinProfile -> { + GameProfile profile = MinionProfileUtils.makeNewMinionProfile(null, data.name, skinProfile); + Minions.addExecuteOnNextTick(() -> { + MinionFakePlayer instance = new MinionFakePlayer(server, level, profile, SyncedClientOptions.createDefault()); + if(skinProfile != null) { + instance.skinUuid = skinProfile.getId(); } - if(profile == null) { - profile = new GameProfile(new UUID(0,0), username); - } - GameProfile newProfile = new GameProfile(UUID.randomUUID(), username); - newProfile.getProperties().putAll(profile.getProperties()); - GameProfile finalProfile = newProfile; - ((GameProfileMixin)finalProfile).setId(UUID.randomUUID()); - Minions.addExecuteOnNextTick(() -> { - MinionFakePlayer instance = new MinionFakePlayer(server, level, finalProfile, SyncedClientOptions.createDefault()); - instance.programmable = canProgram; - instance.fixStartingPosition = () -> instance.refreshPositionAndAngles(pos.x, pos.y, pos.z, (float) yaw, (float) pitch); - server.getPlayerManager().onPlayerConnect(new FakeClientConnection(NetworkSide.SERVERBOUND), instance, new ConnectedClientData(finalProfile, 0, instance.getClientOptions(), false)); - instance.teleport(level, pos.x, pos.y, pos.z, (float) yaw, (float) pitch); - instance.setHealth(20.0F); - instance.unsetRemoved(); - instance.getAttributeInstance(EntityAttributes.GENERIC_STEP_HEIGHT).setBaseValue(0.6F); - instance.interactionManager.changeGameMode(GameMode.SURVIVAL); - server.getPlayerManager().sendToDimension(new EntitySetHeadYawS2CPacket(instance, (byte) (instance.headYaw * 256 / 360)), level.getRegistryKey());//instance.dimension); - server.getPlayerManager().sendToDimension(new EntityPositionS2CPacket(instance), level.getRegistryKey());//instance.dimension); - //instance.world.getChunkManager(). updatePosition(instance); - instance.dataTracker.set(PLAYER_MODEL_PARTS, (byte) 0x7f); // show all model layers (incl. capes) - instance.getAbilities().flying = false; - MinionPersistentState.INSTANCE.addMinion(instance); - }); - }catch (Throwable ex) { - ex.printStackTrace(); - } + + instance.programmable = canProgram; + instance.fixStartingPosition = () -> instance.refreshPositionAndAngles(pos.x, pos.y, pos.z, (float) yaw, (float) pitch); + server.getPlayerManager().onPlayerConnect(new FakeClientConnection(NetworkSide.SERVERBOUND), instance, new ConnectedClientData(profile, 0, instance.getClientOptions(), false)); + instance.teleport(level, pos.x, pos.y, pos.z, (float) yaw, (float) pitch); + instance.setHealth(20.0F); + instance.unsetRemoved(); + instance.getAttributeInstance(EntityAttributes.GENERIC_STEP_HEIGHT).setBaseValue(0.6F); + instance.interactionManager.changeGameMode(GameMode.SURVIVAL); + server.getPlayerManager().sendToDimension(new EntitySetHeadYawS2CPacket(instance, (byte) (instance.headYaw * 256 / 360)), level.getRegistryKey());//instance.dimension); + server.getPlayerManager().sendToDimension(new EntityPositionS2CPacket(instance), level.getRegistryKey());//instance.dimension); + //instance.world.getChunkManager(). updatePosition(instance); + instance.dataTracker.set(PLAYER_MODEL_PARTS, (byte) 0x7f); // show all model layers (incl. capes) + instance.getAbilities().flying = false; + MinionPersistentState.INSTANCE.addMinion(instance); + }); }); } public static void spawnMinionAt(MinionData data, ServerWorld level, @Nullable Vec3d pos, @Nullable Vec2f rot) { MinecraftServer server = level.getServer(); - server.getUserCache().findByNameAsync(data.name).thenAcceptAsync((optional) -> { - GameProfile profile = null; - if (optional.isPresent()) { - ProfileResult result = server.getSessionService().fetchProfile(optional.get().getId(), true); - if (result != null) { - profile = result.profile(); - } - } - if (profile == null) { - profile = new GameProfile(new UUID(0, 0), data.name); - } - GameProfile newProfile = new GameProfile(data.uuid, data.name); - newProfile.getProperties().putAll(profile.getProperties()); - GameProfile finalProfile = newProfile; - ((GameProfileMixin)finalProfile).setId(data.uuid); + + CompletableFuture future; + if(data.skinUuid != null) { + future = MinionProfileUtils.getSkinOwnerProfile(server, data.skinUuid); + } else { + future = MinionProfileUtils.lookupSkinOwnerProfile(server, data.name); + } + + future.thenAccept((skinProfile) -> { + GameProfile profile = MinionProfileUtils.makeNewMinionProfile(data.uuid, data.name, skinProfile); Minions.addExecuteOnNextTick(() -> { - MinionFakePlayer instance = new MinionFakePlayer(server, level, finalProfile, SyncedClientOptions.createDefault()); + MinionFakePlayer instance = new MinionFakePlayer(server, level, profile, SyncedClientOptions.createDefault()); + if (skinProfile != null) { + instance.skinUuid = skinProfile.getId(); + } if(pos != null && rot != null) { instance.fixStartingPosition = () -> instance.refreshPositionAndAngles(pos.x, pos.y, pos.z, rot.x, rot.y); } - System.out.println(instance.getPos()); - server.getPlayerManager().onPlayerConnect(new FakeClientConnection(NetworkSide.SERVERBOUND), instance, new ConnectedClientData(finalProfile, 0, instance.getClientOptions(), false)); + server.getPlayerManager().onPlayerConnect(new FakeClientConnection(NetworkSide.SERVERBOUND), instance, new ConnectedClientData(profile, 0, instance.getClientOptions(), false)); System.out.println(instance.getPos()); if(pos != null && rot != null) { instance.teleport(level, pos.x, pos.y, pos.z, rot.x, rot.y); @@ -172,7 +164,7 @@ public class MinionFakePlayer extends ServerPlayerEntity { @Override public ActionResult interact(PlayerEntity player, Hand hand) { if(player instanceof ServerPlayerEntity spe) { - MinionInventory.openInventory(spe, this); + MinionGui.openInventory(spe, this); } return ActionResult.CONSUME; } @@ -353,4 +345,8 @@ public class MinionFakePlayer extends ServerPlayerEntity { public String getMinionName() { return getGameProfile().getName(); } + + public UUID getSkinUuid() { + return skinUuid; + } } \ No newline at end of file diff --git a/src/main/java/io/github/skippyall/minions/gui/CommandsGui.java b/src/main/java/io/github/skippyall/minions/gui/CommandsGui.java new file mode 100644 index 0000000..f92a740 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/gui/CommandsGui.java @@ -0,0 +1,53 @@ +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.command.Command; +import io.github.skippyall.minions.fakeplayer.MinionFakePlayer; +import io.github.skippyall.minions.module.ModuleItem; +import net.minecraft.item.Item; +import net.minecraft.screen.ScreenHandlerType; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; + +import java.util.List; + +public class CommandsGui { + public static void openServerModuleCommandGui(ServerPlayerEntity player, MinionFakePlayer minion) { + List modules = minion.getModuleInventory().getModuleItems(); + + SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_9X3, player, false); + + gui.setTitle(Text.translatable("minions.gui.module_commands.title")); + + for (int i = 0; i < modules.size(); i++) { + ModuleItem module = modules.get(i); + gui.setSlot(i, new GuiElementBuilder() + .setItem(module.asItem()) + .setCallback(() -> openServerCommandGui(player, minion, module)) + ); + } + + gui.open(); + } + + public static void openServerCommandGui(ServerPlayerEntity player, MinionFakePlayer minion, ModuleItem module) { + List commands = module.getCommands(); + + SimpleGui commandGui = new SimpleGui(ScreenHandlerType.GENERIC_9X3, player, false); + + commandGui.setTitle(Text.translatable("minions.gui.commands.title", module.asItem().getName())); + + for(int j = 0; j < commands.size(); j++) { + Command command = commands.get(j); + commandGui.setSlot(j, new GuiElementBuilder() + .setItem(command.getItemRepresentation()) + .setName(command.getName()) + .addLoreLine(command.getDescription()) + .setCallback(() -> command.onRun(player, minion)) + ); + } + + commandGui.open(); + } +} diff --git a/src/main/java/io/github/skippyall/minions/minion/MinionInventory.java b/src/main/java/io/github/skippyall/minions/gui/MinionGui.java similarity index 82% rename from src/main/java/io/github/skippyall/minions/minion/MinionInventory.java rename to src/main/java/io/github/skippyall/minions/gui/MinionGui.java index ce70b97..d369a19 100644 --- a/src/main/java/io/github/skippyall/minions/minion/MinionInventory.java +++ b/src/main/java/io/github/skippyall/minions/gui/MinionGui.java @@ -1,4 +1,4 @@ -package io.github.skippyall.minions.minion; +package io.github.skippyall.minions.gui; import eu.pb4.sgui.api.elements.GuiElementBuilder; import eu.pb4.sgui.api.gui.SimpleGui; @@ -11,18 +11,18 @@ import net.minecraft.screen.slot.Slot; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.Text; -public class MinionInventory { +public class MinionGui { public static void openInventory(ServerPlayerEntity player, MinionFakePlayer minion) { openServerSideInventory(player, minion); } public static void openServerSideInventory(ServerPlayerEntity player, MinionFakePlayer minion) { SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_3X3, player, false); - gui.setTitle(Text.literal("Minion")); + gui.setTitle(minion.getName()); gui.setSlot(1, new GuiElementBuilder() .setItem(Items.COMMAND_BLOCK) - .setName(Text.literal("Commands")) + .setName(Text.translatable("minions.gui.main.commands")) .setCallback((i, clickType, slotActionType) -> { openCommandsGui(player, minion); }) @@ -30,7 +30,7 @@ public class MinionInventory { if(minion.isProgrammable()) { gui.setSlot(4, new GuiElementBuilder() .setItem(Items.REDSTONE) - .setName(Text.literal("Programming")) + .setName(Text.translatable("minions.gui.main.programming")) .setCallback((i, clickType, slotActionType) -> { openProgrammingInventory(player, minion); }) @@ -38,14 +38,14 @@ public class MinionInventory { } gui.setSlot(3, new GuiElementBuilder() .setItem(Items.NETHERITE_UPGRADE_SMITHING_TEMPLATE) - .setName(Text.literal("Modules and Detectors")) + .setName(Text.translatable("minions.gui.main.modules")) .setCallback((i, clickType, slotActionType) -> { openModuleInventory(player, minion); }) ); gui.setSlot(5, new GuiElementBuilder() .setItem(Items.CHEST) - .setName(Text.literal("Inventory")) + .setName(Text.translatable("minions.gui.main.inventory")) .setCallback((i, clickType, slotActionType) -> { openMinionInventory(player, minion); }) @@ -54,7 +54,7 @@ public class MinionInventory { } public static void openCommandsGui(ServerPlayerEntity player, MinionFakePlayer minion) { - CommandsInventory.openServerCommandsInventory(player, minion); + CommandsGui.openServerModuleCommandGui(player, minion); } public static void openProgrammingInventory(ServerPlayerEntity player, MinionFakePlayer minion) { @@ -62,12 +62,12 @@ public class MinionInventory { } public static void openModuleInventory(ServerPlayerEntity player, MinionFakePlayer minion) { - player.openHandledScreen(new SimpleNamedScreenHandlerFactory((syncId, playerInventory, player2) -> GenericContainerScreenHandler.createGeneric9x3(syncId, playerInventory, minion.getModuleInventory()), Text.literal(""))); + player.openHandledScreen(new SimpleNamedScreenHandlerFactory((syncId, playerInventory, player2) -> GenericContainerScreenHandler.createGeneric9x3(syncId, playerInventory, minion.getModuleInventory()), Text.translatable(""))); } public static void openMinionInventory(ServerPlayerEntity player, MinionFakePlayer minion) { SimpleGui gui = new SimpleGui(ScreenHandlerType.GENERIC_9X5, player, false); - gui.setTitle(Text.literal("Minion")); + gui.setTitle(Text.translatable("minions.gui.inventory.title", minion.getName())); for (int i = 0; i < minion.getInventory().size(); i++) { gui.setSlotRedirect(i, new Slot(minion.getInventory(), i, 0, 0)); } diff --git a/src/main/java/io/github/skippyall/minions/minion/ModuleInventory.java b/src/main/java/io/github/skippyall/minions/gui/ModuleInventory.java similarity index 67% rename from src/main/java/io/github/skippyall/minions/minion/ModuleInventory.java rename to src/main/java/io/github/skippyall/minions/gui/ModuleInventory.java index c276ffb..cd67870 100644 --- a/src/main/java/io/github/skippyall/minions/minion/ModuleInventory.java +++ b/src/main/java/io/github/skippyall/minions/gui/ModuleInventory.java @@ -1,27 +1,20 @@ -package io.github.skippyall.minions.minion; +package io.github.skippyall.minions.gui; -import io.github.skippyall.minions.Minions; import io.github.skippyall.minions.command.Command; import io.github.skippyall.minions.module.ModuleItem; -import io.github.skippyall.minions.module.Modules; import io.github.skippyall.minions.program.block.CodeBlock; import net.minecraft.inventory.Inventories; -import net.minecraft.item.Item; +import net.minecraft.inventory.SimpleInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; -import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.RegistryWrapper; -import net.minecraft.registry.tag.TagKey; -import net.minecraft.util.Identifier; -import net.minecraft.util.collection.DefaultedList; import java.util.ArrayList; import java.util.List; -public class ModuleInventory implements ImplementedInventory { - private DefaultedList stacks = DefaultedList.ofSize(27, ItemStack.EMPTY); - +public class ModuleInventory extends SimpleInventory { public ModuleInventory() { + super(27); } @Override @@ -34,22 +27,26 @@ public class ModuleInventory implements ImplementedInventory { return (stack.getCount() <= getMaxCountPerStack()) && stack.getItem() instanceof ModuleItem; } - @Override - public DefaultedList getItems() { - return stacks; - } - public void readNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup lookup) { - Inventories.readNbt(nbt, stacks, lookup); + Inventories.readNbt(nbt, heldStacks, lookup); } public NbtCompound writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup lookup) { - return Inventories.writeNbt(nbt, stacks, lookup); + return Inventories.writeNbt(nbt, heldStacks, lookup); + } + + public boolean hasModule(ModuleItem module) { + for(ItemStack stack : heldStacks) { + if(stack.getItem() instanceof ModuleItem module2 && module2 == module) { + return true; + } + } + return false; } public List getModuleItems() { ArrayList modules = new ArrayList<>(); - for(ItemStack stack : stacks) { + for(ItemStack stack : heldStacks) { if(stack.getItem() instanceof ModuleItem module) { modules.add(module); } @@ -59,7 +56,7 @@ public class ModuleInventory implements ImplementedInventory { public List getAllCommands() { ArrayList commands = new ArrayList<>(); - for(ItemStack stack : stacks) { + for(ItemStack stack : heldStacks) { if(stack.getItem() instanceof ModuleItem module) { commands.addAll(module.getCommands()); } @@ -69,7 +66,7 @@ public class ModuleInventory implements ImplementedInventory { public List> getAllCodeBlocks() { ArrayList> commands = new ArrayList<>(); - for(ItemStack stack : stacks) { + for(ItemStack stack : heldStacks) { if(stack.getItem() instanceof ModuleItem module) { commands.addAll(module.getCodeBlocks()); } diff --git a/src/main/java/io/github/skippyall/minions/minion/CommandsInventory.java b/src/main/java/io/github/skippyall/minions/minion/CommandsInventory.java deleted file mode 100644 index e4414c8..0000000 --- a/src/main/java/io/github/skippyall/minions/minion/CommandsInventory.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.github.skippyall.minions.minion; - -import eu.pb4.sgui.api.elements.GuiElementBuilder; -import eu.pb4.sgui.api.gui.SimpleGui; -import io.github.skippyall.minions.command.Command; -import io.github.skippyall.minions.fakeplayer.MinionFakePlayer; -import net.minecraft.screen.GenericContainerScreenHandler; -import net.minecraft.screen.ScreenHandlerType; -import net.minecraft.server.network.ServerPlayerEntity; - -import java.util.List; - -public class CommandsInventory { - public static void openServerCommandsInventory(ServerPlayerEntity player, MinionFakePlayer minion) { - List commands = minion.getModuleInventory().getAllCommands(); - int rows = (int) Math.ceil((double) commands.size() / 9.0); - - if(rows != 0) { - boolean paged = false; - SimpleGui gui = new SimpleGui(getTypeForRows(rows), player, false); - - for (int i = 0; i < commands.size() && i < 54; i++) { - Command command = commands.get(i); - gui.setSlot(i, new GuiElementBuilder() - .setItem(command.getItemRepresentation()) - .setName(command.getName()) - .addLoreLine(command.getDescription()) - .setCallback(() -> command.onRun(player, minion)) - ); - } - - gui.open(); - } - } - - public static ScreenHandlerType getTypeForRows(int rows) { - return switch (rows) { - case 1 -> ScreenHandlerType.GENERIC_9X1; - case 2 -> ScreenHandlerType.GENERIC_9X2; - case 3 -> ScreenHandlerType.GENERIC_9X3; - case 4 -> ScreenHandlerType.GENERIC_9X4; - case 5 -> ScreenHandlerType.GENERIC_9X5; - case 6 -> ScreenHandlerType.GENERIC_9X6; - default -> throw new IllegalStateException("Unexpected value: " + rows); - }; - } -} diff --git a/src/main/java/io/github/skippyall/minions/minion/ImplementedInventory.java b/src/main/java/io/github/skippyall/minions/minion/ImplementedInventory.java deleted file mode 100644 index ec59747..0000000 --- a/src/main/java/io/github/skippyall/minions/minion/ImplementedInventory.java +++ /dev/null @@ -1,132 +0,0 @@ -package io.github.skippyall.minions.minion; - -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.inventory.Inventories; -import net.minecraft.inventory.Inventory; -import net.minecraft.item.ItemStack; -import net.minecraft.util.collection.DefaultedList; - -/** - * A simple {@code Inventory} implementation with only default methods + an item list getter. - * - * Originally by Juuz - */ -public interface ImplementedInventory extends Inventory { - - /** - * Retrieves the item list of this inventory. - * Must return the same instance every time it's called. - */ - DefaultedList getItems(); - - /** - * Creates an inventory from the item list. - */ - static ImplementedInventory of(DefaultedList items) { - return () -> items; - } - - /** - * Creates a new inventory with the specified size. - */ - static ImplementedInventory ofSize(int size) { - return of(DefaultedList.ofSize(size, ItemStack.EMPTY)); - } - - /** - * Returns the inventory size. - */ - @Override - default int size() { - return getItems().size(); - } - - /** - * Checks if the inventory is empty. - * @return true if this inventory has only empty stacks, false otherwise. - */ - @Override - default boolean isEmpty() { - for (int i = 0; i < size(); i++) { - ItemStack stack = getStack(i); - if (!stack.isEmpty()) { - return false; - } - } - return true; - } - - /** - * Retrieves the item in the slot. - */ - @Override - default ItemStack getStack(int slot) { - return getItems().get(slot); - } - - /** - * Removes items from an inventory slot. - * @param slot The slot to remove from. - * @param count How many items to remove. If there are less items in the slot than what are requested, - * takes all items in that slot. - */ - @Override - default ItemStack removeStack(int slot, int count) { - ItemStack result = Inventories.splitStack(getItems(), slot, count); - if (!result.isEmpty()) { - markDirty(); - } - return result; - } - - /** - * Removes all items from an inventory slot. - * @param slot The slot to remove from. - */ - @Override - default ItemStack removeStack(int slot) { - return Inventories.removeStack(getItems(), slot); - } - - /** - * Replaces the current stack in an inventory slot with the provided stack. - * @param slot The inventory slot of which to replace the itemstack. - * @param stack The replacing itemstack. If the stack is too big for - * this inventory ({@link Inventory#getMaxCountPerStack()}), - * it gets resized to this inventory's maximum amount. - */ - @Override - default void setStack(int slot, ItemStack stack) { - getItems().set(slot, stack); - if (stack.getCount() > stack.getMaxCount()) { - stack.setCount(stack.getMaxCount()); - } - } - - /** - * Clears the inventory. - */ - @Override - default void clear() { - getItems().clear(); - } - - /** - * Marks the state as dirty. - * Must be called after changes in the inventory, so that the game can properly save - * the inventory contents and notify neighboring blocks of inventory changes. - */ - @Override - default void markDirty() { - // Override if you want behavior. - } - - /** - * @return true if the player can use the inventory, false otherwise. - */ - @Override - default boolean canPlayerUse(PlayerEntity player) { - return true; - } -} - diff --git a/src/main/java/io/github/skippyall/minions/minion/MinionComponent.java b/src/main/java/io/github/skippyall/minions/minion/MinionComponent.java new file mode 100644 index 0000000..ea5fe3d --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/minion/MinionComponent.java @@ -0,0 +1,14 @@ +package io.github.skippyall.minions.minion; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.component.ComponentType; + +public class MinionComponent { + /*public static final Codec CODEC = RecordCodecBuilder.create(instance -> + instance.group() + ); + public static final ComponentType TYPE = ComponentType.builder().codec(CODEC).build(); +*/ + +} diff --git a/src/main/java/io/github/skippyall/minions/minion/MinionData.java b/src/main/java/io/github/skippyall/minions/minion/MinionData.java index 4e8bc4c..d16a267 100644 --- a/src/main/java/io/github/skippyall/minions/minion/MinionData.java +++ b/src/main/java/io/github/skippyall/minions/minion/MinionData.java @@ -8,10 +8,12 @@ import java.util.UUID; public class MinionData { public UUID uuid; public String name; + public UUID skinUuid; - public MinionData(UUID uuid, String name) { + public MinionData(UUID uuid, String name, UUID skinUuid) { this.uuid = uuid; this.name = name; + this.skinUuid = skinUuid; } public NbtCompound writeNbt() { @@ -19,6 +21,9 @@ public class MinionData { nbt.putUuid("uuid", uuid); nbt.putString("name", name); + if(skinUuid != null) { + nbt.putUuid("skinUuid", skinUuid); + } return nbt; } @@ -26,11 +31,15 @@ public class MinionData { public static MinionData readNbt(NbtCompound nbt) { UUID uuid = nbt.getUuid("uuid"); String name = nbt.getString("name"); + UUID skinUuid = null; + if(nbt.contains("skinUuid")) { + skinUuid = nbt.getUuid("skinUuid"); + } - return new MinionData(uuid, name); + return new MinionData(uuid, name, skinUuid); } public static MinionData fromMinion(MinionFakePlayer minion) { - return new MinionData(minion.getUuid(), minion.getMinionName()); + return new MinionData(minion.getUuid(), minion.getMinionName(), minion.getSkinUuid()); } } diff --git a/src/main/java/io/github/skippyall/minions/minion/MinionItem.java b/src/main/java/io/github/skippyall/minions/minion/MinionItem.java index 03f6087..daadf47 100644 --- a/src/main/java/io/github/skippyall/minions/minion/MinionItem.java +++ b/src/main/java/io/github/skippyall/minions/minion/MinionItem.java @@ -5,12 +5,14 @@ import eu.pb4.polymer.core.api.item.PolymerItemUtils; import io.github.skippyall.minions.fakeplayer.MinionFakePlayer; import net.minecraft.component.DataComponentTypes; import net.minecraft.component.type.NbtComponent; -import net.minecraft.item.*; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemUsageContext; +import net.minecraft.item.Items; import net.minecraft.item.tooltip.TooltipType; import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtElement; import net.minecraft.registry.RegistryWrapper; -import net.minecraft.server.command.DebugMobSpawningCommand; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.ActionResult; @@ -18,12 +20,11 @@ import net.minecraft.util.math.Vec2f; import org.jetbrains.annotations.Nullable; public class MinionItem extends Item implements PolymerItem { - private final boolean canProgram; + public MinionItem(boolean canProgram) { super(new Item.Settings()); - this.canProgram = canProgram; - + this.canProgram = canProgram; } @Override @@ -49,9 +50,15 @@ public class MinionItem extends Item implements PolymerItem { } if(!context.getWorld().isClient) { MinionData data = getData(context.getStack()); - if (data == null) { - MinionFakePlayer.createMinion(name, (ServerWorld) context.getWorld(), (ServerPlayerEntity) context.getPlayer(), canProgram, context.getBlockPos().toCenterPos().add(0,0.5,0), 0, 0); + + if(data == null) { + data = new MinionData(null, name, null); + } + + if (data.uuid == null) { + MinionFakePlayer.createMinion(data, (ServerWorld) context.getWorld(), (ServerPlayerEntity) context.getPlayer(), canProgram, context.getBlockPos().toCenterPos().add(0,0.5,0), 0, 0); }else { + data.name = name; MinionFakePlayer.spawnMinionAt(data, (ServerWorld) context.getWorld(), context.getBlockPos().toCenterPos().add(0,0.5,0), new Vec2f(0, 0)); MinionPersistentState.INSTANCE.addMinion(data); } diff --git a/src/main/java/io/github/skippyall/minions/minion/MinionPersistentState.java b/src/main/java/io/github/skippyall/minions/minion/MinionPersistentState.java index 1c6b590..5b59b34 100644 --- a/src/main/java/io/github/skippyall/minions/minion/MinionPersistentState.java +++ b/src/main/java/io/github/skippyall/minions/minion/MinionPersistentState.java @@ -1,7 +1,9 @@ package io.github.skippyall.minions.minion; import io.github.skippyall.minions.fakeplayer.MinionFakePlayer; -import net.minecraft.nbt.*; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtList; import net.minecraft.registry.RegistryWrapper; import net.minecraft.server.MinecraftServer; import net.minecraft.world.PersistentState; diff --git a/src/main/java/io/github/skippyall/minions/minion/MinionProfileUtils.java b/src/main/java/io/github/skippyall/minions/minion/MinionProfileUtils.java new file mode 100644 index 0000000..e041dc7 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/minion/MinionProfileUtils.java @@ -0,0 +1,71 @@ +package io.github.skippyall.minions.minion; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.ProfileLookupCallback; +import com.mojang.authlib.yggdrasil.ProfileResult; +import net.minecraft.server.MinecraftServer; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ForkJoinPool; + +import static io.github.skippyall.minions.Minions.LOGGER; + +public class MinionProfileUtils { + public static CompletableFuture<@Nullable GameProfile> lookupSkinOwnerProfile(MinecraftServer server, String username) { + CompletableFuture future = new CompletableFuture<>(); + + ForkJoinPool.commonPool().execute(() -> { + try { + server.getGameProfileRepo().findProfilesByNames(new String[]{username}, new ProfileLookupCallback() { + @Override + public void onProfileLookupSucceeded(GameProfile found) { + LOGGER.info("SkinProfile: {}", found); + try { + getSkinOwnerProfile(server, found.getId()).thenAccept(future::complete); + } catch (Throwable ex) { + LOGGER.warn("Exception during Game Profile creation", ex); + } + } + + @Override + public void onProfileLookupFailed(String profileName, Exception exception) { + LOGGER.warn("Lookup Error: ", exception); + future.complete(null); + } + }); + } catch (Throwable e) { + LOGGER.warn("Failed to get UUID for username " + username, e); + future.complete(null); + } + }); + + return future; + } + + public static CompletableFuture<@Nullable GameProfile> getSkinOwnerProfile(MinecraftServer server, @Nullable UUID uuid) { + CompletableFuture future = new CompletableFuture<>(); + future.completeAsync(() -> { + GameProfile profile = null; + if(uuid != null) { + ProfileResult result = server.getSessionService().fetchProfile(uuid, true); + if (result != null) { + profile = result.profile(); + LOGGER.info("Full SkinProfile: {}", profile); + } + } + return profile; + }); + return future; + } + + public static GameProfile makeNewMinionProfile(@Nullable UUID uuidMinion, String username, @Nullable GameProfile skinProfile) { + GameProfile newProfile = new GameProfile(uuidMinion != null ? uuidMinion : UUID.randomUUID(), username); + if (skinProfile != null) { + newProfile.getProperties().putAll(skinProfile.getProperties()); + } + LOGGER.info("Minion Profile: {}", newProfile); + return newProfile; + } +} diff --git a/src/main/java/io/github/skippyall/minions/mixins/Debug2Mixin.java b/src/main/java/io/github/skippyall/minions/mixins/Debug2Mixin.java deleted file mode 100644 index 3fa0e58..0000000 --- a/src/main/java/io/github/skippyall/minions/mixins/Debug2Mixin.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.skippyall.minions.mixins; - -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.world.PlayerSaveHandler; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.util.Optional; - -@Mixin(PlayerSaveHandler.class) -public class Debug2Mixin { - @Inject(method = "method_55788", at = @At("HEAD")) - public void debug(PlayerEntity playerEntity, NbtCompound nbt, CallbackInfoReturnable cir) { - System.out.println("loadPlayerData " + playerEntity.getNameForScoreboard()); - } - - @Inject(method = "loadPlayerData(Lnet/minecraft/entity/player/PlayerEntity;Ljava/lang/String;)Ljava/util/Optional;", at = @At("RETURN")) - public void debug(PlayerEntity player, String extension, CallbackInfoReturnable> cir) { - System.out.println(cir.getReturnValue().isEmpty() + player.getUuidAsString()); - } -} diff --git a/src/main/java/io/github/skippyall/minions/mixins/DebugMixin.java b/src/main/java/io/github/skippyall/minions/mixins/DebugMixin.java deleted file mode 100644 index 6acd469..0000000 --- a/src/main/java/io/github/skippyall/minions/mixins/DebugMixin.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.skippyall.minions.mixins; - -import com.llamalad7.mixinextras.sugar.Local; -import io.github.skippyall.minions.fakeplayer.MinionFakePlayer; -import net.minecraft.entity.Entity; -import net.minecraft.nbt.NbtCompound; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -//@Mixin(SectionedEntityCache.class) -@Mixin(Entity.class) -public class DebugMixin { - /*@Inject(method = "forEachInBox", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/ChunkSectionPos;asLong(III)J", shift = At.Shift.BEFORE, ordinal = 0)) - private void debug(Box box, LazyIterationConsumer> consumer, CallbackInfo ci) { - System.out.println("call"); - }*/ - @Inject(method = "readNbt", at = @At("HEAD")) - public void debug(NbtCompound nbt, CallbackInfo ci) { - if ((Object) this instanceof MinionFakePlayer) { - System.out.println("readNBT"); - } - } - - @Inject(method = "setPos", at = @At("HEAD")) - public void debug(double x, double y, double z, CallbackInfo ci) { - if ((Object) this instanceof MinionFakePlayer) { - //System.out.println("Set Minion Pos to " + x + " " + y + " " + z); - } - } -} diff --git a/src/main/java/io/github/skippyall/minions/mixins/GameProfileMixin.java b/src/main/java/io/github/skippyall/minions/mixins/GameProfileMixin.java deleted file mode 100644 index 96bcf99..0000000 --- a/src/main/java/io/github/skippyall/minions/mixins/GameProfileMixin.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.github.skippyall.minions.mixins; - -import com.mojang.authlib.GameProfile; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Mutable; -import org.spongepowered.asm.mixin.gen.Accessor; - -import java.util.UUID; - -@Mixin(value = GameProfile.class, remap = false) -public interface GameProfileMixin{ - @Mutable - @Accessor("id") - void setId(UUID id); -} diff --git a/src/main/java/io/github/skippyall/minions/mixins/MobEntityMixin.java b/src/main/java/io/github/skippyall/minions/mixins/MobEntityMixin.java index 9ebd36d..3e161b5 100644 --- a/src/main/java/io/github/skippyall/minions/mixins/MobEntityMixin.java +++ b/src/main/java/io/github/skippyall/minions/mixins/MobEntityMixin.java @@ -16,7 +16,7 @@ import org.spongepowered.asm.mixin.injection.Redirect; public abstract class MobEntityMixin { @Redirect(method = "checkDespawn", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;getClosestPlayer(Lnet/minecraft/entity/Entity;D)Lnet/minecraft/entity/player/PlayerEntity;")) public PlayerEntity checkMobDespawningMinion(World instance, Entity entity, double maxDistance) { - return instance.getClosestPlayer(entity.getX(), entity.getY(), entity.getZ(), maxDistance, EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR.and(entity1 -> { + return instance.getClosestPlayer(entity.getX(), entity.getY(), entity.getZ(), maxDistance, EntityPredicates.EXCEPT_SPECTATOR.and(entity1 -> { if(entity1 instanceof ServerPlayerEntity player) { if(player instanceof MinionFakePlayer minion) { return MobSpawningModule.canMinionDespawnMobs(minion); diff --git a/src/main/java/io/github/skippyall/minions/mixins/SleepManagerMixin.java b/src/main/java/io/github/skippyall/minions/mixins/SleepManagerMixin.java new file mode 100644 index 0000000..64e7719 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/mixins/SleepManagerMixin.java @@ -0,0 +1,21 @@ +package io.github.skippyall.minions.mixins; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import io.github.skippyall.minions.fakeplayer.MinionFakePlayer; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.SleepManager; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(SleepManager.class) +public class SleepManagerMixin { + @WrapOperation(method = "update", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayerEntity;isSpectator()Z")) + public boolean excludeMinions(ServerPlayerEntity instance, Operation original) { + if (instance instanceof MinionFakePlayer) { + return true; + } else { + return original.call(instance); + } + } +} diff --git a/src/main/java/io/github/skippyall/minions/mixins/SpawnHelperMixin.java b/src/main/java/io/github/skippyall/minions/mixins/SpawnHelperMixin.java index 650d0da..f79f7a4 100644 --- a/src/main/java/io/github/skippyall/minions/mixins/SpawnHelperMixin.java +++ b/src/main/java/io/github/skippyall/minions/mixins/SpawnHelperMixin.java @@ -15,7 +15,7 @@ import org.spongepowered.asm.mixin.injection.Redirect; public class SpawnHelperMixin { @Redirect(method = "spawnEntitiesInChunk(Lnet/minecraft/entity/SpawnGroup;Lnet/minecraft/server/world/ServerWorld;Lnet/minecraft/world/chunk/Chunk;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/SpawnHelper$Checker;Lnet/minecraft/world/SpawnHelper$Runner;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/world/ServerWorld;getClosestPlayer(DDDDZ)Lnet/minecraft/entity/player/PlayerEntity;")) private static PlayerEntity checkMobSpawningMinion(ServerWorld instance, double x, double y, double z, double maxDistance, boolean b) { - return instance.getClosestPlayer(x, y, z, maxDistance, EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR.and(entity -> { + return instance.getClosestPlayer(x, y, z, maxDistance, EntityPredicates.EXCEPT_SPECTATOR.and(entity -> { if(entity instanceof ServerPlayerEntity player) { if(player instanceof MinionFakePlayer minion) { return MobSpawningModule.canMinionSpawnMobs(minion); diff --git a/src/main/java/io/github/skippyall/minions/module/ChatModule.java b/src/main/java/io/github/skippyall/minions/module/ChatModule.java index 8946db5..1f8dc86 100644 --- a/src/main/java/io/github/skippyall/minions/module/ChatModule.java +++ b/src/main/java/io/github/skippyall/minions/module/ChatModule.java @@ -8,7 +8,6 @@ import net.minecraft.util.Identifier; import java.util.ArrayList; import java.util.Arrays; -import java.util.List; import static io.github.skippyall.minions.module.Modules.register; diff --git a/src/main/java/io/github/skippyall/minions/module/MobSpawningModule.java b/src/main/java/io/github/skippyall/minions/module/MobSpawningModule.java index ff79fb7..ab6a16b 100644 --- a/src/main/java/io/github/skippyall/minions/module/MobSpawningModule.java +++ b/src/main/java/io/github/skippyall/minions/module/MobSpawningModule.java @@ -1,15 +1,24 @@ package io.github.skippyall.minions.module; +import io.github.skippyall.minions.Minions; import io.github.skippyall.minions.fakeplayer.MinionFakePlayer; +import net.minecraft.item.Items; +import net.minecraft.util.Identifier; + +import java.util.List; + +import static io.github.skippyall.minions.module.Modules.register; public class MobSpawningModule { - + public static final SimpleModuleItem MOB_SPAWNING_MODULE = register(Identifier.of(Minions.MOD_ID, "mob_spawning_module"), new SimpleModuleItem(List.of(), List.of(), Items.SPAWNER)); public static boolean canMinionSpawnMobs(MinionFakePlayer minion) { - return false; + return minion.getModuleInventory().hasModule(MOB_SPAWNING_MODULE); } public static boolean canMinionDespawnMobs(MinionFakePlayer minion) { - return false; + return minion.getModuleInventory().hasModule(MOB_SPAWNING_MODULE); } + + public static void registerMe() {} } diff --git a/src/main/java/io/github/skippyall/minions/module/ModuleItem.java b/src/main/java/io/github/skippyall/minions/module/ModuleItem.java index 070a65f..a2a21e1 100644 --- a/src/main/java/io/github/skippyall/minions/module/ModuleItem.java +++ b/src/main/java/io/github/skippyall/minions/module/ModuleItem.java @@ -2,12 +2,11 @@ package io.github.skippyall.minions.module; import io.github.skippyall.minions.command.Command; import io.github.skippyall.minions.program.block.CodeBlock; -import net.minecraft.text.Text; +import net.minecraft.item.ItemConvertible; import java.util.List; -public interface ModuleItem { - +public interface ModuleItem extends ItemConvertible { List> getCodeBlocks(); List getCommands(); diff --git a/src/main/java/io/github/skippyall/minions/module/Modules.java b/src/main/java/io/github/skippyall/minions/module/Modules.java index 56bad67..568bf58 100644 --- a/src/main/java/io/github/skippyall/minions/module/Modules.java +++ b/src/main/java/io/github/skippyall/minions/module/Modules.java @@ -6,12 +6,11 @@ import net.minecraft.registry.Registry; import net.minecraft.util.Identifier; public class Modules { - MoveModule MOVE = new MoveModule(); - public static void register() { ChatModule.registerMe(); MountModule.registerMe(); MoveModule.registerMe(); + MobSpawningModule.registerMe(); } public static T register(Identifier id, T item) { diff --git a/src/main/resources/data/minions/lang/en_us.json b/src/main/resources/data/minions/lang/en_us.json new file mode 100644 index 0000000..d7cf7b3 --- /dev/null +++ b/src/main/resources/data/minions/lang/en_us.json @@ -0,0 +1,10 @@ +{ + "minions.gui.main.commands": "Commands", + "minions.gui.main.programming": "Programming", + "minions.gui.main.modules": "Modules", + "minions.gui.main.inventory": "Inventory", + "minions.gui.inventory.title": "%s's Inventory", + "minions.gui.module_commands.title": "Commands", + "minions.gui.commands.title": "%s's Commands", + "minions.gui.modules.title": "%s's Modules" +} \ No newline at end of file diff --git a/src/main/resources/data/minions/recipe/minion.json b/src/main/resources/data/minions/recipe/minion.json index 8cd6552..1bac50d 100644 --- a/src/main/resources/data/minions/recipe/minion.json +++ b/src/main/resources/data/minions/recipe/minion.json @@ -7,7 +7,7 @@ ], "key": { "H": { - "tag": "minions:heads" + "tag": "minecraft:skulls" }, "R": { "item": "minecraft:redstone_torch" diff --git a/src/main/resources/data/minions/tags/item/heads.json b/src/main/resources/data/minions/tags/item/heads.json deleted file mode 100644 index d2f3ddd..0000000 --- a/src/main/resources/data/minions/tags/item/heads.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "values": [ - "minecraft:creeper_head", - "minecraft:zombie_head", - "minecraft:piglin_head", - "minecraft:wither_skeleton_skull", - "minecraft:skeleton_skull", - "minecraft:dragon_head", - "minecraft:player_head" - ] -} \ No newline at end of file diff --git a/src/main/resources/minions.mixins.json b/src/main/resources/minions.mixins.json index fbe4f6e..0246eee 100644 --- a/src/main/resources/minions.mixins.json +++ b/src/main/resources/minions.mixins.json @@ -6,16 +6,14 @@ "mixins": [ "ChunkTicketManagerFixMixin", "ConnectionMixin", - "Debug2Mixin", - "DebugMixin", "EntityAccessor", - "GameProfileMixin", "MinecraftServerMixin", "MobEntityMixin", "PlayerListEntryS2CPacket$EntryMixin", "PlayerListMixin", "ServerPlayerMixin", "ServerPlayNetworkHandlerMixin", + "SleepManagerMixin", "SpawnHelperMixin" ], "client": [],