Files
CustomNPC-Plus/src/main/java/kamkeel/npcs/controllers/SyncController.java
T

997 lines
40 KiB
Java
Raw Normal View History

2025-01-29 18:25:34 -05:00
package kamkeel.npcs.controllers;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import kamkeel.npcs.addon.DBCAddon;
2025-10-19 22:06:11 -04:00
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import kamkeel.npcs.network.LargeAbstractPacket;
2025-01-29 18:25:34 -05:00
import kamkeel.npcs.network.PacketHandler;
2025-02-20 21:40:39 -05:00
import kamkeel.npcs.network.enums.EnumSyncAction;
import kamkeel.npcs.network.enums.EnumSyncType;
2025-02-18 22:22:40 -05:00
import kamkeel.npcs.network.packets.data.LoginPacket;
2025-02-10 21:41:54 -05:00
import kamkeel.npcs.network.packets.data.large.SyncEffectPacket;
import kamkeel.npcs.network.packets.data.large.SyncPacket;
2025-06-24 05:41:34 -04:00
import kamkeel.npcs.network.packets.request.party.PartyInfoPacket;
2025-10-19 22:06:11 -04:00
import kamkeel.npcs.util.ByteBufUtils;
2025-02-10 21:41:54 -05:00
import net.minecraft.client.Minecraft;
2025-01-29 18:25:34 -05:00
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
2025-10-19 22:06:11 -04:00
import net.minecraft.server.MinecraftServer;
2025-01-29 18:25:34 -05:00
import noppes.npcs.client.ClientCacheHandler;
2025-07-23 14:22:52 -04:00
import noppes.npcs.controllers.CustomEffectController;
import noppes.npcs.controllers.DialogController;
import noppes.npcs.controllers.FactionController;
import noppes.npcs.controllers.MagicController;
import noppes.npcs.controllers.QuestController;
import noppes.npcs.controllers.RecipeController;
import noppes.npcs.controllers.data.CustomEffect;
import noppes.npcs.controllers.data.Dialog;
import noppes.npcs.controllers.data.DialogCategory;
import noppes.npcs.controllers.data.EffectKey;
import noppes.npcs.controllers.data.Faction;
import noppes.npcs.controllers.data.Magic;
import noppes.npcs.controllers.data.MagicCycle;
import noppes.npcs.controllers.data.PlayerData;
import noppes.npcs.controllers.data.PlayerEffect;
import noppes.npcs.controllers.data.Quest;
import noppes.npcs.controllers.data.QuestCategory;
import noppes.npcs.controllers.data.RecipeAnvil;
import noppes.npcs.controllers.data.RecipeCarpentry;
2025-01-29 18:25:34 -05:00
2025-10-19 22:06:11 -04:00
import java.io.IOException;
import java.util.EnumMap;
import java.util.EnumSet;
2025-01-29 18:25:34 -05:00
import java.util.HashMap;
2025-10-19 22:06:11 -04:00
import java.util.Map;
import java.util.UUID;
2025-02-10 21:41:54 -05:00
import java.util.concurrent.ConcurrentHashMap;
2025-10-19 22:06:11 -04:00
import java.util.function.Supplier;
2025-01-29 18:25:34 -05:00
2025-01-30 09:26:04 -05:00
public class SyncController {
2025-01-29 18:25:34 -05:00
2025-10-19 22:06:11 -04:00
public static boolean DEBUG_SYNC_LOGGING = false;
2025-11-05 17:33:24 -05:00
private static final EnumMap<EnumSyncType, SyncCacheEntry> cacheEntries = new EnumMap<>(EnumSyncType.class);
private static final ConcurrentHashMap<UUID, PlayerSyncState> playerSyncState = new ConcurrentHashMap<>();
2025-11-05 17:23:21 -05:00
2025-10-19 22:06:11 -04:00
private static void debug(String message, Object... args) {
if (DEBUG_SYNC_LOGGING) {
System.out.println("[SyncController] " + String.format(message, args));
}
}
private static final EnumSyncType[] LOGIN_SYNC_TYPES = new EnumSyncType[]{
EnumSyncType.FACTION,
EnumSyncType.DIALOG_CATEGORY,
EnumSyncType.QUEST_CATEGORY,
EnumSyncType.WORKBENCH_RECIPES,
EnumSyncType.CARPENTRY_RECIPES,
EnumSyncType.ANVIL_RECIPES,
EnumSyncType.CUSTOM_EFFECTS,
EnumSyncType.MAGIC,
EnumSyncType.MAGIC_CYCLE
};
2025-11-05 17:23:21 -05:00
public static void load() {
2025-11-05 17:33:24 -05:00
cacheEntries.clear();
2025-11-05 17:23:21 -05:00
playerSyncState.clear();
2025-11-05 17:33:24 -05:00
registerCache(EnumSyncType.FACTION, SyncController::factionsNBT);
registerCache(EnumSyncType.DIALOG_CATEGORY, SyncController::dialogCategoriesNBT);
registerCache(EnumSyncType.QUEST_CATEGORY, SyncController::questCategoriesNBT);
registerCache(EnumSyncType.WORKBENCH_RECIPES, SyncController::workbenchNBT);
registerCache(EnumSyncType.CARPENTRY_RECIPES, SyncController::carpentryNBT);
registerCache(EnumSyncType.ANVIL_RECIPES, SyncController::anvilNBT);
registerCache(EnumSyncType.CUSTOM_EFFECTS, SyncController::customEffectsNBT);
registerCache(EnumSyncType.MAGIC, SyncController::magicsNBT);
registerCache(EnumSyncType.MAGIC_CYCLE, SyncController::magicCyclesNBT);
2025-11-05 16:52:38 -05:00
}
2025-03-13 17:14:45 -04:00
public static void syncPlayer(EntityPlayerMP player) {
2025-11-05 17:33:24 -05:00
SyncController.syncPlayerInternal(player, true);
2025-10-19 22:06:11 -04:00
}
2025-02-18 22:22:40 -05:00
2025-11-05 17:33:24 -05:00
private static void syncPlayerInternal(EntityPlayerMP player, boolean includePostPackets) {
2025-11-05 17:23:21 -05:00
PlayerSyncState state = playerSyncState.computeIfAbsent(player.getUniqueID(), PlayerSyncState::new);
2025-10-19 22:06:11 -04:00
for (EnumSyncType type : LOGIN_SYNC_TYPES) {
2025-11-05 17:23:21 -05:00
SyncCacheEntry entry = cacheEntries.get(type);
2025-10-19 22:06:11 -04:00
if (entry == null) {
continue;
}
2025-03-04 18:19:53 -05:00
2025-10-19 22:06:11 -04:00
int currentRevision = entry.getRevisionValue();
int lastRevision = state.getRevision(type);
if (lastRevision == currentRevision) {
continue;
}
CachedSyncPayload payload = entry.getPayload(type);
if (payload == null) {
continue;
}
if (lastRevision != payload.getRevision()) {
debug("Sending %s data to %s", type, player.getCommandSenderName());
2025-10-19 22:06:11 -04:00
PacketHandler.Instance.sendToPlayer(new SyncPacket(type, payload), player);
state.updateRevision(type, payload.getRevision());
}
}
if (includePostPackets) {
sendPostLoginPackets(player);
}
}
public static void beginLogin(EntityPlayerMP player) {
2025-11-05 17:33:24 -05:00
playerSyncState.computeIfAbsent(player.getUniqueID(), PlayerSyncState::new);
2025-10-19 22:06:11 -04:00
PacketHandler.Instance.sendToPlayer(
new LoginPacket(getServerCacheKey(), getServerRevisionSnapshot()),
player
);
}
public static void handleClientRevisionReport(
EntityPlayerMP player,
String serverKey,
String previousServerKey,
2025-10-19 22:06:11 -04:00
Map<EnumSyncType, Integer> clientRevisions
) {
String currentServerKey = getServerCacheKey();
2025-11-05 17:33:24 -05:00
PlayerSyncState state = playerSyncState.computeIfAbsent(player.getUniqueID(), PlayerSyncState::new);
if (!currentServerKey.equals(serverKey)) {
state.reset();
2025-10-19 22:06:11 -04:00
syncPlayer(player);
return;
}
if (!currentServerKey.isEmpty()) {
if (previousServerKey == null || !currentServerKey.equals(previousServerKey)) {
state.reset();
syncPlayer(player);
return;
}
}
if (clientRevisions == null || clientRevisions.isEmpty()) {
debug(
"Client %s reported no cached revisions; forcing full sync",
player.getCommandSenderName()
);
state.reset();
} else {
state.applyHandshake(clientRevisions);
}
2025-10-19 22:06:11 -04:00
syncPlayer(player);
}
private static void sendPostLoginPackets(EntityPlayerMP player) {
2025-01-29 18:25:34 -05:00
DBCAddon.instance.syncPlayer(player);
syncPlayerData(player, false);
2025-06-24 05:41:34 -04:00
PartyInfoPacket.sendPartyData(player);
2025-01-29 18:25:34 -05:00
}
2025-10-19 22:06:11 -04:00
private static EnumMap<EnumSyncType, Integer> getServerRevisionSnapshot() {
EnumMap<EnumSyncType, Integer> snapshot = new EnumMap<>(EnumSyncType.class);
for (EnumSyncType type : LOGIN_SYNC_TYPES) {
2025-11-05 17:33:24 -05:00
SyncCacheEntry entry = cacheEntries.get(type);
2025-10-19 22:06:11 -04:00
if (entry != null) {
snapshot.put(type, entry.getRevisionValue());
}
}
return snapshot;
}
private static final String SERVER_IDENTITY_KEY = UUID.randomUUID().toString();
2025-10-19 22:06:11 -04:00
private static String getServerCacheKey() {
MinecraftServer server = MinecraftServer.getServer();
if (server == null) {
return "";
2025-10-19 22:06:11 -04:00
}
if (!server.isDedicatedServer()) {
return "";
}
return SERVER_IDENTITY_KEY;
2025-10-19 22:06:11 -04:00
}
public static int getCurrentRevision(EnumSyncType type) {
2025-11-05 17:33:24 -05:00
SyncCacheEntry entry = cacheEntries.get(type);
2025-10-19 22:06:11 -04:00
return entry == null ? -1 : entry.getRevisionValue();
}
2025-03-13 17:14:45 -04:00
public static NBTTagCompound workbenchNBT() {
2025-01-30 09:22:43 -05:00
RecipeController controller = RecipeController.Instance;
NBTTagList list = new NBTTagList();
NBTTagCompound compound = new NBTTagCompound();
2025-03-13 17:14:45 -04:00
for (RecipeCarpentry recipe : controller.globalRecipes.values()) {
2025-07-09 15:40:31 -04:00
list.appendTag(recipe.writeNBT(false));
2025-01-30 09:22:43 -05:00
}
compound.setTag("recipes", list);
return compound;
}
2025-03-13 17:14:45 -04:00
public static NBTTagCompound carpentryNBT() {
2025-01-30 09:22:43 -05:00
RecipeController controller = RecipeController.Instance;
NBTTagList list = new NBTTagList();
NBTTagCompound compound = new NBTTagCompound();
2025-03-13 17:14:45 -04:00
for (RecipeCarpentry recipe : controller.carpentryRecipes.values()) {
2025-07-09 15:40:31 -04:00
list.appendTag(recipe.writeNBT(false));
2025-01-30 09:22:43 -05:00
}
compound.setTag("recipes", list);
return compound;
}
2025-03-13 17:14:45 -04:00
public static NBTTagCompound anvilNBT() {
2025-02-10 02:44:28 -05:00
RecipeController controller = RecipeController.Instance;
NBTTagList list = new NBTTagList();
NBTTagCompound compound = new NBTTagCompound();
2025-03-13 17:14:45 -04:00
for (RecipeAnvil recipe : controller.anvilRecipes.values()) {
2025-07-09 15:40:31 -04:00
list.appendTag(recipe.writeNBT(false));
2025-02-10 02:44:28 -05:00
}
compound.setTag("recipes", list);
return compound;
}
2025-03-13 17:14:45 -04:00
public static NBTTagCompound factionsNBT() {
2025-01-29 18:25:34 -05:00
NBTTagList list = new NBTTagList();
NBTTagCompound compound = new NBTTagCompound();
2025-03-13 17:14:45 -04:00
for (Faction faction : FactionController.getInstance().factions.values()) {
2025-01-29 18:25:34 -05:00
NBTTagCompound factioNBT = new NBTTagCompound();
faction.writeNBT(factioNBT);
list.appendTag(factioNBT);
}
compound.setTag("Factions", list);
return compound;
}
2025-03-13 17:14:45 -04:00
public static NBTTagCompound dialogCategoriesNBT() {
2025-01-29 18:25:34 -05:00
NBTTagCompound compound = new NBTTagCompound();
NBTTagList categoryList = new NBTTagList();
2025-03-13 17:14:45 -04:00
for (DialogCategory category : DialogController.Instance.categories.values()) {
2025-01-29 19:43:45 -05:00
NBTTagCompound questCompound = new NBTTagCompound();
2025-01-29 18:25:34 -05:00
NBTTagList dialogList = new NBTTagList();
2025-03-13 17:14:45 -04:00
for (int dialogID : category.dialogs.keySet()) {
2025-01-29 18:25:34 -05:00
Dialog quest = category.dialogs.get(dialogID);
dialogList.appendTag(quest.writeToNBT(new NBTTagCompound()));
}
questCompound.setTag("Data", dialogList);
questCompound.setTag("CatNBT", category.writeSmallNBT(new NBTTagCompound()));
2025-01-29 19:43:45 -05:00
categoryList.appendTag(questCompound);
2025-01-29 18:25:34 -05:00
}
compound.setTag("DialogCategories", categoryList);
return compound;
}
2025-03-13 17:14:45 -04:00
public static NBTTagCompound questCategoriesNBT() {
2025-01-29 19:43:45 -05:00
NBTTagCompound compound = new NBTTagCompound();
NBTTagList categoryList = new NBTTagList();
2025-03-13 17:14:45 -04:00
for (QuestCategory category : QuestController.Instance.categories.values()) {
NBTTagCompound questCompound = new NBTTagCompound();
;
2025-01-29 19:43:45 -05:00
NBTTagList questList = new NBTTagList();
2025-03-13 17:14:45 -04:00
for (int questID : category.quests.keySet()) {
2025-01-29 19:43:45 -05:00
Quest quest = category.quests.get(questID);
questList.appendTag(quest.writeToNBT(new NBTTagCompound()));
}
questCompound.setTag("Data", questList);
questCompound.setTag("CatNBT", category.writeSmallNBT(new NBTTagCompound()));
categoryList.appendTag(questCompound);
}
compound.setTag("QuestCategories", categoryList);
return compound;
}
2025-03-13 17:14:45 -04:00
public static NBTTagCompound customEffectsNBT() {
2025-02-10 16:38:50 -05:00
NBTTagList list = new NBTTagList();
NBTTagCompound compound = new NBTTagCompound();
2025-02-12 03:15:28 -05:00
for (CustomEffect effect : CustomEffectController.getInstance().getCustomEffects().values()) {
2025-02-10 16:38:50 -05:00
list.appendTag(effect.writeToNBT(false));
}
compound.setTag("Data", list);
return compound;
}
2025-03-13 17:14:45 -04:00
public static NBTTagCompound magicsNBT() {
2025-03-04 18:19:53 -05:00
NBTTagList list = new NBTTagList();
NBTTagCompound compound = new NBTTagCompound();
for (Magic magic : MagicController.getInstance().magics.values()) {
NBTTagCompound magicCompound = new NBTTagCompound();
magic.writeNBT(magicCompound);
list.appendTag(magicCompound);
}
compound.setTag("Data", list);
return compound;
}
2025-03-13 17:14:45 -04:00
public static NBTTagCompound magicCyclesNBT() {
2025-03-04 18:19:53 -05:00
NBTTagList list = new NBTTagList();
NBTTagCompound compound = new NBTTagCompound();
for (MagicCycle cycle : MagicController.getInstance().cycles.values()) {
NBTTagCompound cycleCompound = new NBTTagCompound();
cycle.writeNBT(cycleCompound);
list.appendTag(cycleCompound);
}
compound.setTag("Data", list);
return compound;
}
2025-03-13 17:14:45 -04:00
public static void syncPlayerData(EntityPlayerMP player, boolean update) {
2025-01-29 18:25:34 -05:00
PlayerData data = PlayerData.get(player);
2025-03-13 17:14:45 -04:00
if (data != null) {
if (update) {
PacketHandler.Instance.sendToPlayer(new SyncPacket(
2025-01-29 18:25:34 -05:00
EnumSyncType.PLAYERDATA,
EnumSyncAction.UPDATE,
-1,
data.getSyncNBT())
, player);
} else {
PacketHandler.Instance.sendToPlayer(new SyncPacket(
2025-01-29 18:25:34 -05:00
EnumSyncType.PLAYERDATA,
EnumSyncAction.RELOAD,
-1,
data.getSyncNBTFull()), player);
}
}
}
2025-03-13 17:14:45 -04:00
public static void syncRemove(EnumSyncType enumSyncType, int id) {
2025-10-19 22:06:11 -04:00
Map<EnumSyncType, Integer> revisions = invalidateCaches(enumSyncType);
int revision = revisions.getOrDefault(enumSyncType, getCurrentRevision(enumSyncType));
PacketHandler.Instance.sendToAll(new SyncPacket(
2025-01-29 18:25:34 -05:00
enumSyncType,
EnumSyncAction.REMOVE,
id,
2025-10-19 22:06:11 -04:00
revision,
2025-01-29 18:25:34 -05:00
new NBTTagCompound()
));
2025-10-19 22:06:11 -04:00
updateAllPlayerRevisions(revisions);
2025-01-29 18:25:34 -05:00
}
public static void syncAllDialogs() {
2025-10-19 22:06:11 -04:00
CachedSyncPayload payload = rebuildNow(EnumSyncType.DIALOG_CATEGORY);
if (payload == null) {
return;
}
PacketHandler.Instance.sendToAll(new SyncPacket(EnumSyncType.DIALOG_CATEGORY, payload));
updateAllPlayerRevisions(EnumSyncType.DIALOG_CATEGORY, payload.getRevision());
2025-01-29 18:25:34 -05:00
}
public static void syncAllQuests() {
2025-10-19 22:06:11 -04:00
CachedSyncPayload payload = rebuildNow(EnumSyncType.QUEST_CATEGORY);
if (payload == null) {
return;
}
PacketHandler.Instance.sendToAll(new SyncPacket(EnumSyncType.QUEST_CATEGORY, payload));
updateAllPlayerRevisions(EnumSyncType.QUEST_CATEGORY, payload.getRevision());
2025-01-29 18:25:34 -05:00
}
2025-01-30 09:22:43 -05:00
public static void syncAllWorkbenchRecipes() {
2025-10-19 22:06:11 -04:00
CachedSyncPayload payload = rebuildNow(EnumSyncType.WORKBENCH_RECIPES);
if (payload == null) {
return;
}
PacketHandler.Instance.sendToAll(new SyncPacket(EnumSyncType.WORKBENCH_RECIPES, payload));
updateAllPlayerRevisions(EnumSyncType.WORKBENCH_RECIPES, payload.getRevision());
2025-01-30 09:22:43 -05:00
}
public static void syncAllCarpentryRecipes() {
2025-10-19 22:06:11 -04:00
CachedSyncPayload payload = rebuildNow(EnumSyncType.CARPENTRY_RECIPES);
if (payload == null) {
return;
}
PacketHandler.Instance.sendToAll(new SyncPacket(EnumSyncType.CARPENTRY_RECIPES, payload));
updateAllPlayerRevisions(EnumSyncType.CARPENTRY_RECIPES, payload.getRevision());
2025-01-30 09:22:43 -05:00
}
2025-02-10 02:44:28 -05:00
public static void syncAllAnvilRecipes() {
2025-10-19 22:06:11 -04:00
CachedSyncPayload payload = rebuildNow(EnumSyncType.ANVIL_RECIPES);
if (payload == null) {
return;
}
PacketHandler.Instance.sendToAll(new SyncPacket(EnumSyncType.ANVIL_RECIPES, payload));
updateAllPlayerRevisions(EnumSyncType.ANVIL_RECIPES, payload.getRevision());
2025-02-10 02:44:28 -05:00
}
2025-02-10 16:38:50 -05:00
public static void syncAllCustomEffects() {
2025-10-19 22:06:11 -04:00
CachedSyncPayload payload = rebuildNow(EnumSyncType.CUSTOM_EFFECTS);
if (payload == null) {
return;
}
PacketHandler.Instance.sendToAll(new SyncPacket(EnumSyncType.CUSTOM_EFFECTS, payload));
updateAllPlayerRevisions(EnumSyncType.CUSTOM_EFFECTS, payload.getRevision());
2025-02-10 16:38:50 -05:00
}
2025-01-29 18:25:34 -05:00
@SideOnly(Side.CLIENT)
2025-10-19 22:06:11 -04:00
public static void clientSync(EnumSyncType enumSyncType, int revision, NBTTagCompound fullCompound) {
2025-03-13 17:14:45 -04:00
switch (enumSyncType) {
2025-01-29 18:25:34 -05:00
case FACTION: {
NBTTagList list = fullCompound.getTagList("Factions", 10);
for (int i = 0; i < list.tagCount(); i++) {
Faction faction = new Faction();
faction.readNBT(list.getCompoundTagAt(i));
FactionController.getInstance().factionsSync.put(faction.id, faction);
}
FactionController.getInstance().factions = FactionController.getInstance().factionsSync;
FactionController.getInstance().factionsSync = new HashMap<Integer, Faction>();
break;
}
case DIALOG_CATEGORY: {
2025-03-13 17:14:45 -04:00
if (!fullCompound.hasNoTags()) {
2025-01-29 18:25:34 -05:00
NBTTagList categories = fullCompound.getTagList("DialogCategories", 10);
2025-03-13 17:14:45 -04:00
for (int j = 0; j < fullCompound.getTagList("DialogCategories", 10).tagCount(); j++) {
2025-01-29 18:25:34 -05:00
NBTTagCompound categoryCompound = categories.getCompoundTagAt(j);
2025-03-13 17:14:45 -04:00
if (categoryCompound.hasNoTags())
2025-01-29 18:25:34 -05:00
continue;
DialogCategory category = new DialogCategory();
category.readSmallNBT(categoryCompound.getCompoundTag("CatNBT"));
NBTTagList dialogList = categoryCompound.getTagList("Data", 10);
2025-03-13 17:14:45 -04:00
if (DialogController.Instance.categoriesSync.containsKey(category.id)) {
2025-01-29 18:25:34 -05:00
category = DialogController.Instance.categoriesSync.get(category.id);
category.readSmallNBT(categoryCompound.getCompoundTag("CatNBT"));
}
2025-03-13 17:14:45 -04:00
for (int i = 0; i < dialogList.tagCount(); i++) {
2025-01-29 18:25:34 -05:00
Dialog dialog = new Dialog();
dialog.readNBT(dialogList.getCompoundTagAt(i));
dialog.category = category;
category.dialogs.put(dialog.id, dialog);
}
DialogController.Instance.categoriesSync.put(category.id, category);
}
}
HashMap<Integer, Dialog> dialogs = new HashMap<Integer, Dialog>();
2025-03-13 17:14:45 -04:00
for (DialogCategory category : DialogController.Instance.categoriesSync.values()) {
for (Dialog dialog : category.dialogs.values()) {
2025-01-29 18:25:34 -05:00
dialogs.put(dialog.id, dialog);
}
}
DialogController.Instance.categories = DialogController.Instance.categoriesSync;
DialogController.Instance.dialogs = dialogs;
DialogController.Instance.categoriesSync = new HashMap<Integer, DialogCategory>();
break;
}
case QUEST_CATEGORY: {
2025-03-13 17:14:45 -04:00
if (!fullCompound.hasNoTags()) {
2025-01-29 18:25:34 -05:00
NBTTagList categories = fullCompound.getTagList("QuestCategories", 10);
2025-03-13 17:14:45 -04:00
for (int j = 0; j < fullCompound.getTagList("QuestCategories", 10).tagCount(); j++) {
2025-01-29 18:25:34 -05:00
NBTTagCompound categoryCompound = categories.getCompoundTagAt(j);
2025-03-13 17:14:45 -04:00
if (categoryCompound.hasNoTags())
2025-01-29 18:25:34 -05:00
continue;
QuestCategory category = new QuestCategory();
category.readSmallNBT(categoryCompound.getCompoundTag("CatNBT"));
NBTTagList questList = categoryCompound.getTagList("Data", 10);
2025-03-13 17:14:45 -04:00
if (QuestController.Instance.categoriesSync.containsKey(category.id)) {
2025-01-29 18:25:34 -05:00
category = QuestController.Instance.categoriesSync.get(category.id);
category.readSmallNBT(categoryCompound.getCompoundTag("CatNBT"));
}
2025-03-13 17:14:45 -04:00
for (int i = 0; i < questList.tagCount(); i++) {
2025-01-29 18:25:34 -05:00
Quest quest = new Quest();
quest.readNBT(questList.getCompoundTagAt(i));
quest.category = category;
category.quests.put(quest.id, quest);
}
QuestController.Instance.categoriesSync.put(category.id, category);
}
}
2025-03-13 17:14:45 -04:00
HashMap<Integer, Quest> quests = new HashMap<Integer, Quest>();
for (QuestCategory category : QuestController.Instance.categoriesSync.values()) {
for (Quest quest : category.quests.values()) {
2025-01-29 18:25:34 -05:00
quests.put(quest.id, quest);
}
}
QuestController.Instance.categories = QuestController.Instance.categoriesSync;
QuestController.Instance.quests = quests;
QuestController.Instance.categoriesSync = new HashMap<Integer, QuestCategory>();
break;
}
case PLAYERDATA: {
ClientCacheHandler.playerData.setSyncNBTFull(fullCompound);
break;
}
2025-03-04 18:19:53 -05:00
case MAGIC: {
NBTTagList list = fullCompound.getTagList("Data", 10);
MagicController mc = MagicController.getInstance();
mc.magicSync.clear();
for (int i = 0; i < list.tagCount(); i++) {
Magic magic = new Magic();
magic.readNBT(list.getCompoundTagAt(i));
mc.magicSync.put(magic.id, magic);
}
mc.magics.clear();
mc.magics.putAll(mc.magicSync);
mc.magicSync.clear();
break;
}
case MAGIC_CYCLE: {
NBTTagList list = fullCompound.getTagList("Data", 10);
MagicController mc = MagicController.getInstance();
mc.cyclesSync.clear();
for (int i = 0; i < list.tagCount(); i++) {
MagicCycle cycle = new MagicCycle();
cycle.readNBT(list.getCompoundTagAt(i));
mc.cyclesSync.put(cycle.id, cycle);
}
mc.cycles.clear();
mc.cycles.putAll(mc.cyclesSync);
mc.cyclesSync.clear();
break;
}
2025-01-30 09:22:43 -05:00
case WORKBENCH_RECIPES: {
NBTTagList list = fullCompound.getTagList("recipes", 10);
2025-03-13 17:14:45 -04:00
if (list == null)
2025-01-30 09:22:43 -05:00
return;
2025-03-13 17:14:45 -04:00
for (int i = 0; i < list.tagCount(); i++) {
2025-07-09 15:40:31 -04:00
NBTTagCompound recipeCompound = list.getCompoundTagAt(i);
RecipeCarpentry recipe = RecipeCarpentry.create(recipeCompound);
recipe.readNBT(recipeCompound);
2025-03-13 17:14:45 -04:00
RecipeController.syncRecipes.put(recipe.id, recipe);
2025-01-30 09:22:43 -05:00
}
RecipeController.reloadGlobalRecipes(RecipeController.syncRecipes);
RecipeController.syncRecipes = new HashMap<Integer, RecipeCarpentry>();
break;
}
case CARPENTRY_RECIPES: {
NBTTagList list = fullCompound.getTagList("recipes", 10);
2025-03-13 17:14:45 -04:00
if (list == null)
2025-01-30 09:22:43 -05:00
return;
2025-03-13 17:14:45 -04:00
for (int i = 0; i < list.tagCount(); i++) {
2025-07-09 15:40:31 -04:00
NBTTagCompound recipeCompound = list.getCompoundTagAt(i);
RecipeCarpentry recipe = RecipeCarpentry.create(recipeCompound);
recipe.readNBT(recipeCompound);
2025-03-13 17:14:45 -04:00
RecipeController.syncRecipes.put(recipe.id, recipe);
2025-01-30 09:22:43 -05:00
}
RecipeController.Instance.carpentryRecipes = RecipeController.syncRecipes;
RecipeController.syncRecipes = new HashMap<Integer, RecipeCarpentry>();
break;
}
2025-02-10 02:44:28 -05:00
case ANVIL_RECIPES: {
NBTTagList list = fullCompound.getTagList("recipes", 10);
2025-03-13 17:14:45 -04:00
if (list == null)
2025-02-10 02:44:28 -05:00
return;
2025-03-13 17:14:45 -04:00
for (int i = 0; i < list.tagCount(); i++) {
2025-07-09 15:40:31 -04:00
RecipeAnvil recipe = new RecipeAnvil();
recipe.readNBT(list.getCompoundTagAt(i));
2025-02-10 02:44:28 -05:00
RecipeController.syncAnvilRecipes.put(recipe.id, recipe);
}
RecipeController.Instance.anvilRecipes = RecipeController.syncAnvilRecipes;
RecipeController.syncAnvilRecipes = new HashMap<Integer, RecipeAnvil>();
break;
}
2025-02-10 16:38:50 -05:00
case CUSTOM_EFFECTS: {
NBTTagList list = fullCompound.getTagList("Data", 10);
2025-02-12 03:15:28 -05:00
CustomEffectController ce = CustomEffectController.getInstance();
ce.customEffectsSync.clear();
2025-02-10 16:38:50 -05:00
for (int i = 0; i < list.tagCount(); i++) {
CustomEffect effect = new CustomEffect();
effect.readFromNBT(list.getCompoundTagAt(i));
2025-02-10 17:24:26 -05:00
ClientCacheHandler.getImageData(effect.icon);
2025-02-12 03:15:28 -05:00
ce.customEffectsSync.put(effect.id, effect);
2025-02-10 16:38:50 -05:00
}
2025-02-12 03:15:28 -05:00
ce.getCustomEffects().clear();
ce.getCustomEffects().putAll(ce.customEffectsSync);
ce.customEffectsSync.clear();
2025-02-10 16:38:50 -05:00
break;
}
2025-01-29 18:25:34 -05:00
}
2025-10-19 22:06:11 -04:00
ClientCacheHandler.updateClientRevision(enumSyncType, revision);
2025-01-29 18:25:34 -05:00
}
/**
* Update Related Tasks
*/
@SideOnly(Side.CLIENT)
2025-10-19 22:06:11 -04:00
public static void clientUpdate(EnumSyncType enumSyncType, int category_id, int revision, NBTTagCompound compound) {
2025-03-13 17:14:45 -04:00
switch (enumSyncType) {
2025-01-29 18:25:34 -05:00
case FACTION: {
Faction faction = new Faction();
faction.readNBT(compound);
FactionController.getInstance().factions.put(faction.id, faction);
break;
}
case DIALOG: {
DialogCategory category = DialogController.Instance.categories.get(category_id);
Dialog dialog = new Dialog();
dialog.category = category;
dialog.readNBT(compound);
DialogController.Instance.dialogs.put(dialog.id, dialog);
category.dialogs.put(dialog.id, dialog);
break;
}
case QUEST: {
QuestCategory category = QuestController.Instance.categories.get(category_id);
Quest quest = new Quest();
quest.category = category;
quest.readNBT(compound);
QuestController.Instance.quests.put(quest.id, quest);
category.quests.put(quest.id, quest);
break;
}
case QUEST_CATEGORY: {
QuestCategory category = new QuestCategory();
category.readSmallNBT(compound.getCompoundTag("CatNBT"));
NBTTagList list = compound.getTagList("Data", 10);
2025-03-13 17:14:45 -04:00
if (QuestController.Instance.categoriesSync.containsKey(category.id)) {
2025-01-29 18:25:34 -05:00
category = QuestController.Instance.categoriesSync.get(category.id);
category.readSmallNBT(compound.getCompoundTag("CatNBT"));
}
2025-03-13 17:14:45 -04:00
for (int i = 0; i < list.tagCount(); i++) {
2025-01-29 18:25:34 -05:00
Quest quest = new Quest();
quest.readNBT(list.getCompoundTagAt(i));
quest.category = category;
category.quests.put(quest.id, quest);
}
QuestController.Instance.categories.put(category.id, category);
break;
}
case DIALOG_CATEGORY: {
DialogCategory category = new DialogCategory();
category.readSmallNBT(compound.getCompoundTag("CatNBT"));
NBTTagList list = compound.getTagList("Data", 10);
2025-03-13 17:14:45 -04:00
if (DialogController.Instance.categoriesSync.containsKey(category.id)) {
2025-01-29 18:25:34 -05:00
category = DialogController.Instance.categoriesSync.get(category.id);
category.readSmallNBT(compound.getCompoundTag("CatNBT"));
}
2025-03-13 17:14:45 -04:00
for (int i = 0; i < list.tagCount(); i++) {
2025-01-29 18:25:34 -05:00
Dialog dialog = new Dialog();
dialog.readNBT(list.getCompoundTagAt(i));
dialog.category = category;
category.dialogs.put(dialog.id, dialog);
}
DialogController.Instance.categories.put(category.id, category);
break;
}
2025-03-04 18:19:53 -05:00
case MAGIC_CYCLE: {
MagicCycle cycle = new MagicCycle();
cycle.readNBT(compound);
MagicController.getInstance().cycles.put(cycle.id, cycle);
break;
}
case MAGIC: {
Magic magic = new Magic();
magic.readNBT(compound);
MagicController.getInstance().magics.put(magic.id, magic);
break;
}
2025-01-29 18:25:34 -05:00
case PLAYERDATA: {
ClientCacheHandler.playerData.setSyncNBT(compound);
break;
}
2025-02-10 16:38:50 -05:00
case CUSTOM_EFFECTS: {
CustomEffect effect = new CustomEffect();
effect.readFromNBT(compound);
ClientCacheHandler.getImageData(effect.icon);
2025-02-12 03:15:28 -05:00
CustomEffectController.Instance.getCustomEffects().put(effect.id, effect);
2025-02-10 16:38:50 -05:00
break;
}
2025-01-29 18:25:34 -05:00
}
2025-10-19 22:06:11 -04:00
ClientCacheHandler.updateClientRevision(enumSyncType, revision);
2025-01-29 18:25:34 -05:00
}
public static void syncUpdate(EnumSyncType type, int cat, NBTTagCompound compound) {
2025-10-19 22:06:11 -04:00
Map<EnumSyncType, Integer> revisions = invalidateCaches(type);
int revision = revisions.getOrDefault(type, getCurrentRevision(type));
PacketHandler.Instance.sendToAll(new SyncPacket(
2025-01-29 18:25:34 -05:00
type,
EnumSyncAction.UPDATE,
cat,
2025-10-19 22:06:11 -04:00
revision,
2025-01-29 18:25:34 -05:00
compound
));
2025-10-19 22:06:11 -04:00
updateAllPlayerRevisions(revisions);
2025-01-29 18:25:34 -05:00
}
public static NBTTagCompound updateQuestCat(QuestCategory questCategory) {
NBTTagCompound questCompound = new NBTTagCompound();
NBTTagList questList = new NBTTagList();
2025-03-13 17:14:45 -04:00
for (int questID : questCategory.quests.keySet()) {
2025-01-29 18:25:34 -05:00
Quest quest = questCategory.quests.get(questID);
questList.appendTag(quest.writeToNBT(new NBTTagCompound()));
}
questCompound.setTag("Data", questList);
questCompound.setTag("CatNBT", questCategory.writeSmallNBT(new NBTTagCompound()));
return questCompound;
}
public static NBTTagCompound updateDialogCat(DialogCategory dialogCategory) {
NBTTagCompound dialogCompound = new NBTTagCompound();
NBTTagList dialogList = new NBTTagList();
2025-03-13 17:14:45 -04:00
for (int questID : dialogCategory.dialogs.keySet()) {
2025-01-29 18:25:34 -05:00
Dialog dialog = dialogCategory.dialogs.get(questID);
dialogList.appendTag(dialog.writeToNBT(new NBTTagCompound()));
}
dialogCompound.setTag("Data", dialogList);
dialogCompound.setTag("CatNBT", dialogCategory.writeSmallNBT(new NBTTagCompound()));
return dialogCompound;
}
@SideOnly(Side.CLIENT)
2025-10-19 22:06:11 -04:00
public static void clientSyncRemove(EnumSyncType enumSyncType, int id, int revision) {
2025-03-13 17:14:45 -04:00
switch (enumSyncType) {
2025-01-29 18:25:34 -05:00
case FACTION:
FactionController.getInstance().factions.remove(id);
break;
case DIALOG:
Dialog dialog = DialogController.Instance.dialogs.remove(id);
2025-03-13 17:14:45 -04:00
if (dialog != null) {
2025-01-29 18:25:34 -05:00
dialog.category.dialogs.remove(id);
}
break;
case DIALOG_CATEGORY:
DialogCategory dialogCategory = DialogController.Instance.categories.remove(id);
2025-03-13 17:14:45 -04:00
if (dialogCategory != null) {
2025-01-29 18:25:34 -05:00
DialogController.Instance.dialogs.keySet().removeAll(dialogCategory.dialogs.keySet());
}
break;
case QUEST:
Quest quest = QuestController.Instance.quests.remove(id);
2025-03-13 17:14:45 -04:00
if (quest != null) {
2025-01-29 18:25:34 -05:00
quest.category.quests.remove(id);
}
break;
case QUEST_CATEGORY:
QuestCategory questCategory = QuestController.Instance.categories.remove(id);
2025-03-13 17:14:45 -04:00
if (questCategory != null) {
2025-01-29 18:25:34 -05:00
QuestController.Instance.quests.keySet().removeAll(questCategory.quests.keySet());
}
break;
case MAGIC:
2025-03-13 17:14:45 -04:00
for (MagicCycle cycle : MagicController.getInstance().cycles.values()) {
2025-03-04 18:19:53 -05:00
cycle.associations.remove(id);
}
MagicController.getInstance().cycles.remove(id);
break;
case MAGIC_CYCLE:
MagicController.getInstance().cycles.remove(id);
2025-01-29 18:25:34 -05:00
break;
2025-02-10 16:38:50 -05:00
case CUSTOM_EFFECTS:
2025-02-12 03:15:28 -05:00
CustomEffectController.Instance.getCustomEffects().remove(id);
2025-02-10 16:38:50 -05:00
break;
2025-01-29 18:25:34 -05:00
}
2025-10-19 22:06:11 -04:00
ClientCacheHandler.updateClientRevision(enumSyncType, revision);
2025-01-29 18:25:34 -05:00
}
2025-02-10 21:41:54 -05:00
2025-03-13 17:14:45 -04:00
public static void syncEffects(EntityPlayerMP playerMP) {
2025-02-10 21:41:54 -05:00
ConcurrentHashMap<EffectKey, PlayerEffect> playerEffects = CustomEffectController.getInstance().getPlayerEffects(playerMP);
PlayerData playerData = PlayerData.get(playerMP);
playerData.effectData.setEffects(playerEffects);
NBTTagCompound compound = playerData.getPlayerEffects();
PacketHandler.Instance.sendToPlayer(new SyncEffectPacket(compound), playerMP);
}
@SideOnly(Side.CLIENT)
public static void clientSyncEffects(NBTTagCompound compound) {
PlayerData playerData = PlayerData.get(Minecraft.getMinecraft().thePlayer);
2025-03-13 17:14:45 -04:00
if (playerData != null) {
2025-02-10 21:41:54 -05:00
playerData.setPlayerEffects(compound);
}
}
2025-10-19 22:06:11 -04:00
2025-11-05 17:33:24 -05:00
private static void registerCache(EnumSyncType type, Supplier<NBTTagCompound> supplier) {
2025-11-05 17:23:21 -05:00
cacheEntries.put(type, new SyncCacheEntry(type, supplier));
2025-10-19 22:06:11 -04:00
}
private static CachedSyncPayload rebuildNow(EnumSyncType type) {
2025-11-05 17:33:24 -05:00
SyncCacheEntry entry = cacheEntries.get(type);
2025-10-19 22:06:11 -04:00
if (entry == null) {
return null;
}
entry.invalidate();
return entry.getPayload(type);
}
public static Map<EnumSyncType, Integer> invalidateCaches(EnumSyncType type) {
EnumSet<EnumSyncType> targets = getInvalidationTargets(type);
EnumMap<EnumSyncType, Integer> revisions = new EnumMap<>(EnumSyncType.class);
for (EnumSyncType target : targets) {
2025-11-05 17:33:24 -05:00
SyncCacheEntry entry = cacheEntries.get(target);
2025-10-19 22:06:11 -04:00
if (entry != null) {
int newRevision = entry.invalidate();
revisions.put(target, newRevision);
}
}
return revisions;
}
private static EnumSet<EnumSyncType> getInvalidationTargets(EnumSyncType type) {
switch (type) {
case DIALOG:
case DIALOG_CATEGORY:
return EnumSet.of(EnumSyncType.DIALOG_CATEGORY);
case QUEST:
case QUEST_CATEGORY:
return EnumSet.of(EnumSyncType.QUEST_CATEGORY);
case FACTION:
return EnumSet.of(EnumSyncType.FACTION);
case MAGIC:
return EnumSet.of(EnumSyncType.MAGIC);
case MAGIC_CYCLE:
return EnumSet.of(EnumSyncType.MAGIC_CYCLE);
case WORKBENCH_RECIPES:
return EnumSet.of(EnumSyncType.WORKBENCH_RECIPES);
case CARPENTRY_RECIPES:
return EnumSet.of(EnumSyncType.CARPENTRY_RECIPES);
case ANVIL_RECIPES:
return EnumSet.of(EnumSyncType.ANVIL_RECIPES);
case CUSTOM_EFFECTS:
return EnumSet.of(EnumSyncType.CUSTOM_EFFECTS);
default:
return EnumSet.noneOf(EnumSyncType.class);
}
}
private static void updateAllPlayerRevisions(EnumSyncType type, int revision) {
if (revision < 0) {
return;
}
2025-11-05 17:33:24 -05:00
for (PlayerSyncState state : playerSyncState.values()) {
2025-10-19 22:06:11 -04:00
state.updateRevision(type, revision);
}
}
private static void updateAllPlayerRevisions(Map<EnumSyncType, Integer> revisions) {
if (revisions.isEmpty()) {
return;
}
2025-11-05 17:33:24 -05:00
for (PlayerSyncState state : playerSyncState.values()) {
2025-10-19 22:06:11 -04:00
for (Map.Entry<EnumSyncType, Integer> entry : revisions.entrySet()) {
state.updateRevision(entry.getKey(), entry.getValue());
}
}
}
public static final class CachedSyncPayload {
private final int revision;
private final byte[] payload;
private final byte[][] chunks;
private CachedSyncPayload(int revision, byte[] payload, byte[][] chunks) {
this.revision = revision;
this.payload = payload;
this.chunks = chunks;
}
public int getRevision() {
return revision;
}
public byte[] getPayload() {
return payload;
}
public byte[][] getChunks() {
return chunks;
}
}
private static final class SyncCacheEntry {
private final Supplier<NBTTagCompound> supplier;
private volatile CachedSyncPayload payload;
private int revision = 0;
private boolean dirty = true;
private SyncCacheEntry(EnumSyncType type, Supplier<NBTTagCompound> supplier) {
this.supplier = supplier;
}
private synchronized CachedSyncPayload getPayload(EnumSyncType requestedType) {
if (!dirty && payload != null) {
return payload;
}
int payloadRevision = revision;
NBTTagCompound data = supplier.get();
ByteBuf buffer = Unpooled.buffer();
byte[] bytes;
try {
buffer.writeInt(requestedType.ordinal());
buffer.writeInt(EnumSyncAction.RELOAD.ordinal());
buffer.writeInt(-1);
buffer.writeInt(payloadRevision);
ByteBufUtils.writeBigNBT(buffer, data);
bytes = new byte[buffer.readableBytes()];
buffer.readBytes(bytes);
} catch (IOException e) {
throw new RuntimeException("Failed to serialize sync payload for " + requestedType, e);
} finally {
buffer.release();
}
byte[][] chunks = splitIntoChunks(bytes);
payload = new CachedSyncPayload(payloadRevision, bytes, chunks);
dirty = false;
return payload;
}
private synchronized int invalidate() {
dirty = true;
payload = null;
revision++;
return revision;
}
private synchronized int getRevisionValue() {
return revision;
}
2025-11-05 16:52:38 -05:00
private synchronized void reset() {
dirty = true;
payload = null;
revision = 0;
}
2025-10-19 22:06:11 -04:00
private static byte[][] splitIntoChunks(byte[] payload) {
int totalSize = payload.length;
int chunkCount = (totalSize + LargeAbstractPacket.CHUNK_SIZE - 1) / LargeAbstractPacket.CHUNK_SIZE;
if (chunkCount <= 0) {
chunkCount = 1;
}
byte[][] chunks = new byte[chunkCount][];
for (int i = 0; i < chunkCount; i++) {
int offset = i * LargeAbstractPacket.CHUNK_SIZE;
int length = Math.min(LargeAbstractPacket.CHUNK_SIZE, totalSize - offset);
byte[] chunk = new byte[length];
System.arraycopy(payload, offset, chunk, 0, length);
chunks[i] = chunk;
}
return chunks;
}
}
private static final class PlayerSyncState {
private final EnumMap<EnumSyncType, Integer> revisions = new EnumMap<>(EnumSyncType.class);
private PlayerSyncState(UUID ignored) {
}
private synchronized int getRevision(EnumSyncType type) {
Integer value = revisions.get(type);
return value == null ? -1 : value;
}
private synchronized void updateRevision(EnumSyncType type, int revision) {
revisions.put(type, revision);
}
private synchronized void applyHandshake(Map<EnumSyncType, Integer> incoming) {
if (incoming == null || incoming.isEmpty()) {
return;
}
revisions.putAll(incoming);
}
private synchronized void reset() {
revisions.clear();
}
2025-10-19 22:06:11 -04:00
}
2025-01-29 18:25:34 -05:00
}