@SideOnly(Side.CLIENT) & server

Версия Minecraft
1.7.10
API
Forge
192
2
9
Всем привет. Сразу скажу, что я не прошу помогать мне что-то фиксить. Я прошу лишь объяснить на пальцах, как работает прокси. Если я смогу понять эту тему, вряд ли я буду приходить на форум с подобными вопросами в поисках решений! Я читал ряд статей, в том числе и статью большую (где-то тут) отведенную конкретно под прокси, но вопросы всё еще остаются.

Собрал сборку, решил поиграть, но сервер не запускается из-за крашей. Краш вполне себе понятный, сервер пытается обратиться к клиентским полям (иногда и к классам):
Краш:
cpw.mods.fml.common.LoaderException: java.lang.NoSuchFieldError: icons
    at cpw.mods.fml.common.LoadController.transition(LoadController.java:163)
    at cpw.mods.fml.common.Loader.preinitializeMods(Loader.java:559)
    at cpw.mods.fml.server.FMLServerHandler.beginServerLoading(FMLServerHandler.java:88)
    at cpw.mods.fml.common.FMLCommonHandler.onServerStart(FMLCommonHandler.java:314)
    at net.minecraft.server.dedicated.DedicatedServer.func_71197_b(DedicatedServer.java:117)
    at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:387)
    at net.minecraft.server.MinecraftServer$2.run(MinecraftServer.java:685)
Caused by: java.lang.NoSuchFieldError: icons
    at thaumrev.item.armor.ItemThauminiteFortressArmor.<init>(ItemThauminiteFortressArmor.java:33)
    at thaumrev.config.ConfigItems.initializeThaumicBasesIntegration(ConfigItems.java:118)
    at thaumrev.config.ConfigItems.init(ConfigItems.java:30)
    at thaumrev.common.CommonProxy.preInit(CommonProxy.java:28)
    at thaumrev.ThaumicRevelations.preInit(ThaumicRevelations.java:57)

Залезаю в код предмета, вижу нечто подобное:
ItemThauminiteFortressArmor:
package thaumrev.item.armor;

import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumAction;
import net.minecraft.item.EnumRarity;
import net.minecraft.item.ItemArmor;
import net.minecraft.item.ItemStack;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IIcon;
import net.minecraft.util.StatCollector;
import net.minecraftforge.common.ISpecialArmor;
import net.minecraftforge.oredict.OreDictionary;
import thaumcraft.api.IGoggles;
import thaumcraft.api.IRepairable;
import thaumcraft.api.IRunicArmor;
import thaumcraft.api.IVisDiscountGear;
import thaumcraft.api.aspects.Aspect;
import thaumcraft.api.nodes.IRevealer;
import thaumrev.client.models.ModelThauminiteFortressArmor;
import thaumrev.config.ConfigLibrary;

public class ItemThauminiteFortressArmor extends ItemArmor implements IRepairable, IRunicArmor, ISpecialArmor, IGoggles, IRevealer, IVisDiscountGear {
  
    @SideOnly(Side.CLIENT)
    public IIcon[] icons = new IIcon[3];
 
    @SideOnly(Side.CLIENT)
    ModelBiped[] models = new ModelBiped[3];
  
    public ItemThauminiteFortressArmor(int type, String name) {
        super(ConfigLibrary.armorMaterialFortressThauminite, 4, type);
        this.setUnlocalizedName(name);
        this.setCreativeTab(ConfigLibrary.tabThaumRev);
    }
  
    public int getVisDiscount(ItemStack armor, EntityPlayer entityPlayer, Aspect aspect) { return 4; }
  
    public ArmorProperties getProperties(EntityLivingBase player, ItemStack armor, DamageSource source, double damage, int slot) {
        int priority = 1;
        double ratio = this.damageReduceAmount / 20.0D;
      
        if (source.isMagicDamage()) ratio = this.damageReduceAmount / 35.0D;
        else if (!source.isFireDamage() && !source.isExplosion() && source.isUnblockable()) {
            ratio = 0.0D;
            priority = 0;
        }
      
        if (player instanceof EntityPlayer) {
            double set = 0.875D;
            for (ItemStack piece : ((EntityPlayer)player).inventory.armorInventory) {
                if (piece != null && piece.getItem() instanceof ItemThauminiteFortressArmor) {
                    set += 0.125D;
                    if (piece.hasTagCompound() && piece.stackTagCompound.hasKey("mask")) set += 0.05D;
                }
            }
            ratio *= set;
        }
      
        return new ISpecialArmor.ArmorProperties(priority, ratio, armor.getMaxDamage() + 1 - armor.getItemDamageForDisplay());
    }
  
