diff --git a/src/main/java/io/github/skippyall/minions/Minions.java b/src/main/java/io/github/skippyall/minions/Minions.java index 536c19d..3b793cc 100644 --- a/src/main/java/io/github/skippyall/minions/Minions.java +++ b/src/main/java/io/github/skippyall/minions/Minions.java @@ -1,9 +1,12 @@ package io.github.skippyall.minions; import eu.pb4.polymer.core.api.entity.PolymerEntityUtils; +import io.github.skippyall.minions.fakeplayer.MinionFakePlayer; import io.github.skippyall.minions.minion.MinionItem; +import io.github.skippyall.minions.minion.MinionPersistentState; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; import net.minecraft.registry.Registries; import net.minecraft.registry.Registry; import net.minecraft.server.MinecraftServer; @@ -11,16 +14,45 @@ import net.minecraft.util.Identifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.List; + public class Minions implements ModInitializer { public static final String MOD_ID = "minions"; public static final MinionItem MINION_ITEM = Registry.register(Registries.ITEM, Identifier.of(MOD_ID, "minion"), new MinionItem(false)); public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); + + private static final List executeOnNextTick = new ArrayList<>(); + @Override public void onInitialize() { + LOGGER.debug("Add Customthing"); PolymerEntityUtils.registerType(); ServerLifecycleEvents.SERVER_STARTED.register(server -> { + MinionPersistentState.create(server); + MinionPersistentState.INSTANCE.getMinionData().forEach(data -> { + System.out.println("spawn Minion " + data.name); + MinionFakePlayer.spawnMinion(data, server.getOverworld()); + }); + }); + ServerTickEvents.START_SERVER_TICK.register(server -> { + exec(() -> { + for (Runnable run:executeOnNextTick) { + run.run(); + } + executeOnNextTick.clear(); + }); + }); + } + private static synchronized void exec(Runnable run) { + run.run(); + } + + public static void addExecuteOnNextTick(Runnable run) { + exec(() -> { + executeOnNextTick.add(run); }); } } 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 f46bb8c..34021b3 100644 --- a/src/main/java/io/github/skippyall/minions/fakeplayer/MinionFakePlayer.java +++ b/src/main/java/io/github/skippyall/minions/fakeplayer/MinionFakePlayer.java @@ -4,7 +4,9 @@ import com.mojang.authlib.GameProfile; import com.mojang.authlib.yggdrasil.ProfileResult; import io.github.skippyall.minions.Minions; import io.github.skippyall.minions.minion.MinionInventory; +import io.github.skippyall.minions.minion.MinionPersistentState; import io.github.skippyall.minions.minion.ModuleInventory; +import io.github.skippyall.minions.mixins.GameProfileMixin; import io.github.skippyall.minions.program.runtime.MinionRuntime; import net.minecraft.block.BlockState; import net.minecraft.entity.Entity; @@ -18,13 +20,11 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; import net.minecraft.network.DisconnectionInfo; import net.minecraft.network.NetworkSide; -import net.minecraft.network.packet.StatusPackets; 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.network.packet.s2c.query.PingResultS2CPacket; import net.minecraft.server.MinecraftServer; import net.minecraft.server.ServerTask; import net.minecraft.server.network.ConnectedClientData; @@ -34,12 +34,12 @@ import net.minecraft.text.Text; import net.minecraft.text.TranslatableTextContent; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; -import net.minecraft.util.Uuids; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; import net.minecraft.world.GameMode; import net.minecraft.world.TeleportTarget; +import java.util.Optional; import java.util.UUID; public class MinionFakePlayer extends ServerPlayerEntity { @@ -56,30 +56,75 @@ public class MinionFakePlayer extends ServerPlayerEntity { public static void createMinion(String username, 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(); + } + } + 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(), false, 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(); + } + }); + } + + public static void spawnMinion(MinionPersistentState.MinionData data, ServerWorld level) { + MinecraftServer server = level.getServer(); + server.getUserCache().findByNameAsync(data.name).thenAcceptAsync((optional) -> { GameProfile profile = null; - if(optional.isPresent()){ - UUID uuid = optional.get().getId(); - ProfileResult result = server.getSessionService().fetchProfile(uuid, true); - if(result != 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(Uuids.getOfflinePlayerUuid(username), username); + if (profile == null) { + profile = new GameProfile(new UUID(0, 0), data.name); } - MinionFakePlayer instance = new MinionFakePlayer(server, level, profile, SyncedClientOptions.createDefault(), false, 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; + GameProfile newProfile = new GameProfile(data.uuid, data.name); + newProfile.getProperties().putAll(profile.getProperties()); + GameProfile finalProfile = newProfile; + ((GameProfileMixin)finalProfile).setId(data.uuid); + Minions.addExecuteOnNextTick(() -> { + MinionFakePlayer instance = new MinionFakePlayer(server, level, finalProfile, SyncedClientOptions.createDefault(), false, data.programmable); + System.out.println(instance.getPos()); + server.getPlayerManager().onPlayerConnect(new FakeClientConnection(NetworkSide.SERVERBOUND), instance, new ConnectedClientData(finalProfile, 0, instance.getClientOptions(), false)); + System.out.println(instance.getPos()); + instance.setHealth(20.0F); + instance.unsetRemoved(); + 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; + }); }); } @@ -117,7 +162,7 @@ public class MinionFakePlayer extends ServerPlayerEntity { { super(server, worldIn, profile, cli); isAShadow = shadow; - + this.programmable = programmable; } public boolean isProgrammable() { @@ -155,11 +200,11 @@ public class MinionFakePlayer extends ServerPlayerEntity { if (!isUsingItem()) super.onEquipStack(slot, previous, stack); } - @Override + /*@Override public void kill() { kill(Text.literal("Killed")); - } + }*/ public void kill(Text reason) { @@ -172,6 +217,8 @@ public class MinionFakePlayer extends ServerPlayerEntity { this.networkHandler.onDisconnected(new DisconnectionInfo(reason)); })); } + + MinionPersistentState.INSTANCE.removeMinion(this); } @Override @@ -307,4 +354,8 @@ public class MinionFakePlayer extends ServerPlayerEntity { super.readCustomDataFromNbt(nbt); moduleInventory.readNbt(nbt.getCompound("modules"), getRegistryManager()); } + + public String getMinionName() { + return getGameProfile().getName(); + } } \ No newline at end of file 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 b327ebd..2548902 100644 --- a/src/main/java/io/github/skippyall/minions/minion/MinionPersistentState.java +++ b/src/main/java/io/github/skippyall/minions/minion/MinionPersistentState.java @@ -16,7 +16,7 @@ public class MinionPersistentState extends PersistentState { public static MinionPersistentState INSTANCE; - public static List minionData = new ArrayList<>(); + private List minionData = new ArrayList<>(); @Override public NbtCompound writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { @@ -30,10 +30,42 @@ public class MinionPersistentState extends PersistentState { public static MinionPersistentState read(NbtCompound compound, RegistryWrapper.WrapperLookup lookup) { NbtList list = compound.getList("minions", NbtElement.COMPOUND_TYPE); + MinionPersistentState instance = new MinionPersistentState(); for(NbtElement element : list) { - minionData.add(MinionData.readNbt((NbtCompound) element)); + instance.addMinion(MinionData.readNbt((NbtCompound) element)); } - return new MinionPersistentState(); + return instance; + } + + public void addMinion(MinionFakePlayer minion) { + addMinion(MinionData.fromMinion(minion)); + } + + public void addMinion(MinionData data) { + System.out.println("add Minion " + data.name); + minionData.add(data); + markDirty(); + } + + public void removeMinion(MinionFakePlayer minionData) { + removeMinion(minionData.getUuid()); + } + + public void removeMinion(UUID minionUUID) { + MinionData removal = null; + for (MinionData data : minionData) { + if (data.uuid.equals(minionUUID)) { + removal = data; + } + } + if (removal != null) { + minionData.remove(removal); + } + markDirty(); + } + + public List getMinionData() { + return minionData; } public static void create(MinecraftServer server) { @@ -42,9 +74,13 @@ public class MinionPersistentState extends PersistentState { public static class MinionData { public UUID uuid; + public String name; + public boolean programmable; - public MinionData(UUID uuid) { + public MinionData(UUID uuid, String name, boolean programmable) { this.uuid = uuid; + this.name = name; + this.programmable = programmable; } public NbtCompound writeNbt() { @@ -62,6 +98,8 @@ public class MinionPersistentState extends PersistentState { nbt.put("rotation", rotList);*/ nbt.putUuid("uuid", uuid); + nbt.putString("name", name); + nbt.putBoolean("programmable", programmable); return nbt; } @@ -79,12 +117,14 @@ public class MinionPersistentState extends PersistentState { Vec2f rot = new Vec2f(yaw, pitch);*/ UUID uuid = nbt.getUuid("uuid"); + String name = nbt.getString("name"); + boolean programmable = nbt.getBoolean("programmable"); - return new MinionData(uuid); + return new MinionData(uuid, name, programmable); } public static MinionData fromMinion(MinionFakePlayer minion) { - return new MinionData(minion.getUuid()); + return new MinionData(minion.getUuid(), minion.getMinionName(), minion.isProgrammable()); } } } diff --git a/src/main/java/io/github/skippyall/minions/mixins/Debug2Mixin.java b/src/main/java/io/github/skippyall/minions/mixins/Debug2Mixin.java new file mode 100644 index 0000000..3fa0e58 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/mixins/Debug2Mixin.java @@ -0,0 +1,24 @@ +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 new file mode 100644 index 0000000..7512276 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/mixins/DebugMixin.java @@ -0,0 +1,32 @@ +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 new file mode 100644 index 0000000..96bcf99 --- /dev/null +++ b/src/main/java/io/github/skippyall/minions/mixins/GameProfileMixin.java @@ -0,0 +1,15 @@ +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/PlayerListMixin.java b/src/main/java/io/github/skippyall/minions/mixins/PlayerListMixin.java index 6ca61a0..9bb04ce 100644 --- a/src/main/java/io/github/skippyall/minions/mixins/PlayerListMixin.java +++ b/src/main/java/io/github/skippyall/minions/mixins/PlayerListMixin.java @@ -14,6 +14,8 @@ import net.minecraft.server.network.ConnectedClientData; import net.minecraft.server.network.ServerPlayNetworkHandler; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; +import net.minecraft.text.Text; +import org.apache.logging.log4j.core.jmx.Server; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; diff --git a/src/main/resources/minions.mixins.json b/src/main/resources/minions.mixins.json index 476382c..7f9d41f 100644 --- a/src/main/resources/minions.mixins.json +++ b/src/main/resources/minions.mixins.json @@ -5,10 +5,14 @@ "compatibilityLevel": "JAVA_17", "mixins": [ "ConnectionMixin", + "Debug2Mixin", + "DebugMixin", + "GameProfileMixin", "MinecraftServerMixin", "PlayerListEntryS2CPacket$EntryMixin", "PlayerListMixin", - "ServerPlayerMixin" + "ServerPlayerMixin", + "ServerPlayNetworkHandlerMixin" ], "client": [], "server": [],