feat: nPC进一步完善,自动加载,杀死删除
目前问题是创建退出世界后在加入单人看不见该NPC,需远离跟踪范围后才能重新看见(暂时未排查出具体原因,不过问题应该定位在ChunkMap这里)
This commit is contained in:
parent
f1f5e204a2
commit
872c69f4fe
|
|
@ -32,7 +32,7 @@ emi_version=1.1.22+1.20.1
|
|||
player_anim_version=1.0.2-rc1+1.20
|
||||
geckolib_version=4.2.1
|
||||
curios_version=5.5.0+1.20.1
|
||||
lib39_version=0.5.5
|
||||
lib39_version=0.5.6
|
||||
accore_version=1.20.1-26H7
|
||||
super_lead_version=1.20.1-1.2.1
|
||||
## Mod Properties
|
||||
|
|
|
|||
|
|
@ -0,0 +1,383 @@
|
|||
/*
|
||||
* Copyright 2025-2026 R3944Realms
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.r3944realms.eroticdungeongame.content.entity.npc;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.core.LayeredRegistryAccess;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.chat.ChatType;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.PlayerChatMessage;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.PlayerAdvancements;
|
||||
import net.minecraft.server.RegistryLayer;
|
||||
import net.minecraft.server.ServerScoreboard;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.players.*;
|
||||
import net.minecraft.stats.ServerStatsCounter;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import top.r3944realms.eroticdungeongame.EroticDungeon;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class FakePlayerList extends PlayerList {
|
||||
private final List<NPCServerPlayer> npcs = new ArrayList<>();
|
||||
private final Map<UUID, NPCServerPlayer> npcsByUUID = Maps.newHashMap();
|
||||
private int sendAllNPCInfoIn;
|
||||
private final List<ServerPlayer> npcsView = java.util.Collections.unmodifiableList(npcs);
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
public FakePlayerList(MinecraftServer server, LayeredRegistryAccess<RegistryLayer> registries, List<NPCServerPlayer> npcs, Map<UUID, NPCServerPlayer> npcsByUUID) {
|
||||
super(server, registries, null, -1);
|
||||
this.npcs.addAll(npcs);
|
||||
this.npcsByUUID.putAll(npcsByUUID);
|
||||
}
|
||||
|
||||
private static void noSupportOperate() {
|
||||
EroticDungeon.getLogger().error("Not supported in FakePlayerList");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void placeNewPlayer(@NotNull Connection netManager, @NotNull ServerPlayer player) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateEntireScoreboard(@NotNull ServerScoreboard scoreboard, @NotNull ServerPlayer player) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addWorldborderListener(@NotNull ServerLevel level) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CompoundTag load(@NotNull ServerPlayer player) {
|
||||
noSupportOperate();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void save(@NotNull ServerPlayer player) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(@NotNull ServerPlayer player) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Component canPlayerLogin(@NotNull SocketAddress socketAddress, @NotNull GameProfile gameProfile) {
|
||||
noSupportOperate();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ServerPlayer getPlayerForLogin(@NotNull GameProfile profile) {
|
||||
noSupportOperate();
|
||||
return super.getPlayerForLogin(profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ServerPlayer respawn(@NotNull ServerPlayer player, boolean keepEverything) {
|
||||
noSupportOperate();
|
||||
return super.respawn(player, keepEverything);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPlayerPermissionLevel(@NotNull ServerPlayer player) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastAll(@NotNull Packet<?> packet) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastAll(@NotNull Packet<?> packet, @NotNull ResourceKey<Level> dimension) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastSystemToTeam(@NotNull Player player, @NotNull Component message) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastSystemToAllExceptTeam(@NotNull Player player, @NotNull Component message) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String @NotNull [] getPlayerNamesArray() {
|
||||
noSupportOperate();
|
||||
return super.getPlayerNamesArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull UserBanList getBans() {
|
||||
noSupportOperate();
|
||||
return super.getBans();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull IpBanList getIpBans() {
|
||||
noSupportOperate();
|
||||
return super.getIpBans();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void op(@NotNull GameProfile profile) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deop(@NotNull GameProfile profile) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWhiteListed(@NotNull GameProfile profile) {
|
||||
noSupportOperate();
|
||||
return super.isWhiteListed(profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOp(@NotNull GameProfile profile) {
|
||||
noSupportOperate();
|
||||
return super.isOp(profile);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ServerPlayer getPlayerByName(@NotNull String username) {
|
||||
return super.getPlayerByName(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcast(@Nullable Player except, double x, double y, double z, double radius, @NotNull ResourceKey<Level> dimension, @NotNull Packet<?> packet) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAll() {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull UserWhiteList getWhiteList() {
|
||||
noSupportOperate();
|
||||
return super.getWhiteList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String @NotNull [] getWhiteListNames() {
|
||||
noSupportOperate();
|
||||
return super.getWhiteListNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ServerOpList getOps() {
|
||||
noSupportOperate();
|
||||
return super.getOps();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String @NotNull [] getOpNames() {
|
||||
noSupportOperate();
|
||||
return super.getOpNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadWhiteList() {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendLevelInfo(@NotNull ServerPlayer player, @NotNull ServerLevel level) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendAllPlayerInfo(@NotNull ServerPlayer player) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayerCount() {
|
||||
return this.npcs.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxPlayers() {
|
||||
noSupportOperate();
|
||||
return super.getMaxPlayers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUsingWhitelist() {
|
||||
noSupportOperate();
|
||||
return super.isUsingWhitelist();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUsingWhiteList(boolean whitelistEnabled) {
|
||||
noSupportOperate();
|
||||
super.setUsingWhiteList(whitelistEnabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<ServerPlayer> getPlayersWithAddress(@NotNull String address) {
|
||||
List<ServerPlayer> list = Lists.newArrayList();
|
||||
|
||||
for(ServerPlayer serverplayer : this.npcs) {
|
||||
if (serverplayer.getIpAddress().equals(address)) {
|
||||
list.add(serverplayer);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewDistance() {
|
||||
noSupportOperate();
|
||||
return super.getViewDistance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSimulationDistance() {
|
||||
noSupportOperate();
|
||||
return super.getSimulationDistance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull MinecraftServer getServer() {
|
||||
return super.getServer();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CompoundTag getSingleplayerData() {
|
||||
noSupportOperate();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAllowCheatsForAllPlayers(boolean allowCheatsForAllPlayers) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAll() {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastSystemMessage(@NotNull Component message, boolean bypassHiddenChat) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastSystemMessage(@NotNull Component serverMessage, @NotNull Function<ServerPlayer, Component> playerMessageFactory, boolean bypassHiddenChat) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastChatMessage(@NotNull PlayerChatMessage message, @NotNull CommandSourceStack sender, ChatType.@NotNull Bound boundChatType) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastChatMessage(@NotNull PlayerChatMessage message, @NotNull ServerPlayer sender, ChatType.@NotNull Bound boundChatType) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ServerStatsCounter getPlayerStats(@NotNull Player player) {
|
||||
noSupportOperate();
|
||||
return super.getPlayerStats(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PlayerAdvancements getPlayerAdvancements(@NotNull ServerPlayer player) {
|
||||
noSupportOperate();
|
||||
return super.getPlayerAdvancements(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setViewDistance(int viewDistance) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSimulationDistance(int simulationDistance) {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<ServerPlayer> getPlayers() {
|
||||
return this.npcsView;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ServerPlayer getPlayer(@NotNull UUID playerUUID) {
|
||||
return npcsByUUID.get(playerUUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBypassPlayerLimit(@NotNull GameProfile profile) {
|
||||
noSupportOperate();
|
||||
return super.canBypassPlayerLimit(profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadResources() {
|
||||
noSupportOperate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowCheatsForAllPlayers() {
|
||||
noSupportOperate();
|
||||
return super.isAllowCheatsForAllPlayers();
|
||||
}
|
||||
}
|
||||
|
|
@ -23,21 +23,23 @@ import com.mojang.logging.LogUtils;
|
|||
import com.mojang.serialization.Dynamic;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.core.LayeredRegistryAccess;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.core.RegistrySynchronization;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.nbt.NbtUtils;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.*;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.PacketFlow;
|
||||
import net.minecraft.network.protocol.game.*;
|
||||
import net.minecraft.network.protocol.status.ServerStatus;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.PlayerAdvancements;
|
||||
import net.minecraft.server.RegistryLayer;
|
||||
import net.minecraft.server.ServerScoreboard;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
|
|
@ -45,7 +47,6 @@ import net.minecraft.server.level.ServerPlayer;
|
|||
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
||||
import net.minecraft.server.players.GameProfileCache;
|
||||
import net.minecraft.server.players.PlayerList;
|
||||
import net.minecraft.stats.ServerStatsCounter;
|
||||
import net.minecraft.stats.Stats;
|
||||
import net.minecraft.tags.TagNetworkSerialization;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
|
|
@ -60,23 +61,26 @@ import net.minecraft.world.level.dimension.DimensionType;
|
|||
import net.minecraft.world.level.storage.LevelData;
|
||||
import net.minecraft.world.scores.Objective;
|
||||
import net.minecraft.world.scores.PlayerTeam;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.OnDatapackSyncEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import top.r3944realms.eroticdungeongame.core.network.EDGNetworkHandler;
|
||||
import top.r3944realms.eroticdungeongame.core.network.NPCEmptyClientConnection;
|
||||
import top.r3944realms.eroticdungeongame.core.network.toClient.NPCInfoUpdatePacket;
|
||||
import top.r3944realms.eroticdungeongame.core.storage.NPCDataStorage;
|
||||
import top.r3944realms.lib39.util.nbt.NBTReader;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class NPCPlayerList {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static final int SEND_PLAYER_INFO_INTERVAL = 600;
|
||||
private static final SimpleDateFormat BAN_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z");
|
||||
private final LayeredRegistryAccess<RegistryLayer> registries;
|
||||
private final RegistryAccess.Frozen synchronizedRegistries;
|
||||
private final NPCDataStorage npcIo;
|
||||
|
|
@ -86,13 +90,13 @@ public class NPCPlayerList {
|
|||
private int sendAllNPCInfoIn;
|
||||
private final List<NPCServerPlayer> npcsView = java.util.Collections.unmodifiableList(npcs);
|
||||
public static final Component CHAT_FILTERED_FULL = Component.translatable("chat.filtered_full");
|
||||
public NPCPlayerList(MinecraftServer server, LayeredRegistryAccess<RegistryLayer> registries, NPCDataStorage npcDataStorage, int maxPlayers) {
|
||||
public NPCPlayerList(MinecraftServer server, LayeredRegistryAccess<RegistryLayer> registries, NPCDataStorage npcDataStorage) {
|
||||
this.server = server;
|
||||
this.registries = registries;
|
||||
this.synchronizedRegistries = (new RegistryAccess.ImmutableRegistryAccess(RegistrySynchronization.networkedRegistries(registries))).freeze();
|
||||
this.npcIo = npcDataStorage;
|
||||
}
|
||||
public void placeNewNPC(Connection netManager, NPCServerPlayer npc) {
|
||||
public void placeNewNPC(Connection netManager, @NotNull NPCServerPlayer npc) {
|
||||
GameProfile gameprofile = npc.getGameProfile();
|
||||
GameProfileCache gameprofilecache = this.server.getProfileCache();
|
||||
String s;
|
||||
|
|
@ -106,14 +110,7 @@ public class NPCPlayerList {
|
|||
CompoundTag compoundtag = this.load(npc);
|
||||
//noinspection deprecation
|
||||
ResourceKey<Level> resourcekey = compoundtag != null ? DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, compoundtag.get("Dimension"))).resultOrPartial(LOGGER::error).orElse(Level.OVERWORLD) : Level.OVERWORLD;
|
||||
ServerLevel serverlevel = this.server.getLevel(resourcekey);
|
||||
ServerLevel serverlevel1;
|
||||
if (serverlevel == null) {
|
||||
LOGGER.warn("Unknown respawn dimension {}, defaulting to overworld", resourcekey);
|
||||
serverlevel1 = this.server.overworld();
|
||||
} else {
|
||||
serverlevel1 = serverlevel;
|
||||
}
|
||||
ServerLevel serverlevel1 = getServerLevel(resourcekey);
|
||||
|
||||
npc.setServerLevel(serverlevel1);
|
||||
String s1 = "local";
|
||||
|
|
@ -135,8 +132,10 @@ public class NPCPlayerList {
|
|||
servergamepacketlistenerimpl.send(new ClientboundChangeDifficultyPacket(leveldata.getDifficulty(), leveldata.isDifficultyLocked()));
|
||||
servergamepacketlistenerimpl.send(new ClientboundPlayerAbilitiesPacket(npc.getAbilities()));
|
||||
servergamepacketlistenerimpl.send(new ClientboundSetCarriedItemPacket(npc.getInventory().selected));
|
||||
// todo: 数据同步事件(对于NPC类) 可以搞个继承类复用这个事件
|
||||
// net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.OnDatapackSyncEvent(this, npc));
|
||||
|
||||
//数据同步事件(对于NPC类) 搞个继承类复用这个事件 (如果有模组用这个PlayerList搞其它事情可能会导致BUG)
|
||||
MinecraftForge.EVENT_BUS.post(new OnDatapackSyncEvent(this.toFakePlayerList(), npc));
|
||||
|
||||
servergamepacketlistenerimpl.send(new ClientboundUpdateRecipesPacket(this.server.getRecipeManager().getRecipes()));
|
||||
servergamepacketlistenerimpl.send(new ClientboundUpdateTagsPacket(TagNetworkSerialization.serializeTagsToNetwork(this.registries)));
|
||||
this.sendPlayerPermissionLevel(npc);
|
||||
|
|
@ -145,7 +144,7 @@ public class NPCPlayerList {
|
|||
this.updateEntireScoreboard(serverlevel1.getScoreboard(), npc);
|
||||
this.server.invalidateStatus();
|
||||
MutableComponent mutablecomponent;
|
||||
if (npc.getGameProfile().getName().equalsIgnoreCase(s)) { //todo npc
|
||||
if (npc.getGameProfile().getName().equalsIgnoreCase(s)) { //todo npc 语言
|
||||
mutablecomponent = Component.translatable("multiplayer.player.joined", npc.getDisplayName());
|
||||
} else {
|
||||
mutablecomponent = Component.translatable("multiplayer.player.joined.renamed", npc.getDisplayName(), s);
|
||||
|
|
@ -157,7 +156,7 @@ public class NPCPlayerList {
|
|||
if (serverstatus != null) {
|
||||
npc.sendServerStatus(serverstatus);
|
||||
}
|
||||
//todo 实现网络传递
|
||||
// 实现网络传递
|
||||
EDGNetworkHandler.sendToPlayer(NPCInfoUpdatePacket.createNPCPlayerInitializing(this.npcs), npc);
|
||||
this.npcs.add(npc);
|
||||
this.npcsByUUID.put(npc.getUUID(), npc);
|
||||
|
|
@ -207,6 +206,79 @@ public class NPCPlayerList {
|
|||
npc.initInventoryMenu();
|
||||
net.minecraftforge.event.ForgeEventFactory.firePlayerLoggedIn(npc);
|
||||
}
|
||||
|
||||
private @NotNull ServerLevel getServerLevel(ResourceKey<Level> resourcekey) {
|
||||
ServerLevel serverlevel = this.server.getLevel(resourcekey);
|
||||
ServerLevel serverlevel1;
|
||||
if (serverlevel == null) {
|
||||
LOGGER.warn("Unknown respawn dimension {}, defaulting to overworld", resourcekey);
|
||||
serverlevel1 = this.server.overworld();
|
||||
} else {
|
||||
serverlevel1 = serverlevel;
|
||||
}
|
||||
return serverlevel1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载所有已存在的 NPC(从磁盘)
|
||||
*/
|
||||
public void loadAllNPCs() {
|
||||
String[] seenNPCs = this.npcIo.getSeenNPCs();
|
||||
LOGGER.info("Loading {} existing NPCs from disk", seenNPCs.length);
|
||||
|
||||
for (String uuidStr : seenNPCs) {
|
||||
try {
|
||||
UUID uuid = UUID.fromString(uuidStr);
|
||||
if (loadNPC(uuid)) {
|
||||
LOGGER.info("Loaded NPC {} from disk", uuidStr);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Failed to load NPC with UUID: {}", uuidStr, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 加载指定的 NPC
|
||||
* @param uuid NPC 的 UUID
|
||||
* @return 是否加载成功
|
||||
*/
|
||||
public boolean loadNPC(UUID uuid) {
|
||||
// 检查是否已经加载
|
||||
if (this.npcsByUUID.containsKey(uuid)) {
|
||||
LOGGER.debug("NPC {} already loaded", uuid);
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
NPCServerPlayer placeholder = new NPCServerPlayer(server, server.overworld(), new GameProfile(Util.NIL_UUID, "placeholder"));
|
||||
placeholder.setUUID(uuid);
|
||||
NPCServerPlayer npcServerPlayer = this.npcIo.load(uuid, this::createNPCFromData);
|
||||
if (npcServerPlayer != null) {
|
||||
placeNewNPC(new NPCEmptyClientConnection(PacketFlow.SERVERBOUND), npcServerPlayer);
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Error loading NPC with UUID: {}", uuid, e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public @NotNull NPCServerPlayer createNPCFromData(CompoundTag npcData) {
|
||||
AtomicReference<String> npcName = new AtomicReference<>();
|
||||
AtomicReference<GameProfile> gameProfile = new AtomicReference<>();
|
||||
AtomicReference<ResourceKey<Level>> dimension = new AtomicReference<>(Level.OVERWORLD);
|
||||
//noinspection deprecation
|
||||
NBTReader.of(npcData)
|
||||
.string("NpcName", npcName::set)
|
||||
.compound("NpcGameProfile", data -> gameProfile.set(NbtUtils.readGameProfile(data)))
|
||||
.compound("Dimension", data -> dimension.set(DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, data.get("Dimension")))
|
||||
.resultOrPartial(LOGGER::error)
|
||||
.orElse(Level.OVERWORLD)));
|
||||
ResourceKey<Level> levelResourceKey = dimension.get();
|
||||
ServerLevel serverlevel1 = getServerLevel(levelResourceKey);
|
||||
|
||||
return new NPCServerPlayer(server, serverlevel1, gameProfile.get());
|
||||
}
|
||||
|
||||
public void sendPlayerPermissionLevel(@NotNull ServerPlayer player) {
|
||||
GameProfile gameprofile = player.getGameProfile();
|
||||
int i = this.server.getProfilePermissions(gameprofile);
|
||||
|
|
@ -265,16 +337,17 @@ public class NPCPlayerList {
|
|||
* Called during player login. Reads the player information from disk.
|
||||
*/
|
||||
@Nullable
|
||||
public CompoundTag load(@NotNull NPCServerPlayer player) {
|
||||
public CompoundTag load(@NotNull NPCServerPlayer npc) {
|
||||
CompoundTag compoundtag = this.server.getWorldData().getLoadedPlayerTag();
|
||||
CompoundTag compoundtag1;
|
||||
if (this.server.isSingleplayerOwner(player.getGameProfile()) && compoundtag != null) {
|
||||
if (this.server.isSingleplayerOwner(npc.getGameProfile()) && compoundtag != null) {
|
||||
compoundtag1 = compoundtag;
|
||||
player.load(compoundtag);
|
||||
npc.load(compoundtag);
|
||||
LOGGER.debug("loading single npc");
|
||||
// net.minecraftforge.event.ForgeEventFactory.firePlayerLoadingEvent(player, this.npcIo, player.getUUID().toString());
|
||||
// 应该不会有模组干这个吧?
|
||||
net.minecraftforge.event.ForgeEventFactory.firePlayerLoadingEvent(npc, this.npcIo.getNPCDataFolder(), npc.getUUID().toString());
|
||||
} else {
|
||||
compoundtag1 = this.npcIo.load(player);
|
||||
compoundtag1 = this.npcIo.load(npc);
|
||||
}
|
||||
|
||||
return compoundtag1;
|
||||
|
|
@ -298,6 +371,11 @@ public class NPCPlayerList {
|
|||
|
||||
}
|
||||
|
||||
public void delete(NPCServerPlayer npc) {
|
||||
remove(npc);
|
||||
npcIo.deleteNPCData(npc);
|
||||
}
|
||||
|
||||
public void remove(NPCServerPlayer npc) {
|
||||
net.minecraftforge.event.ForgeEventFactory.firePlayerLoggedOut(npc);
|
||||
ServerLevel serverlevel = npc.serverLevel();
|
||||
|
|
@ -324,7 +402,6 @@ public class NPCPlayerList {
|
|||
if (serverplayer == npc) {
|
||||
this.npcsByUUID.remove(uuid);
|
||||
}
|
||||
|
||||
this.broadcastAll(new ClientboundPlayerInfoRemovePacket(List.of(npc.getUUID())));
|
||||
}
|
||||
|
||||
|
|
@ -347,7 +424,7 @@ public class NPCPlayerList {
|
|||
}
|
||||
|
||||
public void tick() {
|
||||
if (++this.sendAllNPCInfoIn > 600) {
|
||||
if (++this.sendAllNPCInfoIn > SEND_PLAYER_INFO_INTERVAL) {
|
||||
EDGNetworkHandler.sendToAllPlayer(new NPCInfoUpdatePacket(EnumSet.of(NPCInfoUpdatePacket.Action.UPDATE_LATENCY), this.npcs));
|
||||
this.sendAllNPCInfoIn = 0;
|
||||
}
|
||||
|
|
@ -434,4 +511,8 @@ public class NPCPlayerList {
|
|||
public MinecraftServer getServer() {
|
||||
return this.server;
|
||||
}
|
||||
|
||||
public @NotNull FakePlayerList toFakePlayerList() {
|
||||
return new FakePlayerList(this.server, this.registries, this.npcs, this.npcsByUUID);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,11 +17,27 @@
|
|||
package top.r3944realms.eroticdungeongame.content.entity.npc;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.multiplayer.PlayerInfo;
|
||||
import net.minecraft.client.player.RemotePlayer;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import top.r3944realms.eroticdungeongame.util.IEDGClientPacketListener;
|
||||
|
||||
public class NPCRemotePlayer extends RemotePlayer implements INPCPlayer {
|
||||
private PlayerInfo npcPlayerInfo;
|
||||
public NPCRemotePlayer(ClientLevel clientLevel, GameProfile gameProfile) {
|
||||
super(clientLevel, gameProfile);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected PlayerInfo getPlayerInfo() {
|
||||
if (this.npcPlayerInfo == null) {
|
||||
if (Minecraft.getInstance().getConnection() instanceof IEDGClientPacketListener iedgClientPacketListener) {
|
||||
this.npcPlayerInfo = iedgClientPacketListener.getNPCPlayerInfo(this.getUUID());
|
||||
}
|
||||
}
|
||||
return this.npcPlayerInfo;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,11 +21,11 @@ import dev.dubhe.curtain.utils.Messenger;
|
|||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.UUIDUtil;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtUtils;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.PacketFlow;
|
||||
import net.minecraft.network.protocol.game.ClientboundRotateHeadPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket;
|
||||
import net.minecraft.network.protocol.game.ServerboundClientCommandPacket;
|
||||
import net.minecraft.network.protocol.game.*;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.TickTask;
|
||||
|
|
@ -46,12 +46,24 @@ import org.jetbrains.annotations.NotNull;
|
|||
import org.jetbrains.annotations.Nullable;
|
||||
import top.r3944realms.eroticdungeongame.EroticDungeon;
|
||||
import top.r3944realms.eroticdungeongame.core.network.NPCEmptyClientConnection;
|
||||
import top.r3944realms.eroticdungeongame.util.IEDGMinecraftServer;
|
||||
import top.r3944realms.lib39.util.nbt.NBTWriter;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class NPCServerPlayer extends ServerPlayer implements INPCPlayer {
|
||||
public static final String PREFIX = "[NPC]";
|
||||
public String npcNameWithoutPrefix;
|
||||
|
||||
public String getNpcNameWithoutPrefix() {
|
||||
return npcNameWithoutPrefix;
|
||||
}
|
||||
|
||||
public void setNpcNameWithoutPrefix(String npcNameWithoutPrefix) {
|
||||
this.npcNameWithoutPrefix = npcNameWithoutPrefix;
|
||||
}
|
||||
|
||||
public Runnable fixStartingPosition = () -> {
|
||||
};
|
||||
public NPCServerPlayer(MinecraftServer server, ServerLevel level, GameProfile gameProfile) {
|
||||
|
|
@ -86,14 +98,14 @@ public class NPCServerPlayer extends ServerPlayer implements INPCPlayer {
|
|||
instance.fixStartingPosition = () -> {
|
||||
instance.moveTo(x, y, z, (float) yaw, (float) pitch);
|
||||
};
|
||||
server.getPlayerList().placeNewPlayer(new NPCEmptyClientConnection(PacketFlow.SERVERBOUND), instance);
|
||||
((IEDGMinecraftServer)server).getNPCPlayerList().placeNewNPC(new NPCEmptyClientConnection(PacketFlow.SERVERBOUND), instance);
|
||||
instance.teleportTo(worldIn, x, y, z, (float) yaw, (float) pitch);
|
||||
instance.setHealth(20.0F);
|
||||
instance.unsetRemoved();
|
||||
instance.setMaxUpStep(0.6F);
|
||||
instance.gameMode.changeGameModeForPlayer(gamemode);
|
||||
server.getPlayerList().broadcastAll(new ClientboundRotateHeadPacket(instance, (byte)((int)(instance.yHeadRot * 256.0F / 360.0F))), dimensionId);
|
||||
server.getPlayerList().broadcastAll(new ClientboundTeleportEntityPacket(instance), dimensionId);
|
||||
((IEDGMinecraftServer)server).getNPCPlayerList().broadcastAll(new ClientboundRotateHeadPacket(instance, (byte)((int)(instance.yHeadRot * 256.0F / 360.0F))), dimensionId);
|
||||
((IEDGMinecraftServer)server).getNPCPlayerList().broadcastAll(new ClientboundTeleportEntityPacket(instance), dimensionId);
|
||||
instance.entityData.set(DATA_PLAYER_MODE_CUSTOMISATION, (byte)127);
|
||||
instance.getAbilities().flying = isflying;
|
||||
return instance;
|
||||
|
|
@ -104,14 +116,15 @@ public class NPCServerPlayer extends ServerPlayer implements INPCPlayer {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
public void onEquipItem(EquipmentSlot p_238393_, ItemStack p_238394_, ItemStack p_238395_) {
|
||||
@Override
|
||||
public void onEquipItem(@NotNull EquipmentSlot equipmentSlot, @NotNull ItemStack oldItem, @NotNull ItemStack newItem) {
|
||||
if (!this.isUsingItem()) {
|
||||
super.onEquipItem(p_238393_, p_238394_, p_238395_);
|
||||
super.onEquipItem(equipmentSlot, oldItem, newItem);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kill() {
|
||||
this.kill(Messenger.s("Killed"));
|
||||
}
|
||||
|
|
@ -119,7 +132,9 @@ public class NPCServerPlayer extends ServerPlayer implements INPCPlayer {
|
|||
public void kill(Component reason) {
|
||||
this.shakeOff();
|
||||
this.server.tell(new TickTask(this.server.getTickCount(), () -> {
|
||||
this.connection.onDisconnect(reason);
|
||||
if (this.getServer() instanceof IEDGMinecraftServer iedgMinecraftServer) {
|
||||
iedgMinecraftServer.getNPCPlayerList().delete(this);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
@ -128,10 +143,7 @@ public class NPCServerPlayer extends ServerPlayer implements INPCPlayer {
|
|||
this.stopRiding();
|
||||
}
|
||||
|
||||
Iterator var1 = this.getIndirectPassengers().iterator();
|
||||
|
||||
while(var1.hasNext()) {
|
||||
Entity passenger = (Entity)var1.next();
|
||||
for (Entity passenger : this.getIndirectPassengers()) {
|
||||
if (passenger instanceof Player) {
|
||||
passenger.stopRiding();
|
||||
}
|
||||
|
|
@ -139,6 +151,7 @@ public class NPCServerPlayer extends ServerPlayer implements INPCPlayer {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void die(@NotNull DamageSource damageSource) {
|
||||
this.shakeOff();
|
||||
super.die(damageSource);
|
||||
|
|
@ -150,6 +163,11 @@ public class NPCServerPlayer extends ServerPlayer implements INPCPlayer {
|
|||
@Override
|
||||
public void addAdditionalSaveData(@NotNull CompoundTag compound) {
|
||||
super.addAdditionalSaveData(compound);
|
||||
String npcName = getName().getString();
|
||||
String npcNameWithoutPrefix = npcName.substring(PREFIX.length());
|
||||
NBTWriter.of(compound)
|
||||
.string("NpcName", npcNameWithoutPrefix)
|
||||
.compound("NpcGameProfile", NbtUtils.writeGameProfile(new CompoundTag(), this.getGameProfile()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -164,7 +182,8 @@ public class NPCServerPlayer extends ServerPlayer implements INPCPlayer {
|
|||
|
||||
@Override
|
||||
public @NotNull Component getName() {
|
||||
return Component.literal("[NPC]").append(super.getName());
|
||||
|
||||
return Component.literal(PREFIX).append(super.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -182,10 +201,11 @@ public class NPCServerPlayer extends ServerPlayer implements INPCPlayer {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity changeDimension(@NotNull ServerLevel level) {
|
||||
super.changeDimension(level);
|
||||
if (this.wonGame) {
|
||||
ServerboundClientCommandPacket packet = new ServerboundClientCommandPacket(net.minecraft.network.protocol.game.ServerboundClientCommandPacket.Action.PERFORM_RESPAWN);
|
||||
ServerboundClientCommandPacket packet = new ServerboundClientCommandPacket(ServerboundClientCommandPacket.Action.PERFORM_RESPAWN);
|
||||
this.connection.handleClientCommand(packet);
|
||||
}
|
||||
|
||||
|
|
@ -196,8 +216,16 @@ public class NPCServerPlayer extends ServerPlayer implements INPCPlayer {
|
|||
return this.connection.player;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkFallDamage(double y, boolean onGround, @NotNull BlockState state, @NotNull BlockPos pos) {
|
||||
this.doCheckFallDamage(this.getX(), y, this.getZ(), onGround);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Packet<ClientGamePacketListener> getAddEntityPacket() {
|
||||
ClientboundAddPlayerPacket clientboundAddPlayerPacket = new ClientboundAddPlayerPacket(this);
|
||||
clientboundAddPlayerPacket.playerId = getUUID();
|
||||
return clientboundAddPlayerPacket;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ public class SuperLeadRopeCompat implements IForgeCompat {
|
|||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
private void onRegisterKeyBind(RegisterKeyMappingsEvent event) {
|
||||
private void onRegisterKeyBind(@NotNull RegisterKeyMappingsEvent event) {
|
||||
event.register(EDGKeyBindings.KEY_BIND);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package top.r3944realms.eroticdungeongame.core.event;
|
|||
import com.google.common.collect.ImmutableList;
|
||||
import net.minecraft.client.RecipeBookCategories;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.npc.VillagerTrades;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
|
|
@ -30,15 +31,18 @@ import net.minecraftforge.event.AttachCapabilitiesEvent;
|
|||
import net.minecraftforge.event.RegisterCommandsEvent;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||
import net.minecraftforge.event.server.ServerStartedEvent;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.DistExecutor;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLConstructModEvent;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.eroticdungeongame.EroticDungeon;
|
||||
import top.r3944realms.eroticdungeongame.content.EDGVillagerTrades;
|
||||
import top.r3944realms.eroticdungeongame.content.command.EDGCommand;
|
||||
import top.r3944realms.eroticdungeongame.content.entity.npc.INPCPlayer;
|
||||
import top.r3944realms.eroticdungeongame.content.recipe.DungeonCraftingBookCategory;
|
||||
import top.r3944realms.eroticdungeongame.content.recipe.DungeonRecipe;
|
||||
import top.r3944realms.eroticdungeongame.content.recipe.EDGRecipeBookTypes;
|
||||
|
|
@ -46,11 +50,14 @@ import top.r3944realms.eroticdungeongame.content.recipe.EDGRecipeTypeCategories;
|
|||
import top.r3944realms.eroticdungeongame.core.capability.DungeonDataSyncManager;
|
||||
import top.r3944realms.eroticdungeongame.core.capability.PlayerDungeonDataProvider;
|
||||
import top.r3944realms.eroticdungeongame.core.compat.*;
|
||||
import top.r3944realms.eroticdungeongame.core.network.EDGNetworkHandler;
|
||||
import top.r3944realms.eroticdungeongame.core.network.toClient.NPCInfoUpdatePacket;
|
||||
import top.r3944realms.eroticdungeongame.core.register.EDGCapabilities;
|
||||
import top.r3944realms.eroticdungeongame.core.register.EDGRecipeTypes;
|
||||
import top.r3944realms.eroticdungeongame.datagen.EDGDataGenEvent;
|
||||
import top.r3944realms.eroticdungeongame.util.IEDGMinecraftServer;
|
||||
import top.r3944realms.lib39.api.event.SyncManagerRegisterEvent;
|
||||
import top.r3944realms.lib39.core.compat.CompatManager;
|
||||
import top.r3944realms.lib39.core.compat.ForgeCompatManager;
|
||||
import top.r3944realms.lib39.core.event.CommonEventHandler;
|
||||
|
||||
import java.util.List;
|
||||
|
|
@ -65,6 +72,13 @@ public class CommonHandler {
|
|||
EDGCapabilities.attachCapability(event);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onServerStarted(@NotNull ServerStartedEvent event) {
|
||||
if (event.getServer() instanceof IEDGMinecraftServer iedgMinecraftServer) {
|
||||
iedgMinecraftServer.getNPCPlayerList().loadAllNPCs();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void syncCapabilities(SyncManagerRegisterEvent event) {
|
||||
dungeonDataSyncManager = new DungeonDataSyncManager();
|
||||
|
|
@ -87,6 +101,11 @@ public class CommonHandler {
|
|||
Player player = event.getEntity();
|
||||
if (player == null || player.level().isClientSide()) return;
|
||||
DungeonDataSyncManager.login(player);
|
||||
if (player.getServer() instanceof IEDGMinecraftServer iedgMinecraftServer && player instanceof ServerPlayer serverPlayer && !(player instanceof INPCPlayer)) {
|
||||
EDGNetworkHandler.sendToPlayer(NPCInfoUpdatePacket.createNPCPlayerInitializing(iedgMinecraftServer.getNPCPlayerList().getPlayers()), serverPlayer);
|
||||
serverPlayer.serverLevel().getChunkSource().removeEntity();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
|
|
@ -108,7 +127,7 @@ public class CommonHandler {
|
|||
*
|
||||
* @return the compat manager
|
||||
*/
|
||||
public static CompatManager getOrCreateCompatManager() {
|
||||
public static ForgeCompatManager getOrCreateCompatManager() {
|
||||
if (compatManager == null) {
|
||||
synchronized (CommonEventHandler.Mod.class) {
|
||||
if (compatManager == null) {
|
||||
|
|
@ -122,7 +141,7 @@ public class CommonHandler {
|
|||
/**
|
||||
* The Compat manager.
|
||||
*/
|
||||
static volatile CompatManager compatManager;
|
||||
static volatile ForgeCompatManager compatManager;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -133,7 +152,7 @@ public class CommonHandler {
|
|||
@SubscribeEvent
|
||||
public static void onConstructMod(FMLConstructModEvent event) {
|
||||
event.enqueueWork(() -> {
|
||||
CompatManager orCreateCompatManager = Mod.getOrCreateCompatManager();
|
||||
ForgeCompatManager orCreateCompatManager = Mod.getOrCreateCompatManager();
|
||||
orCreateCompatManager
|
||||
.registerCompat(CarryOnCompat.ID, CarryOnCompat.INSTANCE);
|
||||
orCreateCompatManager
|
||||
|
|
@ -142,16 +161,16 @@ public class CommonHandler {
|
|||
.registerCompat(EmoteCraftCompat.ID, EmoteCraftCompat.INSTANCE);
|
||||
orCreateCompatManager
|
||||
.registerCompat(TouhouLittleMaidCompat.ID, TouhouLittleMaidCompat.INSTANCE);
|
||||
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
|
||||
orCreateCompatManager
|
||||
.registerCompat(FreecamCompat.ID, FreecamCompat.INSTANCE);
|
||||
orCreateCompatManager
|
||||
.registerCompat(RealCameraCompat.ID, RealCameraCompat.INSTANCE);
|
||||
orCreateCompatManager
|
||||
.registerCompat(FirstPersonModelAndRealCameraCompat.ID, FirstPersonModelAndRealCameraCompat.INSTANCE);
|
||||
}
|
||||
);
|
||||
orCreateCompatManager.initialize();
|
||||
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
|
||||
orCreateCompatManager
|
||||
.registerCompat(FreecamCompat.ID, FreecamCompat.INSTANCE);
|
||||
orCreateCompatManager
|
||||
.registerCompat(RealCameraCompat.ID, RealCameraCompat.INSTANCE);
|
||||
orCreateCompatManager
|
||||
.registerCompat(FirstPersonModelAndRealCameraCompat.ID, FirstPersonModelAndRealCameraCompat.INSTANCE);
|
||||
}
|
||||
);
|
||||
orCreateCompatManager.initialize();
|
||||
});
|
||||
}
|
||||
@SubscribeEvent
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package top.r3944realms.eroticdungeongame.core.network.toClient;
|
||||
|
||||
import com.eliotlash.mclib.math.functions.limit.Min;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.Optionull;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
|
@ -26,7 +25,6 @@ import net.minecraft.network.FriendlyByteBuf;
|
|||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.RemoteChatSession;
|
||||
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
|
|
@ -63,7 +61,7 @@ public final class NPCInfoUpdatePacket {
|
|||
}
|
||||
|
||||
public NPCInfoUpdatePacket(@NotNull FriendlyByteBuf buf) {
|
||||
this.actions = EnumSet.noneOf(Action.class);
|
||||
this.actions = buf.readEnumSet(Action.class);
|
||||
this.entries = buf.readList((read) -> {
|
||||
EntryBuilder builder = new EntryBuilder(read.readUUID());
|
||||
|
||||
|
|
@ -77,7 +75,7 @@ public final class NPCInfoUpdatePacket {
|
|||
public void write(@NotNull FriendlyByteBuf buffer) {
|
||||
buffer.writeEnumSet(this.actions, Action.class);
|
||||
buffer.writeCollection(this.entries, (buf, entry) -> {
|
||||
buf.writeUUID(entry.profileId());
|
||||
buf.writeUUID(entry.uuid());
|
||||
|
||||
for(Action action : this.actions) {
|
||||
action.writer.write(buf, entry);
|
||||
|
|
@ -97,12 +95,12 @@ public final class NPCInfoUpdatePacket {
|
|||
if (clientPacketListener instanceof IEDGClientPacketListener iedgClientPacketListener) {
|
||||
for (Entry entry : packet.newEntries()) {
|
||||
PlayerInfo npcInfo = new PlayerInfo(entry.profile, false);
|
||||
iedgClientPacketListener.getNPCPlayerInfoMap().putIfAbsent(entry.profileId(), npcInfo);
|
||||
iedgClientPacketListener.getNPCPlayerInfoMap().putIfAbsent(entry.uuid(), npcInfo);
|
||||
}
|
||||
for (Entry entry : packet.entries) {
|
||||
PlayerInfo npcInfo = iedgClientPacketListener.getNPCPlayerInfoMap().get(entry.profileId);
|
||||
PlayerInfo npcInfo = iedgClientPacketListener.getNPCPlayerInfoMap().get(entry.uuid);
|
||||
if (npcInfo == null) {
|
||||
EroticDungeon.getLogger().warn("Ignoring npc player info update for unknown npc player {}", entry.profileId());
|
||||
EroticDungeon.getLogger().warn("Ignoring npc player info update for unknown npc player {}", entry.uuid());
|
||||
} else {
|
||||
for(Action action : packet.actions()) {
|
||||
applyPlayerInfoUpdate(action, entry, npcInfo);
|
||||
|
|
@ -121,13 +119,13 @@ public final class NPCInfoUpdatePacket {
|
|||
switch (action) {
|
||||
case INITIALIZE_CHAT:
|
||||
ClientboundPlayerInfoUpdatePacket.Entry copyEntry = new ClientboundPlayerInfoUpdatePacket.Entry(
|
||||
entry.profileId, entry.profile, entry.listed, entry.latency, entry.gameMode, entry.displayName, entry.chatSession
|
||||
entry.uuid, entry.profile, entry.listed, entry.latency, entry.gameMode, entry.displayName, entry.chatSession
|
||||
);
|
||||
clientPacketListener.initializeChatSession(copyEntry, playerInfo);
|
||||
break;
|
||||
case UPDATE_GAME_MODE:
|
||||
|
||||
if (playerInfo.getGameMode() != entry.gameMode() && minecraft.player != null && minecraft.player.getUUID().equals(entry.profileId())) {
|
||||
if (playerInfo.getGameMode() != entry.gameMode() && minecraft.player != null && minecraft.player.getUUID().equals(entry.uuid())) {
|
||||
minecraft.player.onGameModeChanged(entry.gameMode());
|
||||
}
|
||||
|
||||
|
|
@ -204,7 +202,7 @@ public final class NPCInfoUpdatePacket {
|
|||
|
||||
}
|
||||
|
||||
public record Entry(UUID profileId, GameProfile profile, boolean listed, int latency, GameType gameMode,
|
||||
public record Entry(UUID uuid, GameProfile profile, boolean listed, int latency, GameType gameMode,
|
||||
@Nullable Component displayName, @Nullable RemoteChatSession.Data chatSession) {
|
||||
Entry(@NotNull NPCServerPlayer npc) {
|
||||
this(npc.getUUID(), npc.getGameProfile(), true, npc.latency, npc.gameMode.getGameModeForPlayer(), npc.getTabListDisplayName(), Optionull.map(npc.getChatSession(), RemoteChatSession::asData));
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ import top.r3944realms.eroticdungeongame.content.entity.npc.NPCServerPlayer;
|
|||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class NPCDataStorage {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
|
@ -41,6 +44,65 @@ public class NPCDataStorage {
|
|||
this.npcDir = levelStorageAccess.getLevelPath(NPC_DATA_DIR).toFile();
|
||||
this.npcDir.mkdirs();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 删除指定 NPC 的数据文件
|
||||
* @param npcUUID 要删除的 NPC 的 UUID
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
public boolean deleteNPCData(UUID npcUUID) {
|
||||
if (npcUUID == null) {
|
||||
LOGGER.warn("Cannot delete NPC data: UUID is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
File dataFile = new File(this.npcDir, npcUUID + ".dat");
|
||||
File oldFile = new File(this.npcDir, npcUUID + ".dat_old");
|
||||
|
||||
boolean deleted = false;
|
||||
|
||||
// 删除主数据文件
|
||||
if (dataFile.exists()) {
|
||||
deleted = dataFile.delete();
|
||||
if (deleted) {
|
||||
LOGGER.info("Deleted NPC data file: {}", dataFile.getName());
|
||||
} else {
|
||||
LOGGER.warn("Failed to delete NPC data file: {}", dataFile.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// 删除备份文件
|
||||
if (oldFile.exists()) {
|
||||
boolean oldDeleted = oldFile.delete();
|
||||
if (oldDeleted) {
|
||||
LOGGER.debug("Deleted NPC backup file: {}", oldFile.getName());
|
||||
}
|
||||
}
|
||||
|
||||
return deleted;
|
||||
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Failed to delete NPC data for UUID: {}", npcUUID, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定 NPC 的数据文件(通过 NPCServerPlayer 对象)
|
||||
* @param npc 要删除的 NPC
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
public boolean deleteNPCData(NPCServerPlayer npc) {
|
||||
if (npc == null) {
|
||||
LOGGER.warn("Cannot delete NPC data: NPC is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
return deleteNPCData(npc.getUUID());
|
||||
}
|
||||
|
||||
public void save(NPCServerPlayer player) {
|
||||
try {
|
||||
CompoundTag compoundtag = player.saveWithoutId(new CompoundTag());
|
||||
|
|
@ -55,7 +117,27 @@ public class NPCDataStorage {
|
|||
}
|
||||
|
||||
}
|
||||
@Nullable
|
||||
public NPCServerPlayer load(UUID uuid, Function<CompoundTag, NPCServerPlayer> loader) {
|
||||
CompoundTag compoundtag = null;
|
||||
|
||||
try {
|
||||
File file1 = new File(this.npcDir, uuid + ".dat");
|
||||
if (file1.exists() && file1.isFile()) {
|
||||
compoundtag = NbtIo.readCompressed(file1);
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
LOGGER.warn("Failed to load player data for {}", uuid, exception);
|
||||
}
|
||||
NPCServerPlayer npcServerPlayer = loader.apply(compoundtag);
|
||||
if (compoundtag != null) {
|
||||
int i = NbtUtils.getDataVersion(compoundtag, -1);
|
||||
npcServerPlayer.load(DataFixTypes.PLAYER.updateToCurrentVersion(this.fixerUpper, compoundtag, i));
|
||||
}
|
||||
|
||||
net.minecraftforge.event.ForgeEventFactory.firePlayerLoadingEvent(npcServerPlayer, npcDir, uuid.toString());
|
||||
return npcServerPlayer;
|
||||
}
|
||||
@Nullable
|
||||
public CompoundTag load(NPCServerPlayer npcServerPlayer) {
|
||||
CompoundTag compoundtag = null;
|
||||
|
|
@ -73,23 +155,24 @@ public class NPCDataStorage {
|
|||
int i = NbtUtils.getDataVersion(compoundtag, -1);
|
||||
npcServerPlayer.load(DataFixTypes.PLAYER.updateToCurrentVersion(this.fixerUpper, compoundtag, i));
|
||||
}
|
||||
// net.minecraftforge.event.ForgeEventFactory.firePlayerLoadingEvent(npcServerPlayer, npcDir, npcServerPlayer.getStringUUID());
|
||||
net.minecraftforge.event.ForgeEventFactory.firePlayerLoadingEvent(npcServerPlayer, npcDir, npcServerPlayer.getStringUUID());
|
||||
|
||||
return compoundtag;
|
||||
}
|
||||
|
||||
public String[] getSeenNPCs() {
|
||||
String[] astring = this.npcDir.list();
|
||||
Set<String> ret = new HashSet<>();
|
||||
if (astring == null) {
|
||||
astring = new String[0];
|
||||
}
|
||||
|
||||
for(int i = 0; i < astring.length; ++i) {
|
||||
if (astring[i].endsWith(".dat")) {
|
||||
astring[i] = astring[i].substring(0, astring[i].length() - 4);
|
||||
} else {
|
||||
for (String s : astring) {
|
||||
if (s.endsWith(".dat") && !s.endsWith(".dat_old")) {
|
||||
ret.add(s.substring(0, s.length() - 4));
|
||||
}
|
||||
}
|
||||
astring = ret.toArray(new String[0]);
|
||||
}
|
||||
|
||||
return astring;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,8 +19,10 @@ package top.r3944realms.eroticdungeongame.mixin.minecraft;
|
|||
import com.google.common.collect.Maps;
|
||||
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.multiplayer.ClientPacketListener;
|
||||
import net.minecraft.client.multiplayer.PlayerInfo;
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
|
@ -28,6 +30,7 @@ import net.minecraft.network.protocol.PacketUtils;
|
|||
import net.minecraft.network.protocol.game.ClientboundAddPlayerPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetPassengersPacket;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
|
@ -39,6 +42,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
import top.r3944realms.eroticdungeongame.client.EDGKeyBindings;
|
||||
import top.r3944realms.eroticdungeongame.content.entity.SeatEntity;
|
||||
import top.r3944realms.eroticdungeongame.content.entity.npc.NPCRemotePlayer;
|
||||
import top.r3944realms.eroticdungeongame.util.IEDGClientPacketListener;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
|
@ -61,6 +65,7 @@ public abstract class MixinClientPacketListener implements IEDGClientPacketListe
|
|||
@Shadow @Nullable public abstract PlayerInfo getPlayerInfo(UUID uniqueId);
|
||||
|
||||
@Shadow @Final private Set<PlayerInfo> listedPlayers;
|
||||
@Shadow private ClientLevel level;
|
||||
@Unique
|
||||
private Entity edg$cachedMount = null;
|
||||
|
||||
|
|
@ -84,15 +89,51 @@ public abstract class MixinClientPacketListener implements IEDGClientPacketListe
|
|||
PacketUtils.ensureRunningOnSameThread(packet, ClientPacketListener.class.cast(this), this.minecraft);
|
||||
PlayerInfo playerinfo = this.getPlayerInfo(packet.getPlayerId());
|
||||
if (playerinfo == null) {
|
||||
|
||||
PlayerInfo npcPlayerInfo = getNPCPlayerInfo(packet.getPlayerId());
|
||||
if (npcPlayerInfo == null) {
|
||||
original.call(packet);
|
||||
} else {
|
||||
double d0 = packet.getX();
|
||||
double d1 = packet.getY();
|
||||
double d2 = packet.getZ();
|
||||
float f = (float)(packet.getyRot() * 360) / 256.0F;
|
||||
float f1 = (float)(packet.getxRot() * 360) / 256.0F;
|
||||
int i = packet.getEntityId();
|
||||
assert this.minecraft.level != null;
|
||||
NPCRemotePlayer npcRemotePlayer = new NPCRemotePlayer(this.minecraft.level, npcPlayerInfo.getProfile());
|
||||
npcRemotePlayer.setUUID(packet.getPlayerId());
|
||||
npcRemotePlayer.setId(i);
|
||||
npcRemotePlayer.syncPacketPositionCodec(d0, d1, d2);
|
||||
npcRemotePlayer.absMoveTo(d0, d1, d2, f, f1);
|
||||
npcRemotePlayer.setOldPosAndRot();
|
||||
this.level.addPlayer(i, npcRemotePlayer);
|
||||
}
|
||||
} else original.call(packet);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "SuspiciousMethodCalls"})
|
||||
@WrapOperation(
|
||||
method = "handlePlayerInfoUpdate",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Ljava/util/Map;get(Ljava/lang/Object;)Ljava/lang/Object;"
|
||||
)
|
||||
)
|
||||
private<V> V edg$handlePlayerInfoUpdate(Map<?,?> instance, Object object, @NotNull Operation<V> original) {
|
||||
V call = original.call(instance, object);
|
||||
if (call == null) {
|
||||
call = (V) getNPCPlayerInfoMap().get(object);
|
||||
}
|
||||
return call;
|
||||
}
|
||||
|
||||
@SuppressWarnings("AddedMixinMembersNamePattern")
|
||||
@Override
|
||||
public Map<UUID, PlayerInfo> getNPCPlayerInfoMap() {
|
||||
return edg$npcPlayerInfoMap;
|
||||
}
|
||||
|
||||
@SuppressWarnings("AddedMixinMembersNamePattern")
|
||||
@Override
|
||||
public Set<PlayerInfo> getListedNPCPlayers() {
|
||||
return listedPlayers;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2025-2026 R3944Realms
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.r3944realms.eroticdungeongame.mixin.minecraft;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import net.minecraft.server.players.PlayerList;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import top.r3944realms.eroticdungeongame.content.entity.npc.NPCPlayerList;
|
||||
import top.r3944realms.eroticdungeongame.util.IEDGMinecraftServer;
|
||||
|
||||
@Mixin(DedicatedServer.class)
|
||||
public abstract class MixinDedicatedServer implements IEDGMinecraftServer {
|
||||
@WrapOperation(
|
||||
method = "initServer",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/server/dedicated/DedicatedServer;setPlayerList(Lnet/minecraft/server/players/PlayerList;)V"
|
||||
)
|
||||
)
|
||||
void edg$initServer$setNPCList(DedicatedServer instance, PlayerList playerList, Operation<Void> original) {
|
||||
original.call(instance, playerList);
|
||||
this.setNPCPlayerList(new NPCPlayerList(instance, instance.registries(), this.getNPCDataStorage()));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2025-2026 R3944Realms
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.r3944realms.eroticdungeongame.mixin.minecraft;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.arguments.selector.EntitySelector;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import top.r3944realms.eroticdungeongame.content.entity.npc.NPCPlayerList;
|
||||
import top.r3944realms.eroticdungeongame.content.entity.npc.NPCServerPlayer;
|
||||
import top.r3944realms.eroticdungeongame.util.IEDGMinecraftServer;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@Mixin(EntitySelector.class)
|
||||
public class MixinEntitySelector {
|
||||
@Shadow @Final private Predicate<Entity> predicate;
|
||||
|
||||
@Shadow @Final @Nullable private UUID entityUUID;
|
||||
|
||||
@WrapMethod(
|
||||
method = "findEntitiesRaw"
|
||||
)
|
||||
private @NotNull List<? extends Entity> edg$findEntitiesRaw$findNPCs(CommandSourceStack source, @NotNull Operation<List<? extends Entity>> original) {
|
||||
List<Entity> ret = new ArrayList<>(original.call(source));
|
||||
if (source.getServer() instanceof IEDGMinecraftServer iedgMinecraftServer) {
|
||||
NPCPlayerList npcPlayerList = iedgMinecraftServer.getNPCPlayerList();
|
||||
if (npcPlayerList != null) {
|
||||
for (NPCServerPlayer npc : npcPlayerList.getPlayers()) {
|
||||
if (Objects.equals(entityUUID, npc.getUUID()) && predicate.test(npc)) {
|
||||
ret.add(npc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2025-2026 R3944Realms
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.r3944realms.eroticdungeongame.mixin.minecraft;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import net.minecraft.gametest.framework.GameTestServer;
|
||||
import net.minecraft.server.players.PlayerList;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import top.r3944realms.eroticdungeongame.content.entity.npc.NPCPlayerList;
|
||||
import top.r3944realms.eroticdungeongame.util.IEDGMinecraftServer;
|
||||
|
||||
@Mixin(GameTestServer.class)
|
||||
public abstract class MixinGameTestServer implements IEDGMinecraftServer {
|
||||
@WrapOperation(
|
||||
method = "initServer",
|
||||
at = @At(
|
||||
value = "HEAD",
|
||||
target = "Lnet/minecraft/gametest/framework/GameTestServer;setPlayerList(Lnet/minecraft/server/players/PlayerList;)V"
|
||||
)
|
||||
)
|
||||
void edg$initServer$setNPCList(GameTestServer instance, PlayerList playerList, Operation<Void> original) {
|
||||
original.call(instance, playerList);
|
||||
this.setNPCPlayerList(new NPCPlayerList(instance, instance.registries(), this.getNPCDataStorage()));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2025-2026 R3944Realms
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.r3944realms.eroticdungeongame.mixin.minecraft;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import net.minecraft.client.server.IntegratedServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.players.PlayerList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import top.r3944realms.eroticdungeongame.content.entity.npc.NPCPlayerList;
|
||||
import top.r3944realms.eroticdungeongame.content.entity.npc.NPCServerPlayer;
|
||||
import top.r3944realms.eroticdungeongame.util.IEDGMinecraftServer;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.UUID;
|
||||
|
||||
@Mixin(IntegratedServer.class)
|
||||
public abstract class MixinIntegratedServer implements IEDGMinecraftServer {
|
||||
@Shadow @Nullable private UUID uuid;
|
||||
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At("TAIL")
|
||||
)
|
||||
private void edg$init(CallbackInfo ci) {
|
||||
IntegratedServer server = IntegratedServer.class.cast(this);
|
||||
setNPCPlayerList(new NPCPlayerList(server, server.registries(), getNPCDataStorage()));
|
||||
}
|
||||
@WrapOperation(
|
||||
method = "halt",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/server/IntegratedServer;executeBlocking(Ljava/lang/Runnable;)V"
|
||||
)
|
||||
)
|
||||
private void edg$halt$3$removeNpc(IntegratedServer instance, Runnable runnable, @NotNull Operation<Void> original) {
|
||||
original.call(instance, runnable);
|
||||
original.call(instance, (Runnable) () -> {
|
||||
for(NPCServerPlayer npcServerPlayer : Lists.newArrayList(getNPCPlayerList().getPlayers())) {
|
||||
if (!npcServerPlayer.getUUID().equals(this.uuid)) {
|
||||
getNPCPlayerList().remove(npcServerPlayer);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2025-2026 R3944Realms
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.r3944realms.eroticdungeongame.mixin.minecraft;
|
||||
|
||||
import net.minecraft.util.datafix.DataFixers;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import top.r3944realms.eroticdungeongame.core.storage.NPCDataStorage;
|
||||
import top.r3944realms.eroticdungeongame.util.IEDGLevelStorageAccess;
|
||||
|
||||
@Mixin(LevelStorageSource.LevelStorageAccess.class)
|
||||
public abstract class MixinLevelStorageSource$LevelStorageAccess implements IEDGLevelStorageAccess {
|
||||
|
||||
@Shadow protected abstract void checkLock();
|
||||
|
||||
@SuppressWarnings("AddedMixinMembersNamePattern")
|
||||
@Override
|
||||
public NPCDataStorage createNPCPlayerDataStorage() {
|
||||
this.checkLock();
|
||||
return new NPCDataStorage(LevelStorageSource.LevelStorageAccess.class.cast(this), DataFixers.getDataFixer());
|
||||
}
|
||||
}
|
||||
|
|
@ -18,24 +18,43 @@ package top.r3944realms.eroticdungeongame.mixin.minecraft;
|
|||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.Services;
|
||||
import net.minecraft.server.WorldStem;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListenerFactory;
|
||||
import net.minecraft.server.packs.repository.PackRepository;
|
||||
import net.minecraft.server.players.PlayerList;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.WorldData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import top.r3944realms.eroticdungeongame.content.entity.npc.NPCPlayerList;
|
||||
import top.r3944realms.eroticdungeongame.core.storage.NPCDataStorage;
|
||||
import top.r3944realms.eroticdungeongame.util.IEDGLevelStorageAccess;
|
||||
import top.r3944realms.eroticdungeongame.util.IEDGMinecraftServer;
|
||||
|
||||
import java.net.Proxy;
|
||||
|
||||
@Mixin(MinecraftServer.class)
|
||||
public class MixinMinecraftServer implements IEDGMinecraftServer {
|
||||
|
||||
@Unique
|
||||
private NPCDataStorage edg$npcDataStorage;
|
||||
@Unique
|
||||
private NPCPlayerList edg$npcPlayerList;
|
||||
@Inject(
|
||||
method = "<init>",
|
||||
at = @At("TAIL")
|
||||
)
|
||||
private void edg$init(Thread serverThread, LevelStorageSource.LevelStorageAccess storageSource, PackRepository packRepository, WorldStem worldStem, Proxy proxy, DataFixer fixerUpper, Services services, ChunkProgressListenerFactory progressListenerFactory, CallbackInfo ci) {
|
||||
if (storageSource instanceof IEDGLevelStorageAccess iedgLevelStorageAccess) {
|
||||
edg$npcDataStorage = iedgLevelStorageAccess.createNPCPlayerDataStorage();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("AddedMixinMembersNamePattern")
|
||||
@Override
|
||||
|
|
@ -48,6 +67,13 @@ public class MixinMinecraftServer implements IEDGMinecraftServer {
|
|||
public void setNPCPlayerList(NPCPlayerList playerList) {
|
||||
this.edg$npcPlayerList = playerList;
|
||||
}
|
||||
|
||||
@SuppressWarnings("AddedMixinMembersNamePattern")
|
||||
@Override
|
||||
public NPCDataStorage getNPCDataStorage() {
|
||||
return edg$npcDataStorage;
|
||||
}
|
||||
|
||||
@WrapOperation(
|
||||
method = "saveEverything",
|
||||
at = @At(
|
||||
|
|
@ -84,5 +110,17 @@ public class MixinMinecraftServer implements IEDGMinecraftServer {
|
|||
edg$npcPlayerList.saveAll();
|
||||
edg$npcPlayerList.removeAll();
|
||||
}
|
||||
@WrapOperation(
|
||||
method = "tickChildren",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/server/players/PlayerList;tick()V"
|
||||
)
|
||||
)
|
||||
void edg$tickChildren$tick(PlayerList instance, @NotNull Operation<Void> original) {
|
||||
original.call(instance);
|
||||
edg$npcPlayerList.tick();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2025-2026 R3944Realms
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.r3944realms.eroticdungeongame.util;
|
||||
|
||||
public interface IEDGEntitySelector {
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright 2025-2026 R3944Realms
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.r3944realms.eroticdungeongame.util;
|
||||
|
||||
import top.r3944realms.eroticdungeongame.core.storage.NPCDataStorage;
|
||||
|
||||
public interface IEDGLevelStorageAccess {
|
||||
NPCDataStorage createNPCPlayerDataStorage();
|
||||
}
|
||||
|
|
@ -17,8 +17,10 @@
|
|||
package top.r3944realms.eroticdungeongame.util;
|
||||
|
||||
import top.r3944realms.eroticdungeongame.content.entity.npc.NPCPlayerList;
|
||||
import top.r3944realms.eroticdungeongame.core.storage.NPCDataStorage;
|
||||
|
||||
public interface IEDGMinecraftServer {
|
||||
NPCPlayerList getNPCPlayerList();
|
||||
void setNPCPlayerList(NPCPlayerList playerList);
|
||||
NPCDataStorage getNPCDataStorage();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,11 +8,14 @@
|
|||
"mixins": [
|
||||
"bendylib.MixinBendableCuboidBuilder",
|
||||
"minecraft.MixinClientConnect",
|
||||
"minecraft.MixinDedicatedServer",
|
||||
"minecraft.MixinEntity",
|
||||
"minecraft.MixinEntitySelector",
|
||||
"minecraft.MixinGameTestServer",
|
||||
"minecraft.MixinLevelStorageSource$LevelStorageAccess",
|
||||
"minecraft.MixinLivingEntity",
|
||||
"minecraft.MixinMinecraftServer",
|
||||
"minecraft.MixinPlayer",
|
||||
"minecraft.MixinServerLoginPacketListenerImpl",
|
||||
"minecraft.MixinServerPlayer"
|
||||
],
|
||||
"client": [
|
||||
|
|
@ -20,6 +23,7 @@
|
|||
"minecraft.MixinClientPacketListener",
|
||||
"minecraft.MixinEntityRenderer",
|
||||
"minecraft.MixinGameRender",
|
||||
"minecraft.MixinIntegratedServer",
|
||||
"minecraft.MixinItemInHandRenderer",
|
||||
"minecraft.MixinMinecraft",
|
||||
"minecraft.MixinPlayerRenderer"
|
||||
|
|
|
|||
|
|
@ -45,4 +45,5 @@ public net.minecraft.client.multiplayer.ClientPacketListener m_245842_(Lnet/mine
|
|||
public net.minecraft.client.multiplayer.ClientPacketListener f_244156_ # listedPlayers
|
||||
public net.minecraft.client.multiplayer.PlayerInfo m_105317_(Lnet/minecraft/world/level/GameType;)V # setGameMode
|
||||
public net.minecraft.client.multiplayer.PlayerInfo m_105313_(I)V # setLatency
|
||||
public net.minecraft.client.multiplayer.ClientPacketListener f_104892_ # playerInfoMap
|
||||
public net.minecraft.client.multiplayer.ClientPacketListener f_104892_ # playerInfoMap
|
||||
public-f net.minecraft.network.protocol.game.ClientboundAddPlayerPacket f_131588_ # playerId
|
||||
Loading…
Reference in New Issue
Block a user