    public EnumRarity getRarity(ItemStack armor) { return EnumRarity.rare; }
  
    public void addInformation(ItemStack armor, EntityPlayer player, List<String> list, boolean par4) {
        if (armor.hasTagCompound() && armor.stackTagCompound.hasKey("goggles"))
            list.add(EnumChatFormatting.DARK_PURPLE + StatCollector.translateToLocal("item.ItemGoggles.name"));
      
        if (armor.hasTagCompound() && armor.stackTagCompound.hasKey("mask"))
            list.add(EnumChatFormatting.GOLD + StatCollector.translateToLocal("item.HelmetFortress.mask." + armor.stackTagCompound.getInteger("mask")));
      
        list.add("");
        list.add(EnumChatFormatting.DARK_PURPLE + StatCollector.translateToLocal("tc.visdiscount") + ": " + getVisDiscount(armor, player, null) + "%");
        super.addInformation(armor, player, list, par4);
    }
  
    public boolean getIsRepairable(ItemStack armor, ItemStack material) {
        ArrayList<ItemStack> thauminiteList = OreDictionary.getOres("ingotThauminite");
        boolean isRepairable = false;
      
        if (super.getIsRepairable(armor, material)) return true;
      
        for (ItemStack thauminite : thauminiteList)
            isRepairable = material.isItemEqual(thauminite);
      
        return isRepairable;
    }
  
    public int getArmorDisplay(EntityPlayer player, ItemStack armor, int slot) { return this.damageReduceAmount; }
  
    public void damageArmor(EntityLivingBase entity, ItemStack armor, DamageSource source, int damage, int slot) {
        if (source != DamageSource.fall) armor.damageItem(damage, entity);
    }
  
    public int getRunicCharge(ItemStack armor) { return 0; }
  
    public boolean showNodes(ItemStack armor, EntityLivingBase player) { return (armor.hasTagCompound() && armor.stackTagCompound.hasKey("goggles")); }
  
    public boolean showIngamePopups(ItemStack armor, EntityLivingBase player) { return (armor.hasTagCompound() && armor.stackTagCompound.hasKey("goggles")); }
  
    @SideOnly(Side.CLIENT)
    public void registerIcons(IIconRegister register) {
        this.icons[0] = register.registerIcon("thaumrev:armor/thauminitefortresshelm");
        this.icons[1] = register.registerIcon("thaumrev:armor/thauminitefortresschest");
        this.icons[2] = register.registerIcon("thaumrev:armor/thauminitefortresslegs");
    }
  
    @SideOnly(Side.CLIENT)
    public IIcon getIconFromDamage(int par1) { return this.icons[this.armorType]; }
  
    @SideOnly(Side.CLIENT)
    public ModelBiped getArmorModel(EntityLivingBase entity, ItemStack armor, int armorSlot) {
        int type = ((ItemArmor)armor.getItem()).armorType;
      
        if (this.models[1] == null) this.models[1] = new ModelThauminiteFortressArmor(1.0F);
        if (this.models[2] == null) this.models[2] = new ModelThauminiteFortressArmor(0.5F);
        if (type != 1 && type != 3) this.models[0] = this.models[2];
        else this.models[0] = this.models[1];
      
        if (this.models[0] != null) {
            this.models[0].bipedHead.showModel = (armorSlot == 0);
            this.models[0].bipedHeadwear.showModel = (armorSlot == 0);
            this.models[0].bipedBody.showModel = (armorSlot == 1 || armorSlot == 2);
            this.models[0].bipedRightArm.showModel = (armorSlot == 1);
            this.models[0].bipedLeftArm.showModel = (armorSlot == 1);
            this.models[0].bipedRightLeg.showModel = (armorSlot == 2);
            this.models[0].bipedLeftLeg.showModel = (armorSlot == 2);
            this.models[0].isSneak = entity.isSneaking();
            this.models[0].isRiding = entity.isRiding();
            this.models[0].isChild = entity.isChild();
            this.models[0].aimedBow = false;
            this.models[0].heldItemRight = (entity.getHeldItem() != null) ? 1 : 0;
            if (entity instanceof EntityPlayer && ((EntityPlayer)entity).getItemInUseDuration() > 0) {
                EnumAction enumaction = ((EntityPlayer)entity).getItemInUse().getItemUseAction();
                if (enumaction == EnumAction.block) (this.models[0]).heldItemRight = 3;
                else if (enumaction == EnumAction.bow) (this.models[0]).aimedBow = true;
            }
        }
      
        return this.models[0];
    }
  
    @SideOnly(Side.CLIENT)
    public String getArmorTexture(ItemStack armor, Entity entity, int slot, String type) { return "thaumrev:textures/models/thauminite_fortress_armor.png"; }
  
}

Прокси:
ClientProxy:
package thaumrev.client;

import cpw.mods.fml.client.registry.ClientRegistry;
import cpw.mods.fml.client.registry.RenderingRegistry;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.network.NetworkRegistry;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
import thaumrev.ThaumicRevelations;
import thaumrev.client.renderers.RenderPurity;
import thaumrev.client.renderers.TileKnowledgeReprocessorRenderer;
import thaumrev.common.CommonProxy;
import thaumrev.entities.EntityPurity;
import thaumrev.lib.network.ClientPacketHandler;
import thaumrev.lib.utils.KeyHandler;
import thaumrev.tiles.TileKnowledgeReprocessor;

public class ClientProxy extends CommonProxy {
  
    public void preInit() {
        super.preInit();
    }
  
    public void init() {
        super.init();
        RenderingRegistry.registerEntityRenderingHandler(EntityPurity.class, new RenderPurity());
        ClientRegistry.bindTileEntitySpecialRenderer(TileKnowledgeReprocessor.class, new TileKnowledgeReprocessorRenderer());
        FMLCommonHandler.instance().bus().register(new KeyHandler());
    }
  
    public void postInit() {
        super.postInit();
    }
  
}
CommonProxy:
package thaumrev.common;

import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import thaumrev.ThaumicRevelations;
import thaumrev.api.wardenic.WardenicChargeEvents;
import thaumrev.api.wardenic.WardenicUpgrades;
import thaumrev.client.gui.GuiHandler;
import thaumrev.config.ConfigAspects;
import thaumrev.config.ConfigBlocks;
import thaumrev.config.ConfigIntegrations;
import thaumrev.config.ConfigItems;
import thaumrev.config.ConfigLibrary;
import thaumrev.config.ConfigRecipes;
import thaumrev.config.ConfigResearches;
import thaumrev.lib.CreativeTabThaumRev;
import thaumrev.lib.network.ServerPacketHandler;
import thaumrev.lib.utils.MobDropsHandler;
import thaumrev.lib.world.ThaumRevWorldGenerator;

public class CommonProxy {
  
    public void preInit() {
        ConfigLibrary.tabThaumRev = new CreativeTabThaumRev(ThaumicRevelations.MODID);
        ConfigIntegrations.init();
        MobDropsHandler.init();
        ConfigBlocks.init();
        ConfigItems.init();
        ConfigAspects.registerAspects();
        WardenicChargeEvents.init();
        WardenicUpgrades.init();
        GameRegistry.registerWorldGenerator(new ThaumRevWorldGenerator(), 1);
    }
  
    public void init() {
        GuiHandler.init();
        ThaumicRevelations.channel = NetworkRegistry.INSTANCE.newEventDrivenChannel("thaumrev");
        ThaumicRevelations.channel.register(new ServerPacketHandler());
    }
  
    public void postInit() {
        ConfigRecipes.registerRecipes();
        ConfigAspects.registerItemAspects();
        ConfigResearches.registerResearches();
    }
  
}
Дальше по иерархии:
ConfigItems:
package thaumrev.config;

import cpw.mods.fml.common.registry.GameRegistry;
import net.minecraft.item.Item;
import net.minecraft.item.ItemSeeds;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.util.EnumHelper;
import thaumcraft.api.wands.StaffRod;
import thaumcraft.api.wands.WandRod;
import thaumrev.item.ItemExcubituraSeeds;
import thaumrev.item.ItemResource;
import thaumrev.item.ItemWardenBow;
import thaumrev.item.ItemWardenWeapon;
import thaumrev.item.ItemWaslieHammer;
import thaumrev.item.armor.ItemCrimsonHat;
import thaumrev.item.armor.ItemThauminiteFortressArmor;
import thaumrev.item.armor.ItemWardenArmor;
import thaumrev.item.armor.ItemWardenclothRobes;
import thaumrev.item.baubles.ItemLoveRing;
import thaumrev.item.baubles.ItemWardenAmulet;
import thaumrev.item.wands.ItemWandCore;
import thaumrev.item.wands.foci.ItemFocusIllumination;
import thaumrev.item.wands.foci.ItemFocusPurity;

public final class ConfigItems {
  
    public static void init() {
        if (ConfigIntegrations.isThaumicBasesLoaded) {
            initializeThaumicBasesIntegration();
            registerThaumicBasesIntegration();
        }
        initializeMaterials();
        initializeItems();
        initializeWandCores();
        registerItems();
    }
  
    private static void initializeItems() {
        ConfigLibrary.itemResource        = new ItemResource();
      
        ConfigLibrary.itemWardenWeapon    = new ItemWardenWeapon();
        ConfigLibrary.itemWardenBow        = new ItemWardenBow();
        ConfigLibrary.itemFocusPurity    = new ItemFocusPurity();
      
        ConfigLibrary.itemCrimsonHat    = new ItemCrimsonHat();
      
        ConfigLibrary.itemWardenHelm    = new ItemWardenArmor(0, "itemWardenHelm");
        ConfigLibrary.itemWardenChest    = new ItemWardenArmor(1, "itemWardenChest");
        ConfigLibrary.itemWardenLegs    = new ItemWardenArmor(2, "itemWardenLegs");
        ConfigLibrary.itemWardenBoots    = new ItemWardenArmor(3, "itemWardenBoots");
      
        ConfigLibrary.itemWardenAmulet        = new ItemWardenAmulet();
        ConfigLibrary.itemLoveRing            = new ItemLoveRing();
        ConfigLibrary.itemWaslieHammer        = new ItemWaslieHammer();
        ConfigLibrary.itemFocusIllumination = new ItemFocusIllumination();
      
        ConfigLibrary.itemWardenclothChest    = new ItemWardenclothRobes(1, "itemWardenclothChest");
        ConfigLibrary.itemWardenclothLegs    = new ItemWardenclothRobes(2, "itemWardenclothLegs");
        ConfigLibrary.itemWardenclothBoots    = new ItemWardenclothRobes(3, "itemWardenclothBoots");
      
        ConfigLibrary.itemExcubituraSeeds    = new ItemExcubituraSeeds();
    }
  
    private static void initializeMaterials() {
        ConfigLibrary.armorMaterialWarden        = EnumHelper.addArmorMaterial("WARDEN", 999, new int[] { 3, 8, 6, 3 }, 0);
        ConfigLibrary.armorMaterialWardencloth    = EnumHelper.addArmorMaterial("WARDENCLOTH", 50, new int[] { 2, 4, 2, 1 }, 15);
        ConfigLibrary.armorMaterialCrimsoncloth = EnumHelper.addArmorMaterial("CRIMSONCLOTH", 50, new int[] { 2, 3, 2, 1 }, 15);
        ConfigLibrary.toolMaterialWarden        = EnumHelper.addToolMaterial("WARDEN", 9, 999, 6.0F, 12.0F, 0);
    }
  
    private static void initializeWandCores() {
        String res = "thaumrev:textures/models/rod_";
        ConfigLibrary.itemWandCore            = new ItemWandCore();
        ConfigLibrary.WAND_ROD_WARDENCLOTH    = new WandRod("wardencloth", 150, new ItemStack(ConfigLibrary.itemWandCore, 1, 0), 12, new ResourceLocation(res + "wardencloth.png"));
        ConfigLibrary.WAND_ROD_VOIDWOOD        = new WandRod("voidwood", 200, new ItemStack(ConfigLibrary.itemWandCore, 1, 1), 12, new ResourceLocation(res + "voidwood.png"));
        ConfigLibrary.WAND_ROD_CRIMSONCULT    = new WandRod("crimsoncult", 100, new ItemStack(ConfigLibrary.itemWandCore, 1, 2), 12, new ResourceLocation(res + "crimsoncult.png"));
        ConfigLibrary.WAND_ROD_CRIMSONCULT.setGlowing(true);
      
        ConfigLibrary.STAFF_ROD_WARDENCLOTH    = new StaffRod("wardencloth", 375, new ItemStack(ConfigLibrary.itemWandCore, 1, 10), 32, new ResourceLocation(res + "wardencloth_staff.png"));
        ConfigLibrary.STAFF_ROD_VOIDWOOD    = new StaffRod("voidwood", 500, new ItemStack(ConfigLibrary.itemWandCore, 1, 11), 32, new ResourceLocation(res + "voidwood_staff.png"));
        ConfigLibrary.STAFF_ROD_VOIDWOOD.setRunes(true);
      
        ConfigLibrary.STAFF_ROD_CRIMSONCULT    = new StaffRod("crimsoncult", 1000, new ItemStack(ConfigLibrary.itemWandCore, 1, 12), 32, new ResourceLocation(res + "crimsoncult_staff.png"));
        ConfigLibrary.STAFF_ROD_CRIMSONCULT.setRunes(true);
        ConfigLibrary.STAFF_ROD_CRIMSONCULT.setGlowing(true);
    }
  
    private static void registerItems() {
        GameRegistry.registerItem(ConfigLibrary.itemResource, "itemResource");
        GameRegistry.registerItem(ConfigLibrary.itemWardenWeapon, "itemWardenWeapon");
        GameRegistry.registerItem(ConfigLibrary.itemWardenBow, "itemWardenBow");
        GameRegistry.registerItem(ConfigLibrary.itemWardenAmulet, "itemWardenAmulet");
      
        GameRegistry.registerItem(ConfigLibrary.itemCrimsonHat, "itemCrimsonHat");
      
        GameRegistry.registerItem(ConfigLibrary.itemWardenHelm, "itemWardenHelm");
        GameRegistry.registerItem(ConfigLibrary.itemWardenChest, "itemWardenChest");
        GameRegistry.registerItem(ConfigLibrary.itemWardenLegs, "itemWardenLegs");
        GameRegistry.registerItem(ConfigLibrary.itemWardenBoots, "itemWardenBoots");
      
        GameRegistry.registerItem(ConfigLibrary.itemWardenclothChest, "itemWardenclothChest");
        GameRegistry.registerItem(ConfigLibrary.itemWardenclothLegs, "itemWardenclothLegs");
        GameRegistry.registerItem(ConfigLibrary.itemWardenclothBoots, "itemWardenclothBoots");
      
        GameRegistry.registerItem(ConfigLibrary.itemLoveRing, "itemLoveRing");
        GameRegistry.registerItem(ConfigLibrary.itemWaslieHammer, "itemWaslieHammer");
        GameRegistry.registerItem(ConfigLibrary.itemFocusIllumination, "itemFocusIllumination");
        GameRegistry.registerItem(ConfigLibrary.itemFocusPurity, "itemFocusPurity");
      
        GameRegistry.registerItem((Item)ConfigLibrary.itemExcubituraSeeds, "itemExcubituraSeeds");
      
        GameRegistry.registerItem(ConfigLibrary.itemWandCore, "itemWandCore");
    }
  
    private static void initializeThaumicBasesIntegration() {
        ConfigLibrary.armorMaterialFortressThauminite    = EnumHelper.addArmorMaterial("FORTRESS_THAUMINITE", 43, new int[] { 4, 9, 7, 4 }, 17);
        ConfigLibrary.itemThauminiteFortressHelm        = new ItemThauminiteFortressArmor(0, "itemThauminiteFortressHelm");
        ConfigLibrary.itemThauminiteFortressChest        = new ItemThauminiteFortressArmor(1, "itemThauminiteFortressChest");
        ConfigLibrary.itemThauminiteFortressLegs        = new ItemThauminiteFortressArmor(2, "itemThauminiteFortressLegs");
    }
  
    private static void registerThaumicBasesIntegration() {
        GameRegistry.registerItem(ConfigLibrary.itemThauminiteFortressHelm, "itemThauminiteFortressHelm");
        GameRegistry.registerItem(ConfigLibrary.itemThauminiteFortressChest, "itemThauminiteFortressChest");
        GameRegistry.registerItem(ConfigLibrary.itemThauminiteFortressLegs, "itemThauminiteFortressLegs");
    }
  
}
Главный класс:
ThaumicRevelations:
package thaumrev;

import cpw.mods.fml.client.event.ConfigChangedEvent;
import cpw.mods.fml.common.IWorldGenerator;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.SidedProxy;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.network.FMLEventChannel;
import cpw.mods.fml.common.registry.GameRegistry;
import java.io.File;
import net.minecraft.creativetab.CreativeTabs;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import thaumrev.api.wardenic.WardenicChargeEvents;
import thaumrev.api.wardenic.WardenicUpgrades;
import thaumrev.client.gui.GuiHandler;
import thaumrev.common.CommonProxy;
import thaumrev.config.Config;
import thaumrev.config.ConfigAspects;
import thaumrev.config.ConfigBlocks;
import thaumrev.config.ConfigIntegrations;
import thaumrev.config.ConfigItems;
import thaumrev.config.ConfigLibrary;
import thaumrev.config.ConfigRecipes;
import thaumrev.config.ConfigResearches;
import thaumrev.lib.CreativeTabThaumRev;
import thaumrev.lib.utils.MobDropsHandler;
import thaumrev.lib.world.ThaumRevWorldGenerator;

@Mod(modid = ThaumicRevelations.MODID, version = "1.2.2.1", useMetadata = true)
public final class ThaumicRevelations {
  
    public static final String MODID = "thaumrev", networkChannelName = MODID;
    public static final Logger log = LogManager.getLogger("Thaumic Revelations");
    public File modDir;
    public static FMLEventChannel channel;
    public static final int PACKET_TYPE_AMULET_USE = 1, PACKET_TYPE_S2C_TEST = 1;
 
    @Instance(MODID)
    public static ThaumicRevelations instance;
  
    @SidedProxy(serverSide = "thaumrev.common.CommonProxy", clientSide = "thaumrev.client.ClientProxy")
    public static CommonProxy proxy;
  
    @EventHandler
    public void preInit(FMLPreInitializationEvent event) {
        modDir = event.getModConfigurationDirectory();
        try { Config.init(event.getSuggestedConfigurationFile()); }
        catch (Exception var8) { log.error("Thaumic Revelations had a problem loading its configuration"); }
        finally { if (Config.config != null) Config.save(); }
        Config.save();
        proxy.preInit();
    }
  
    @EventHandler
    public void init(FMLInitializationEvent event) {
        proxy.init();
    }
  
    @EventHandler
    public void postInit(FMLPostInitializationEvent event) {
        proxy.postInit();
    }
  
    @SubscribeEvent
    public void onConfigChanged(ConfigChangedEvent.OnConfigChangedEvent event) {
        if (event.modID.equals(MODID)) Config.sync();
        if (Config.config != null && Config.config.hasChanged()) Config.save();
    }
  
}

И вот чего я не могу понять... Регистрация предметов происходит в CommonProxy, то есть на обеих сторонах - верно? Но у айтема есть поля и методы, которые должны работать только на клиенте. Такие поля помечаются аннотацией @SideOnly(Side.CLIENT) и, как я читал всё на этом же форуме, сервер такие куски кода не читает (правда или миф?). Так по какой тогда причине сервер пытается обращаться к этим полям, если поля используются так же только в методах с пометкой @SideOnly(Side.CLIENT)?
То есть сервер обрабатывает методы с @SideOnly(Side.CLIENT)? Или есть какие-то куски кода у мода, где идет обращение к этим полям от классов, зарегистрированных в CommonProxy без аннотации @SideOnly(Side.CLIENT), но в иерархии краша они не прослеживаются в данном случае?
Заранее спасибо!
 
Последнее редактирование:
Решение
СайдОнли не вырезает инициализацию переменных:
public IIcon[] icons = new IIcon[3];
Нельзя так писать. Лучше это как-то иначе, обычно инициализация иконок делаются в registerIcons методе.
428
41
108
Так по какой тогда причине сервер пытается обращаться к этим полям, если поля используются так же только в методах с пометкой @SideOnly(Side.CLIENT)?
Это работает примерно так ,,Если оно помечено @SideOnly(Side.CLIENT),, то этот кусок кода будет тупо вырезан
Однако, вызовы ,,уже вырезанного,, кода по прежнему останутся.

Тебе нужно вручную гарантировать, что обращений к ,,вырезанному коду,, не будет.
Например используй прокси для этого

Еще есть такой вариант - создаешь абстрактный класс, в нем определяешь метод
Наследуешь его, переопределяешь этот метод и помечаешь как клиентский
На сервере будет вызван родительский, а на клиенте - дочерний
 
192
2
9
Это работает примерно так ,,Если оно помечено @SideOnly(Side.CLIENT),, то этот кусок кода будет тупо вырезан
Однако, вызовы ,,уже вырезанного,, кода по прежнему останутся.

Тебе нужно вручную гарантировать, что обращений к ,,вырезанному коду,, не будет.
Например используй прокси для этого

Еще есть такой вариант - создаешь абстрактный класс, в нем определяешь метод
Наследуешь его, переопределяешь этот метод и помечаешь как клиентский
На сервере будет вызван родительский, а на клиенте - дочерний
Ну я не зря привел классы, которые засветились в краше)
Сервер не видит поля, которые помечены @SideOnly(Side.CLIENT) и это логично. Но вопрос: почему он пытается найти эти поля, если обращение к этим полям так же происходит только из методов с @SideOnly(Side.CLIENT) аннотацией? Это то меня и волнует)

Если я пометил всю цепочку вызовов до поля, как @SideOnly(Side.CLIENT), то сервер вообще в эту сторону смотреть не должен и обязан корректно работать, разве я ошибаюсь?

По крайней мере в коде я ни где не нашел обращений ни к полям с @SideOnly(Side.CLIENT), ни к методам, обращающимся к этим самым вырезанным с сервера полям.
 
428
41
108
Еще раз внимательно посмотри стек вызовов, того что ты скинул

Код:
at thaumrev.item.armor.ItemThauminiteFortressArmor.<init>(ItemThauminiteFortressArmor.java:33)
at thaumrev.config.ConfigItems.initializeThaumicBasesIntegration(ConfigItems.java:118)
at thaumrev.config.ConfigItems.init(ConfigItems.java:30)
at thaumrev.common.CommonProxy.preInit(CommonProxy.java:28)

Тут я в упор не увидел ни 1-го @SideOnly(Side.CLIENT)
 
1,370
112
241
Тут я в упор не увидел ни 1-го
Указание на 33 строку:
@SideOnly(Side.CLIENT)
public IIcon[] icons = new IIcon[3];

Проблема, как ты и сказал, как и сказал автор, в том, что сервер пытается обратиться к полю, которого у него нет.
Но где у него обращение со стороны сервера к этому полю - хз. Я тоже ничего не заметил подозрительного.
Мб проблемы с коллизией имён переменных (бред конечно, но всякое бывает)? Пусть попробует сменить имя с icons на какой-нибудь armorIcons.
 
192
2
9
Тут я в упор не увидел ни 1-го @SideOnly(Side.CLIENT)
Ну вот в том и проблема. Я видимо не совсем понимаю как строится эта логика.
Я вижу, что ругается на ItemThauminiteFortressArmor.java:33, где находится код:
Code:
    @SideOnly(Side.CLIENT)
    public IIcon[] icons = new IIcon[3];

Сервер не может найти данное поле, а значит к этому полю есть где-то в коде обращение со стороны сервера, верно ведь? И куда копать?
Единственное место, где к этому полю есть обращение, это:
Code:
    @SideOnly(Side.CLIENT)
    public void registerIcons(IIconRegister register) {
        this.icons[0] = register.registerIcon("thaumrev:armor/thauminitefortresshelm");
        this.icons[1] = register.registerIcon("thaumrev:armor/thauminitefortresschest");
        this.icons[2] = register.registerIcon("thaumrev:armor/thauminitefortresslegs");
    }
 
    @SideOnly(Side.CLIENT)
    public IIcon getIconFromDamage(int par1) { return this.icons[this.armorType]; }

Но и эти два метода, которые обращаются к полю icons, помечены как @SideOnly(Side.CLIENT), а значит сервер физически не может через эти методы обратиться к искомому полю, так как и этих методов у сервера нет. Так в какой момент и по какой причине сервер ищет указанное поле? Как мне такие вещи ловить вручную? Нет ведь ничего невозможного, тут дело знаний и опыта, которых у меня не достаточно, но достаточно у форумчан, чтобы объяснить.
 
192
2
9
Указание на 33 строку:


Проблема, как ты и сказал, как и сказал автор, в том, что сервер пытается обратиться к полю, которого у него нет.
Но где у него обращение со стороны сервера к этому полю - хз. Я тоже ничего не заметил подозрительного.
Мб проблемы с коллизией имён переменных (бред конечно, но всякое бывает)? Пусть попробует сменить имя с icons на какой-нибудь armorIcons.
Значит я понимаю логику прокси верно и нужно лишь выяснить, с какого места сервер обращается к этому полю. Следовательно, аннотация @SideOnly(Side.CLIENT) действительно исключает код со стороны сервера, даже если регистрация этого кода лежит в CommonProxy. Правильно понимаю?
 
428
41
108
Указание на 33 строку:
Действительно ...
Из этого мне почему-то напрашивается вывод, что new IIcon[3]; все же был вызван, и при попытке присвоить значение в поле - вылетела ошибка.
Попробуй сделать нечто такое

Java:
    @SideOnly(Side.CLIENT)
    public IIcon[] icons;

    //клиентский конструктор, полностью эквивалентно по логике и порядку действий
    //исходному конструктору
    public ItemThauminiteFortressArmor(int type, String name) {
        super(ConfigLibrary.armorMaterialFortressThauminite, 4, type);
        icons = new IIcon[3];
        this.setUnlocalizedName(name);
        this.setCreativeTab(ConfigLibrary.tabThaumRev);
    }
    //серверный
    public ItemThauminiteFortressArmor(String name, int type) {
        super(ConfigLibrary.armorMaterialFortressThauminite, 4, type);
        this.setUnlocalizedName(name);
        this.setCreativeTab(ConfigLibrary.tabThaumRev);
    }
 
Последнее редактирование:

tox1cozZ

aka Agravaine
8,455
598
2,892
СайдОнли не вырезает инициализацию переменных:
public IIcon[] icons = new IIcon[3];
Нельзя так писать. Лучше это как-то иначе, обычно инициализация иконок делаются в registerIcons методе.
 
192
2
9
СайдОнли не вырезает инициализацию переменных:
public IIcon[] icons = new IIcon[3];
Нельзя так писать. Лучше это как-то иначе, обычно инициализация иконок делаются в registerIcons методе.
Ну это код не мой, сейчас за синтаксис не болтаю)

А вот по поводу того, что инициализация переменных аннотацией @SideOnly(Side.CLIENT) не вырезается, я уже начал подозревать, ибо если первый сайд убрать, то сервер выдает уже краш, в котором не может найти ModelBiped класс, что указывает на попытку проинициализировать переменную, не смотря на её скрытие аннотацией @SideOnly(Side.CLIENT).

И почему мне постоянно приходится отвечать на предпоследний коммент темы и редактировать его под последний? Кнопки "Ответить" на последнем комменте нет. Баг форума или я чего-то не понимаю?)

Пока придумал такое решение, сохранив (вроде) логику разраба, сработало. Но насколько оно корректно?)
ItemThauminiteFortressArmor:
    private IIcon[] icons;
    private Object[] models;

    @SideOnly(Side.CLIENT)
    public void registerIcons(IIconRegister register) {
        if (icons == null) icons = new IIcon[3];
        icons[0] = register.registerIcon("thaumrev:armor/thauminitefortresshelm");
        icons[1] = register.registerIcon("thaumrev:armor/thauminitefortresschest");
        icons[2] = register.registerIcon("thaumrev:armor/thauminitefortresslegs");
    }
   
    @SideOnly(Side.CLIENT)
    public IIcon getIconFromDamage(int par1) { return icons[this.armorType]; }
   
    @SideOnly(Side.CLIENT)
    public ModelBiped getArmorModel(EntityLivingBase entity, ItemStack armor, int armorSlot) {
        if (models == null) models = new Object[3];
        int type = ((ItemArmor)armor.getItem()).armorType;
       
        if (models[1] == null) models[1] = new ModelThauminiteFortressArmor(1.0F);
        if (models[2] == null) models[2] = new ModelThauminiteFortressArmor(0.5F);
        models[0] = type != 1 && type != 3 ? models[2] : models[1];
       
        if (models[0] != null) {
            ModelBiped model = (ModelBiped)models[0];
            model.bipedHead.showModel = (armorSlot == 0);
            model.bipedHeadwear.showModel = (armorSlot == 0);
            model.bipedBody.showModel = (armorSlot == 1 || armorSlot == 2);
            model.bipedRightArm.showModel = (armorSlot == 1);
            model.bipedLeftArm.showModel = (armorSlot == 1);
            model.bipedRightLeg.showModel = (armorSlot == 2);
            model.bipedLeftLeg.showModel = (armorSlot == 2);
            model.isSneak = entity.isSneaking();
            model.isRiding = entity.isRiding();
            model.isChild = entity.isChild();
            model.aimedBow = false;
            model.heldItemRight = (entity.getHeldItem() != null) ? 1 : 0;
            if (entity instanceof EntityPlayer && ((EntityPlayer)entity).getItemInUseDuration() > 0) {
                EnumAction ea = ((EntityPlayer)entity).getItemInUse().getItemUseAction();
                if (ea == EnumAction.block) model.heldItemRight = 3;
                else if (ea == EnumAction.bow) model.aimedBow = true;
            }
            models[0] = model;
        }
       
        return (ModelBiped)models[0];
    }
 
Последнее редактирование:
1,074
72
372
192
2
9
Только не нужную проверку на null убери, любое поле изначально будет null.
Сделал, потому что есть такой кусочек кода у автора:
ItemThauminiteFortressArmor:
        if (models[1] == null) models[1] = new ModelThauminiteFortressArmor(1.0F);
        if (models[2] == null) models[2] = new ModelThauminiteFortressArmor(0.5F);
        models[0] = type != 1 && type != 3 ? models[2] : models[1];
Подумал, что если он проверяет значения массива на null, то по его логике в этом массиве могут быть уже какие-то значения, иначе можно было бы всегда без проверки делать присвоение в примере выше и всё.
 

tox1cozZ

aka Agravaine
8,455
598
2,892
И почему мне постоянно приходится отвечать на предпоследний коммент темы и редактировать его под последний? Кнопки "Ответить" на последнем комменте нет. Баг форума или я чего-то не понимаю?)
Чтобы не цитировали полностью последний ответ, и так понятно что ты на него отвечаешь.
 
Сверху