- 1,159
- 38
- 544
Добрый день, товарищи. У меня есть IEEP с кастомными инвентарями и я хочу сделать постоянную синхронизацию моих инвентарей с клиентом на уровне контейнера. Т.е. я хочу создавать один объект класса Container на клиенте и сервере и в момент открытия GUI просто использовать их. Ванильный код EntityPlayer так и делает со своим inventoryContainer - он никогда не пересоздает его и держит синхронизацию вызывая в onUpdate Container#detectAndSendChanges().
В своем IEEP я создал ссылку на Container, которую буду использовать при открытии GUI
Я заранее извиняюсь за говнокод - он в процессе рефакторинга.
При входе в мир выдает краш-лог (приложил ниже). Я попытался пройти отладчиком, но вообще не понимаю что происходит. Вот на этой строе все нормально:
Но как только я делаю step into в конструктор EntityPlayerMP, то получается это
Как так получается? Вообще хз. Собстна, вот 2 вопроса:
В своем IEEP я создал ссылку на Container, которую буду использовать при открытии GUI
Код:
package rsstats.inventory.container;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.Slot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemArmor;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IIcon;
import rsstats.common.RSStats;
import rsstats.data.ExtendedPlayer;
import rsstats.inventory.SkillsInventory;
import rsstats.inventory.StatsInventory;
import rsstats.inventory.WearableInventory;
import rsstats.inventory.slots.SkillSlot;
import rsstats.inventory.slots.StatSlot;
import rsstats.items.MiscItems;
import rsstats.items.OtherItems;
import rsstats.items.SkillItem;
import rsstats.items.StatItem;
import rsstats.items.perk.PerkItem;
import rsstats.utils.Utils;
import ru.rarescrap.tabinventory.TabContainer;
import ru.rarescrap.tabinventory.TabHostInventory;
import ru.rarescrap.tabinventory.TabInventory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
*
* @author rares
*/
public class MainContainer extends TabContainer {
private final ExtendedPlayer player;
// private final InventoryPlayer inventoryPlayer;
// private final StatsInventory statsInventory;
// private final WearableInventory wearableInventory;
// private final SkillsInventory skillsInventory;
//
// private final TabHostInventory otherTabsHost;
// private final TabInventory otherTabsInventory;
/** True, если игрок начал прокачивать статы, перейдя тем самым в режим редактирования */
public boolean isEditMode = false;
/** Хранит в себе прокачку игрока, которая была до того как он начал раскидывать очки прокачки.
* Используется для отката изменений. */
/* Может показаться, что ключ уже хранит свой itemDamage, который является уровнем статы, и можно просто
* реализовать хранение через ArrayList, но не нужно забывать, что itemDamage в ключах - может быть измен
* игроком, что делает хранилище ArrayList, т.к. сохраненные уровни стат будут утеряны */
private Map<ItemStack, Integer> savedBild = new HashMap<ItemStack, Integer>(); // TODO: Заменить пару на String-Integer
/** Хранит историю трат при прокачке в формате стак->массив. Каждая ячейка массива соответствует цене,
* которую заплатил игрок для поднятия уровня стат на 1. */
protected Map<ItemStack, ArrayList<Integer>> upgradeHistory;
/** Количество очков прокачки, которые следует вернуть игроку, если тот решил отменить прокачку */
private int wastedPoints;
public MainContainer(ExtendedPlayer player/*, InventoryPlayer inventoryPlayer, StatsInventory statsInventory, SkillsInventory skillsInventory, WearableInventory wearableInventory, TabHostInventory otherTabsHost, TabInventory otherTabsInventory*/) {
this.player = player;
// this.inventoryPlayer = inventoryPlayer;
// this.statsInventory = statsInventory;
// this.skillsInventory = skillsInventory;
// this.wearableInventory = wearableInventory;
//
// this.otherTabsHost = otherTabsHost;
// this.otherTabsInventory = otherTabsInventory;
// Добавляем вкладочный инвентарь к контейнеру
tabInventories.put(player.skillsInventory.getInventoryName(), player.skillsInventory);
tabInventories.put(player.otherTabsInventory.getInventoryName(), player.otherTabsInventory);
// Добавляем его к движку синхронизации (СЕРВЕР->КЛИЕНТ)
if (player.isServerSide()/*!player.worldObj.isRemote*/) {
getSync().addSync(player.skillsInventory);
getSync().addSync(player.otherTabsInventory);
}
addSlots();
}
private void addSlots() {
/*if (inventoryPlayer != null)
for (int y = 0; y < 3; ++y) {
for (int x = 0; x < 9; ++x) {
this.addSlotToContainer(new Slot(inventoryPlayer, x + y * 9 + 9, 8 + x * 18, 84 + y * 18));
}
}*/
// Расставляем слоты на панели руки
for (int i = 0; i < 9; i++) {
this.addSlotToContainer(new Slot(player.getEntityPlayer().inventory, i, (i*18 -3) +8, 188));
}
// Расставляем слоты на панели статов
for (int i = 0, slotIndex = 0; i < player.statsInventory.getSizeInventory(); ++i, slotIndex++) {
this.addSlotToContainer(new StatSlot(player.statsInventory, i, (i*18 +167) +8, /*-24*/8));
//this.addSlotToContainer(new StatSlot(statsInventory, slotIndex, i*9, 0));
}
// Расставляем слоты на панели скиллов
for (int y = 0; y < 3; ++y) {
for (int x = 0; x < 9; ++x) {
this.addSlotToContainer(new SkillSlot(player.skillsInventory, x + y * 9 /*+ 9*/, (x*18 +167) +8, (y * 18) + 26));
}
}
// Расставляем слоты для брони
for (int i = 0; i < 4; ++i) {
final int k = i;
this.addSlotToContainer(new Slot(player.getEntityPlayer().inventory, player.getEntityPlayer().inventory.getSizeInventory() - 1 - i, (i * 18 + 51) + 8, 8) {
@Override
public int getSlotStackLimit() {
return 1;
}
@Override
public boolean isItemValid(ItemStack par1ItemStack) {
if (par1ItemStack == null) return false;
return par1ItemStack.getItem().isValidArmor(par1ItemStack, k, player.getEntityPlayer());
}
@SideOnly(Side.CLIENT)
public IIcon getBackgroundIconIndex() {
return ItemArmor.func_94602_b(k);
}
@Override
public void onPickupFromSlot(EntityPlayer p_82870_1_, ItemStack itemStack) {
super.onPickupFromSlot(p_82870_1_, itemStack);
player.modifierManager.removeModifiersFrom(itemStack); // Удаляем модификаторы от прошлой брони
}
/**
* Helper method to put a stack in the slot.
*
* @param itemStack
*/
@Override
public void putStack(ItemStack itemStack) {
// TODO: Баг с рассинхронизацией модификаторов на клиенте и серве был впервые замечен тут
// if (!player.worldObj.isRemote) - не работает на клиенте
if (!player.isServerSide()/*!*//*player.worldObj.isRemote*/) {
// Если в слоте уже был предмет - удаляем его модификаторы
if (this.getStack() != null) {
player.modifierManager.removeModifiersFrom(this.getStack());
}
// Извлекаем и сохраняем модификаторы из стака, который кладется в слот
player.modifierManager.addModifiersFrom(itemStack);
}
super.putStack(itemStack);
// Дебажная инфа
/*if (player.worldObj.isRemote) {
System.out.println("Клиентские модификаторы:");
} else {
System.out.println("Серверные модификаторы:");
}
for (ArrayList<RollModifier> modifiers : ExtendedPlayer.get(player).getModifierMap().values()) {
for (RollModifier modifier : modifiers) {
System.out.println(modifier);
}
}*/
}
});
}
// Расставляем слоты на панели носимых вещей
for (int y = 0; y < 4; ++y) {
for (int x = 0; x < 4; ++x) {
this.addSlotToContainer(new Slot(player.wearableInventory, x + y * 4 /*+ 9*/, (x*18 + 51) +8, (y * 18) + 26) {
// Сюда нельзя помещать броню
@Override
public boolean isItemValid(ItemStack p_75214_1_) {
if (p_75214_1_.getItem() instanceof ItemArmor)
return false;
else
return super.isItemValid(p_75214_1_);
}
});
}
}
// Расставляем слоты на панели вкладок
for (int i = 0, slotIndex = 0; i < player.otherTabsHost.getSizeInventory(); ++i, slotIndex++) {
this.addSlotToContainer(new Slot(player.otherTabsHost, i, (i*18 +167) +8, 116) {
@Override
public boolean isItemValid(ItemStack p_75214_1_) {
return player.otherTabsHost.isUseableByPlayer(player.getEntityPlayer());
}
});
}
// Расставляем слоты, которе будут хранить содержимое вкладок
for (int y = 0; y < 4; ++y) {
for (int x = 0; x < 9; ++x) {
this.addSlotToContainer(new Slot(player.otherTabsInventory, x + y * 9 /*+ 9*/, (x*18 +167) +8, (y * 18) + 134) {
@Override
public boolean isItemValid(ItemStack itemStack) {
// Получаем имя вкладки с перками
String perkTabName = OtherItems.perksTabItem.getUnlocalizedName();
// Проверяем в какую вкладку игрок хочет поместить стак (пока проверяем только очет ли он поместить ее в вкладку перков)
if (itemStack != null && itemStack.getItem() instanceof PerkItem && player.otherTabsInventory.getCurrentTab().equals(perkTabName)) {
// Проверяем, может ли игрок использовать целевой инвентарь и удовлетворяет ли игрок требованиям перка
PerkItem perkItem = (PerkItem) itemStack.getItem();
return player.otherTabsHost.isUseableByPlayer(player.getEntityPlayer()) && perkItem.isSuitableFor(player);
}
return false;
/* Небользая заметка: раз этот метод исполняется на клиенте, то результат работы этого метода
* должен бть одинаков на обоих сторонах. Возможно, это первый звоночек к тому, что придется
* держать постоянную (т.е. не в рамках срока жизни одного контейнера) ВСЕЙ инфы ExtendedPlayer'а
* с клиентом. Сделать это можно так же как ванильный код поддерживает синхронизацию
* EntityPlayer#inventoryContainer. */
}
});
}
}
}
/**
* This should always return true, since custom inventory can be accessed from anywhere
* @param player TODO
* @return TODO
*/
@Override
public boolean canInteractWith(EntityPlayer player) {
return true;
}
/**
* Called when a entityPlayer shift-clicks on a slot. You must override this or you will crash when someone does that.
* Basically the same as every other container I make, since I define the same constant indices for all of them
* @param player TODO
* @param par2 TODO
* @return TODO
*/
@Override
public ItemStack transferStackInSlot(EntityPlayer player, int par2) {
ItemStack itemstack = null;
Slot slot = (Slot) this.inventorySlots.get(par2);
return itemstack;
}
// TODO: баг при стаках очков прокачки 64 и 1
// TODO: Стоимость прокачки статы должна быть 2 очка, а не 1
/**
* Увеличивает переданную стату на 1, если в {@link EntityPlayer#inventory} в ExtendedPlayer есть хотя бы один {@link rsstats.items.ExpItem}.
* Так же уменьшает ExpItem на 1.
* @param statStack Стак со статой, которую необходимо прокачать
* @throws IllegalAccessException Если в statStack нет {@link StatItem}'а
*/
public void statUp(ItemStack statStack) {
if ( !(statStack.getItem() instanceof StatItem) ) {
throw new IllegalArgumentException("ItemStack argument must contain an StatItem.");
}
StatItem statItem = (StatItem) statStack.getItem();
// Находим стак с очками прокачки // TODO: Удаляем комментарии
// ItemStack expStack = null;
// int expStackPos = -1; // Если -1 - значит стак с очками прокачки нельзя создать + его и не было
// for (int i = 0; i < inventoryPlayer.mainInventory.length; i++) {
// ItemStack itemStack = inventoryPlayer.mainInventory[i];
// if (itemStack != null && "item.ExpItem".equals(itemStack.getUnlocalizedName())) {
// expStack = itemStack;
// expStackPos = i;
// break;
// }
// }
// if (expStack == null) {
// return; // Если очков прокачки нет - выходим
// }
// Выявляет количество подтипов (уровней) статы
// List subitems = new ArrayList();
// statItem.getSubItems(statItem, CreativeTabs.tabMaterials, subitems);
int subitems = statItem.getMaxDamage();
int statItemDamage = statItem.getDamage(statStack);
// if (statItemDamage != subitems.size() - 1) {
// int price = 1; // Цена прокачки
// if (statItem instanceof SkillItem) {
// ItemStack parentStatStack = statsInventory.getStat(((SkillItem) statItem).parentStat.getUnlocalizedName());
// int parentStatDamage = ((SkillItem) statItem).parentStat.getDamage(parentStatStack);
// if (statItemDamage > parentStatDamage)
// price = 2;
// }
//
// // Отнимает очко прокачки ...
// if (expStack.stackSize >= price) {
// wastedPoints += price; // Сохраняем количество очков, что потратил пользователь
// if (expStack.stackSize == price) {
// inventoryPlayer.mainInventory[expStackPos] = null; // Убираем стак
// } else {
// expStack.stackSize -= price; // Уменьшаем стак
// }
// } else {
// return;
// }
// и увеличиваем стату ...
statStack.setItemDamage(statItemDamage < subitems ? statItemDamage + 1 : subitems);
// } else { // Стата уже прокачана до предела - выходим
// return;
// }
}
// TODO: Рефакторить. См addItemStackToInventory
public void statDown(ItemStack statStack) {
if ( !(statStack.getItem() instanceof StatItem) ) {
throw new IllegalArgumentException("ItemStack argument must contain an StatItem.");
}
StatItem statItem = (StatItem) statStack.getItem();
int statItemDamage = statItem.getDamage(statStack);
// boolean isExpStackCreated = false; // TODO: Удаляем комментарии
/* В случае, если игрок в ходе одной сессии прокачки захотел обнулить
* навыки, прокачанный в прошлой сесии - останавливаем его */
// ItemStack s = statStack;
// for (ItemStack keyStack : savedBild.keySet()) { // TODO: Нужно найти и использовать уже имеющийся поиск. Задолбало его писать каждый раз
// if (keyStack.getUnlocalizedName().equals(statStack.getUnlocalizedName())) {
// s = keyStack;
// break;
// }
// }
// if (statItemDamage <= savedBild.get(s)) {
// return;
// }
// Находим стак с очками прокачки
// ItemStack expStack = null;
// for (ItemStack itemStack : inventoryPlayer.mainInventory) {
// if (itemStack != null && "item.ExpItem".equals(itemStack.getUnlocalizedName())) {
// expStack = itemStack;
// }
// }
// // Если очков прокачки нет или их стак забит - ищем свободное место в инветаре, куда их можно положить
// if (expStack == null || expStack.stackSize >= expStack.getMaxStackSize()) {
// int freeSpaceIndex = findFreeSpaceInInventory(inventoryPlayer);
// if (freeSpaceIndex != -1 && statItemDamage != 0) {
// expStack = new ItemStack(GameRegistry.findItem(RSStats.MODID, "ExpItem"));
// isExpStackCreated = true;
// inventoryPlayer.setInventorySlotContents(freeSpaceIndex, expStack);
// } else { // Если свободное место не было найдено - выходим
// return;
// }
// }
//
// // Выявляет количество подтипов (уровней) статы
// List subitems = new ArrayList();
// statItem.getSubItems(statItem, CreativeTabs.tabMaterials, subitems);
//
// if (statItemDamage > 0) {
// int reward; // Сколько очков прокачки вернется за отмену прокачки
// if (statItem instanceof SkillItem) {
// ItemStack parentStatStack = statsInventory.getStat(((SkillItem) statItem).parentStat.getUnlocalizedName());
// int parentStatDamage = ((SkillItem) statItem).parentStat.getDamage(parentStatStack);
// if (statItemDamage > parentStatDamage+1)
// reward = 2;
// else
// reward = 1;
// } else { // instanceof StatItem
// reward = 1;
// }
//
// // возвращаем игроку очки прокачки ...
// if (expStack.stackSize + reward <= expStack.getMaxStackSize()) {
// // Убираем очки из "возмещения", если тот сам (т.е. без диалога) отменил прокачку конкретного навыка или статы
// wastedPoints -= reward;
//
// if (isExpStackCreated) {
// expStack.stackSize = reward;
// } else {
// expStack.stackSize += reward;
// }
// } else {
// reward = (expStack.stackSize + reward) % expStack.getMaxStackSize();
// expStack.stackSize = expStack.getMaxStackSize();
//
// int freeSpaceIndex = findFreeSpaceInInventory(inventoryPlayer);
// if (freeSpaceIndex != -1) {
// expStack = new ItemStack(GameRegistry.findItem(RSStats.MODID, "ExpItem"));
// expStack.stackSize = reward;
// inventoryPlayer.setInventorySlotContents(freeSpaceIndex, expStack);
// } else {
// return;
// }
// }
// и уменьшаем стату ...
statStack.setItemDamage(statItemDamage > 0 ? statItemDamage-1 : 0);
// } else { // Стата уже спущена до минимального предела - выходим
// return;
// }
}
@Override
public void detectAndSendChanges() {
/* Т.к. по логике данного контейнера slotClick(...) может не работать на клиенте и сервере одинакого,
* то для поддержания работоспособности синхронизации нам нужно выставить isChangingQuantityOnly = false
* см. NetHandlerPlayServer#processClickWindow().
*
* Кейс: Если этого не сделать, то при прокачке статы на клиент не будет высылаться пакет об уменьшении
* количества очков прокачки. По логике данного контейнера, проверку на возможность прокачки навыка/статы
* осуществляет сервер. Именно поэтому slotClick(...) работает по разному на клиенте и сервере.
*/
// https://rarescrap.blogspot.com/2018/10/minecraft-1_18.html?zx=7ca4a4ed658beb3
if (player.getEntityPlayer() instanceof EntityPlayerMP)
((EntityPlayerMP) player.getEntityPlayer()).isChangingQuantityOnly = false;
super.detectAndSendChanges(); // Вызов на клиенте ни к чему не приведет, т.к. список crafters будет пустым
}
// TODO: Это выполняется и для клиента и для сервера. Разгранич код. Приводит ли такое поведение к рассинхронизации?
@Override
public ItemStack slotClick(int slotId, int clickedButton, int mode, EntityPlayer playerIn) {
// -999 - "переносимый" стак кликается за предеты контейнера (т.е. выбрасывается)
// -1 - игрок тыкает переносимым стаков в то место в контейнере, в котором нет слота
if (slotId == -999 || slotId == -1)
return super.slotClick(slotId, clickedButton, mode, playerIn);
Slot slot = getSlot(slotId);
Item itemInSlot;
if (slot.getStack() != null) {
itemInSlot = slot.getStack().getItem();
} else {
return super.slotClick(slotId, clickedButton, mode, playerIn);
}
if (clickedButton == 1 && itemInSlot instanceof StatItem) { // ПКМ
ItemStack temp = slot.getStack().copy();
processStatRightClick(slot, mode, playerIn);
/* Не следует возвращать прокачанный ItemStack, т.к. тогда
* NetHandlerPlayServer#processClickWindow() обнаружит что стак, по которому кликнул игрок
* не равен стаку в серверном инвентаре по такой же позиции. Это приведет к тому, что
* ВСЕ содержимое окна перешлется на клиент, что не очень эффективно. */
return temp;
}
if (clickedButton == 2 && itemInSlot instanceof StatItem) { // СКМ
ItemStack temp = slot.getStack().copy();
processStatMiddleClick(slot, mode, playerIn);
return temp;
}
if ((slot.inventory == player.statsInventory || slot.inventory == player.skillsInventory) && (itemInSlot instanceof SkillItem || itemInSlot instanceof StatItem)) {
ItemStack itemStack = getSlot(slotId).getStack();
// Защита от дублирующихся сообщений в чате + ролл посылается в клиента, где определен класс GuiScreen
if (playerIn.worldObj.isRemote) {
( (StatItem) itemStack.getItem() ).sendRollPacket(itemStack, playerIn, !GuiScreen.isCtrlKeyDown());
}
return slot.getStack();
}
// Поведение, если кликнут слот инвентаря otherTabsHost
if (slot.inventory == player.otherTabsHost) {
if (player.otherTabsHost.isUseableByPlayer(playerIn)) { // TODO: Не лучше ли использовать isItemValid из переопределенного слота?
return super.slotClick(slotId, clickedButton, mode, playerIn); // "Захватваем" стак
} else {
return null; // Ничего не делаем
}
}
// Поведение, если кликнут слот инвентаря otherTabsInventory
if (slot.inventory == player.otherTabsInventory) {
if (player.otherTabsInventory.isUseableByPlayer(playerIn)) {
return super.slotClick(slotId, clickedButton, mode, playerIn);
} else {
return null;
}
}
return super.slotClick(slotId, clickedButton, mode, playerIn);
}
// Пересчет при закрытии контейнера нужен, когда в диалоге нажимается "Отменить изменения"
@Override
public void onContainerClosed(EntityPlayer entityPlayer) {
// Пересчитываем параметры и синхронизируем их с клиентом
if (!entityPlayer.worldObj.isRemote) {
player.updateParams();
player.sync();
}
super.onContainerClosed(entityPlayer);
}
public SkillsInventory getSkillsInventory() {
return player.skillsInventory;
}
/**
* Сохраняет прокачку персонажа, дабыы иметь возможность ее восстановить
*/
public void saveBild() {
savedBild.clear();
for (ItemStack statStack : player.statsInventory.getStats()) {
if (statStack != null)
savedBild.put(statStack, statStack.getItemDamage());
}
for (ItemStack skillStack : player.skillsInventory.getSkills()) {
if (skillStack != null)
savedBild.put(skillStack, skillStack.getItemDamage());
}
}
/**
* Восстанавливает прокачку персонажа
*/
public void restoreBild() {
for (ItemStack bildStack : savedBild.keySet()) {
int lvl = savedBild.get(bildStack);
if (bildStack.getItem() instanceof SkillItem) {
for (ItemStack currentSkillStack : player.skillsInventory.getSkills()) {
if (currentSkillStack != null && currentSkillStack.getItem() == bildStack.getItem()) {
currentSkillStack.setItemDamage(lvl);
}
}
} else {
for (ItemStack currentStatStack : player.statsInventory.getStats()) {
if (currentStatStack != null && currentStatStack.getItem() == bildStack.getItem()) {
currentStatStack.setItemDamage(lvl);
}
}
}
}
/* addItemStackToInventory успешно работает с ситуацией, если вернутся больше чем 64 предмета.
* Нет нужды в своих проверках. */
ItemStack expStack = new ItemStack(GameRegistry.findItem(RSStats.MODID, "ExpItem"), wastedPoints);
this.player.getEntityPlayer().inventory.addItemStackToInventory(expStack);
}
/**
* Вычисляет стоимость прокачки статы или навыка на один пункт.
* @param statStack Стак со статой
* @return Стоимость прокачки статы или навыка на один пункт. Если достигнут предел, вовзращает -1
* @throws IllegalAccessException Если в statStack нет {@link StatItem}'а
*/
public int getUpgradePrice(ItemStack statStack) { // TODO: Unit-test this
if ( !(statStack.getItem() instanceof StatItem) ) {
throw new IllegalArgumentException("ItemStack argument must contain an StatItem.");
}
StatItem statItem = (StatItem) statStack.getItem();
// List subitems = new ArrayList(); // TODO: Удалить закоментированный код
// statStack.getItem().getSubItems(statItem, CreativeTabs.tabMaterials, subitems); // TODO: БАГ! Крашится на Dedicated сервере. Заменить костыль ниже на приемлимый аналог
int subtypes = statItem.getMaxDamage();
// if (statItem instanceof SkillItem) {
// subtypes = SkillItem.NUMBER_OF_LEVELS - 1;
// } else { // statItem instanceof StatItem
// subtypes = StatItem.NUMBER_OF_LEVELS - 1;
// }
int price = 1; // Цена прокачки по-умолчанию
int statItemDamage = statItem.getDamage(statStack);
if (statItemDamage != subtypes) {
if (statItem instanceof SkillItem) {
ItemStack parentStatStack = player.statsInventory.getStat(((SkillItem) statItem).parentStat.getUnlocalizedName());
int parentStatDamage = parentStatStack.getItemDamage(); //((SkillItem) statItem).parentStat.getDamage(parentStatStack);
if (statItemDamage > parentStatDamage)
price = 2;
} else { // instanceof StatItem ONLY
price = 2;
}
return price;
}
return -1; // TODO: Не самый удачный выбор возвращаемого числа. Может быть сменить на 0?
}
/**
* Вычисляет количество очков, которое получит игрок после даунгрейда статы или навыка на один пункт.
* @param statStack Стак со статой
* @return Возврат за даунгрейд статы или навыка на один пункт. Если достигнут предел, вовзращает -1
* @throws IllegalAccessException Если в statStack нет {@link StatItem}'а
*/
public int getDowngradeReward(ItemStack statStack) { // TODO: Unit-test this
if ( !(statStack.getItem() instanceof StatItem) ) {
throw new IllegalArgumentException("ItemStack argument must contain an StatItem.");
}
StatItem statItem = (StatItem) statStack.getItem();
int statItemDamage = statStack.getItemDamage();
// Выявляет количество подтипов (уровней) статы // TODO: Удалить закоментированный код
// List subitems = new ArrayList();
// statItem.getSubItems(statItem, CreativeTabs.tabMaterials, subitems); // TODO: Вклалдка не нужна
int subitem = statItem.getMaxDamage();
// int reward; // Сколько очков прокачки вернется за отмену прокачки
// if (statItemDamage > 0) {
// if (statItem instanceof SkillItem) {
// ItemStack parentStatStack = statsInventory.getStat(((SkillItem) statItem).parentStat.getUnlocalizedName());
// int parentStatDamage = ((SkillItem) statItem).parentStat.getDamage(parentStatStack);
// if (statItemDamage > parentStatDamage + 1)
// reward = 2;
// else
// reward = 1;
// } else { // instanceof StatItem ONLY
// reward = 2;
// }
// return reward;
// }
//return -1;
return getPriceFor(statStack, statStack.getItemDamage()/*-1*/);
}
/**
* Определяет, может ли игрок получить возврат очков прокачки при попытки понизить стату
* @param statStack Стак со статой
* @return True, если может, false - нет или если попытается сбросить стату, которую не прокачивал в рамках текущей сессии прокачки.
* @see #saveBild()
*/
public boolean canRefund(ItemStack statStack) { // TODO: Unit-test this
if ( !(statStack.getItem() instanceof StatItem) ) {
throw new IllegalArgumentException("ItemStack argument must contain an StatItem.");
}
int statItemDamage = statStack.getItemDamage();
ItemStack s = statStack;
for (ItemStack keyStack : savedBild.keySet()) { // TODO: Нужно найти и использовать уже имеющийся поиск. Задолбало его писать каждый раз
if (keyStack.getUnlocalizedName().equals(statStack.getUnlocalizedName())) {
s = keyStack;
break;
}
}
return statItemDamage > savedBild.get(s); // TODO: Проверка на null
}
/**
* Возвращает игроку указанное количество очков прокачи и отнимает из их {@link #wastedPoints}
* @param refund Очки прокачки, которые будут возвращены игроку
*/
public void doRefund(int refund) { // TODO: Unit-test this
ItemStack expStack = new ItemStack(MiscItems.expItem, refund);
this.player.getEntityPlayer().inventory.addItemStackToInventory(expStack);
wastedPoints -= refund;
}
/**
* Обрабатывает ПКМ по стате/навыку, т.е. намерение пользователя прокачать навык
* @param slot Слот, в котором лежит стата/навык
* @param mode Режим клик (см. {@link net.minecraft.client.gui.inventory.GuiContainer#handleMouseClick})
* @param playerIn Игрок, нажавший ПКМ
* @return Стак, по которому был сделан клик
*/
protected ItemStack processStatRightClick(Slot slot, int mode, EntityPlayer playerIn) {
// Если у игрока есть очки прокачки и он не в режиме редактирования ...
if (playerIn.inventory.hasItem(MiscItems.expItem) && !isEditMode) {
// ... тогда инициализируем режим прокачки и сохраняем текущий билд игрока
isEditMode = true;
if (!playerIn.worldObj.isRemote) {
wastedPoints = 0;
saveBild();
initUpgradeHistory();
} else {
return slot.getStack();
}
}
if (isEditMode && !playerIn.worldObj.isRemote) { // Игрок в режиме прокачки - пытается повысить стату/навык
int price = getUpgradePrice(slot.getStack());
if (price != -1 && Utils.removeItemStackFromInventory(player.getEntityPlayer().inventory, "item.ExpItem", price)) {
wastedPoints += price;
// Добавляем трату очков в историю
rememberPriceToNextLevel(slot.getStack(), price);
// Поднимаем стату
statUp(slot.getStack());
// Пересчитваем параметры на сервере и информируем клиент, чтобы он сделал то же самое
ExtendedPlayer extendedPlayer = ExtendedPlayer.get(playerIn);
extendedPlayer.updateParams();
extendedPlayer.sync();
}
}
return slot.getStack();
}
/**
* Обрабатывает СКМ по стате/навыку, т.е. намерение пользователя отменить прокачку навыка
* @param slot Слот, в котором лежит стата/навык
* @param mode Режим клика (см. {@link net.minecraft.client.gui.inventory.GuiContainer#handleMouseClick})
* @param playerIn Игрок, нажавший ЛКМ
* @return Стак, по которому был сделан клик
*/
protected ItemStack processStatMiddleClick(Slot slot, int mode, EntityPlayer playerIn) {
if (playerIn.worldObj.isRemote) // Расчет производится только на сервере
return slot.getStack();
int refund = getDowngradeReward(slot.getStack());
if (isEditMode && canRefund(slot.getStack()) && refund != -1) { // Игрок в режиме прокачки - пытается понизить стату/навык
statDown(slot.getStack());
doRefund(refund);
// Пересчитваем параметры на сервере и информируем клиент, чтобы он сделал то же самое
ExtendedPlayer extendedPlayer = ExtendedPlayer.get(playerIn);
extendedPlayer.updateParams();
extendedPlayer.sync();
}
return slot.getStack();
}
/**
* Инициализирует {@link #upgradeHistory} нулевой историей трат
*/
protected void initUpgradeHistory() { // TODO: Unit-test this
upgradeHistory = new HashMap<ItemStack, ArrayList<Integer>>();
// Инициализируем историю трат для статов
for (ItemStack itemStack : player.statsInventory.getStats()) {
if (itemStack == null) continue;
StatItem statItem = (StatItem) itemStack.getItem();
ArrayList<Integer> history = new ArrayList<Integer>(
Collections.nCopies(statItem.getMaxDamage()+1, 0)
);
upgradeHistory.put(itemStack, history);
}
// Инициализируем историю трат для скиллов
for (ItemStack itemStack : player.skillsInventory.getSkills()) {
if (itemStack == null) continue;
SkillItem skillItem = (SkillItem) itemStack.getItem();
ArrayList<Integer> history = new ArrayList<Integer>(
Collections.nCopies(skillItem.getMaxDamage()+1, 0)
);
upgradeHistory.put(itemStack, history);
}
}
/**
* Сохраняет стомость прокачки, которую заплатит игрок, чтобы увеличить стату/навык на следующий уровень
* @param itemStack Стак со статой/навыком
* @param price Цена прокачки на следующий уровень
*/
protected void rememberPriceToNextLevel(ItemStack itemStack, int price) { // TODO: Unit-test this
ArrayList<Integer> history = upgradeHistory.get(itemStack);
history.set(itemStack.getItemDamage()+1, price);
}
/**
* Возвращет цену, которую заплатил игрок, чтобы стата перешла на определенный уровень (lvl) с предыдущего
* @param itemStack Стак со статой/скиллом
* @param lvl Уровень, на который поднялась стата с предыдущего уровня
* @return Цена, которую заплатил игрок, чтобы стата перешла на определеннх уровень с предыдущего
*/
protected int getPriceFor(ItemStack itemStack, int lvl) { // TODO: Unit-test this
return upgradeHistory.get(itemStack).get(lvl);
}
}
Код:
package rsstats.data;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.util.StatCollector;
import net.minecraft.world.World;
import net.minecraftforge.common.IExtendedEntityProperties;
import net.minecraftforge.common.util.Constants;
import rsstats.common.CommonProxy;
import rsstats.common.RSStats;
import rsstats.common.network.PacketSyncPlayer;
import rsstats.i18n.IClientTranslatable;
import rsstats.inventory.SkillsInventory;
import rsstats.inventory.StatsInventory;
import rsstats.inventory.WearableInventory;
import rsstats.inventory.container.MainContainer;
import rsstats.items.OtherItems;
import rsstats.items.SkillItems;
import rsstats.items.StatItem;
import rsstats.items.StatItems;
import rsstats.items.perk.IModifierDependent;
import rsstats.items.perk.PerkItem;
import rsstats.utils.Utils;
import ru.rarescrap.tabinventory.TabHostInventory;
import ru.rarescrap.tabinventory.TabInventory;
/**
*
* @author rares
*/
public class ExtendedPlayer implements IExtendedEntityProperties {
public enum Rank implements IClientTranslatable {
NOVICE,
TEMPERED,
VETERAN,
HERO,
LEGEND;
public int toInt() {
return this.ordinal();
}
public static Rank fromInt(int lvl) {
return Rank.values()[lvl];
}
@Override
public String getTranslatedString() {
return StatCollector.translateToLocal("rank." + this.name().toLowerCase());
}
}
/**
* Ключи статичных параметров игрока
*/
public enum ParamKeys implements IModifierDependent { // TODO: Упростить get и set методы ExtendedPlayer'а, использая в качестве параметра константы из енума Rank
STEP,
PROTECTION,
PERSISTENCE,
CHARISMA
}
/** Каждый наследник {@link IExtendedEntityProperties} должен иметь индивидуальное имя */
private static final String EXTENDED_ENTITY_TAG = RSStats.MODID;
private final EntityPlayer entityPlayer;
/** Основной параметр игрока - Шаг */
public int step = 6;
/** Основной параметр игрока - Защита */
public int protection;
/** Основной параметр игрока - Стойкость */
public int persistence;
/** Основной параметр игрока - Харизма */
public int charisma = 0;
private int exp = 0;
private Rank rank;
private int tiredness = 0;
private int tirednessLimit = 25;
/** Инвентарь для статов */
public final StatsInventory statsInventory;
/** Инвентарь для скиллов */
public final SkillsInventory skillsInventory;
/** Инвентарь для носимых предметов */
public final WearableInventory wearableInventory;
/** Хост вкладок для {@link #otherTabsInventory} */
public TabHostInventory otherTabsHost;
/** Инвентарь с вкладками для прочей информации вроде перков, изъянов и т.д. */
public TabInventory otherTabsInventory;
/** Хранилище модификаторов броска игрока */
public ModifierManager modifierManager = new ModifierManager();
/*
Тут в виде полей можно хранить дополнительную информацию о Entity: мана,
золото, хп, переносимый вес, уровень радиации, репутацию и т.д. Т.е. все то,
что нельзя хранить в виде блоков
*/
public MainContainer container;
private ExtendedPlayer(EntityPlayer player) {
this.entityPlayer = player;
wearableInventory = new WearableInventory(this);
statsInventory = new StatsInventory("stats_inv", 9);
skillsInventory = new SkillsInventory("skills_inv", 36, entityPlayer, statsInventory);
otherTabsHost = new TabHostInventory("effects_host", 4);
otherTabsInventory = new TabInventory("effects", 36, entityPlayer, otherTabsHost);
container = new MainContainer(this);
}
/**
* Used to register these extended properties for the entityPlayer during EntityConstructing event
* This method is for convenience only; it will make your code look nicer
* @param player
*/
public static final void register(EntityPlayer player) {
player.registerExtendedProperties(ExtendedPlayer.EXTENDED_ENTITY_TAG, new ExtendedPlayer(player));
}
/**
* Returns ExtendedPlayer properties for entityPlayer
* This method is for convenience only; it will make your code look nicer
*/
public static final ExtendedPlayer get(EntityPlayer player) {
return (ExtendedPlayer) player.getExtendedProperties(EXTENDED_ENTITY_TAG); // TODO: Добавить Exception, если null
}
public boolean isServerSide() {
return this.entityPlayer instanceof EntityPlayerMP;
}
@Override
public void saveNBTData(NBTTagCompound properties) {
properties.setInteger("exp", exp);
properties.setInteger("rank", rank.toInt());
properties.setInteger("tiredness", tiredness);
properties.setInteger("tirednessLimit", tirednessLimit);
this.statsInventory.writeToNBT(properties);
this.skillsInventory.writeToNBT(properties);
this.wearableInventory.writeToNBT(properties);
this.otherTabsHost.writeToNBT(properties);
this.otherTabsInventory.writeToNBT(properties);
}
// TODO: Почему-то когда открывается GUI - Отображается категорий скиллов ловкости
@Override
public void loadNBTData(NBTTagCompound properties) {
exp = properties.getInteger("exp");
rank = Rank.fromInt(properties.getInteger("rank"));
tiredness = properties.getInteger("tiredness");
tirednessLimit = properties.getInteger("tirednessLimit");
/* Нет нужды очищать инвентари перед применением сохранения, т.к.
* readFromNBT() перезаписывает ВСЕ слоты инвентаря */
this.statsInventory.readFromNBT(properties);
this.skillsInventory.readFromNBT(properties);
this.wearableInventory.readFromNBT(properties);
this.otherTabsHost.readFromNBT(properties);
this.otherTabsInventory.readFromNBT(properties);
/* Т.к. ванильный инвентарь переписывать нежелательно, начальная инициализация модификатором от брони
* реализована здесь */
NBTTagList playerInventory = properties.getTagList("Inventory", Constants.NBT.TAG_COMPOUND);
for (int i = 0; i < playerInventory.tagCount(); i++) {
NBTTagCompound itemNBT = playerInventory.getCompoundTagAt(i);
int slotID = itemNBT.getInteger("Slot");
if (slotID >= 100 && slotID <= 103) {
modifierManager.addModifiersFrom( ItemStack.loadItemStackFromNBT(itemNBT) );
}
}
// Выгружаем модификаторы перков из NBT-сохранения
TabInventory.Tab a = otherTabsInventory.items.get(OtherItems.perksTabItem.getUnlocalizedName());
for (ItemStack stack : a.stacks) {
if (stack != null) {
PerkItem perkItem = (PerkItem) stack.getItem();
modifierManager.addModifiers(perkItem.getModifiers());
}
}
updateParams();
}
/**
* Used to initialize the extended properties with the entity that this is attached to, as well
* as the world object.
* Called automatically if you register with the EntityConstructing event.
* May be called multiple times if the extended properties is moved over to a new entity.
* Such as when a player switches dimension {Minecraft re-creates the player entity}
* @param entity The entity that this extended properties is attached to
* @param world The world in which the entity exists
*/
@Override
public void init(Entity entity, World world) {
/* Крайне интересный хак. Дело в том, что init() используется для инициализации самого ExtendedPlayer'а,
* а не Compound'а, который передается в loadNBTData(). TODO: Я все еще не разобрался как связана сущность, создаваемая тут и compound в loadNBTData
* Я проивожу "инициализацию нового игрока" тут, т.к. если игрок зашел в игру первый раз - loadNBTData никогда не
* вызовется при логине. Подозреваю это из-за того, что на сервере нет NBT записи об этом игроке.
* Зато когда она заходит во второй раз - loadNBTData() точно вызовется, но т.к. перед ним вызовется и init(), то
* в loadNBTData() нужно предварительно очистить инициализацию нового игрока, которая выполнилась тут.
*/
ExtendedPlayer.get((EntityPlayer) entity).statsInventory.initItems();
ExtendedPlayer.get((EntityPlayer) entity).skillsInventory.initItems();
ExtendedPlayer.get((EntityPlayer) entity).rank = Rank.NOVICE;
// Инициализируем основные параметры
updateParams();
}
//@SideOnly(Side.SERVER) // TODO: Удалить это, когда я буду синхронизировать IEEP постоянно
public int getParamWithModifiers(ParamKeys param) {
switch (param) {
case CHARISMA: return charisma+modifierManager.getTotalValue(param);
case PERSISTENCE: return persistence+modifierManager.getTotalValue(param);
case PROTECTION: return protection+modifierManager.getTotalValue(param);
case STEP: return step+modifierManager.getTotalValue(param);
}
throw new IllegalStateException("Impossible state. Contact me about it");
}
// public int getProtection() {
// return protection;
// }
//
// public int getStep() {
// return step;
// }
//
// public int getPersistence() {
// return persistence;
// }
//
// public int getCharisma() {
//
// if (entityPlayer.worldObj.isRemote) {
// return charisma;
// } else {
// int value = 0;
// List<RollModifier> modifiers = modifierManager.getModifiers(ParamKeys.CHARISMA);
// if (modifiers == null) return 0;
//
// for (RollModifier modifier : modifiers)
// value += modifier.getValue();
//
// return value;
//
// }
// }
public int getExp() {
return exp;
}
public Rank getRank() {
return rank;
}
public int getTiredness() {
return tiredness;
}
public int getTirednessLimit() {
return tirednessLimit;
}
// public void setProtection(int protection) {
// this.protection = protection;
// }
//
// public void setPersistence(int persistence) {
// this.persistence = persistence;
// }
//
// public void setCharisma(int charisma) {
// this.charisma = charisma;
// }
public EntityPlayer getEntityPlayer() {
return entityPlayer;
}
public void setRank(Rank rank) {
this.rank = rank;
}
/**
* Перерасчитывает параметры игрока (такие, как например, {@link #protection})
*/
public void updateParams() {
// Расчитываем параметр "Защита"
ItemStack itemStack = ru.rarescrap.tabinventory.utils.Utils.findIn(
skillsInventory,
Utils.getRegistryName(SkillItems.fightingSkillItem),
StatItems.agilityStatItem.getUnlocalizedName()); // TODO: UnlocalizedName используется в качестве ключа вкладки!
if (itemStack != null) {
if (itemStack.getItem().getDamage(itemStack) == 0) {
this.protection = 2;
} else {
this.protection = 2 + StatItem.getRoll(itemStack).dice / 2;
}
}
// Рассчитываем параметр "Стойкость"
itemStack = Utils.findIn(statsInventory, Utils.getRegistryName(StatItems.enduranceStatItem));
if (itemStack != null)
this.persistence = 2 + StatItem.getRoll(itemStack).dice / 2;
// Расчитываем параметр "Харизма"
charisma = 0;
// List<RollModifier> modifiers = modifierManager.getModifiers(ExtendedPlayer.ParamKeys.CHARISMA);
// if (modifiers != null) {
// for (RollModifier rollModifier : modifiers) {
// charisma += rollModifier.value;
// }
// }
}
/**
* Синхронихронизиует серверного и клиентского ExtendedPlayer'а.
*/
public void sync() {
if(!entityPlayer.worldObj.isRemote) {
CommonProxy.INSTANCE.sendTo(new PacketSyncPlayer(this), (EntityPlayerMP)entityPlayer);
}
}
}
Код:
package rsstats.common;
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.network.IGuiHandler;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper;
import cpw.mods.fml.relauncher.Side;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import rsstats.blocks.Blocks;
import rsstats.blocks.UpgradeStationEntity;
import rsstats.common.event.KeyHandler;
import rsstats.common.network.*;
import rsstats.data.ExtendedPlayer;
import rsstats.inventory.container.MainContainer;
import rsstats.inventory.container.StatsContainer;
import rsstats.inventory.container.UpgradeContainer;
import rsstats.items.*;
import ru.rarescrap.tabinventory.network.NetworkUtils;
import static rsstats.common.RSStats.instance;
import static rsstats.common.RSStats.proxy;
/**
* Проки, содержащий код как для клиента, так и сервера
* @author RareScrap
*/
public class CommonProxy implements IGuiHandler {
/** Обработчик нажатия кнопок, используемых для вызова GUI */
protected KeyHandler keyHandler;
/** Обертка для работы с сетью */
public static /*final*/ SimpleNetworkWrapper INSTANCE = // TODO: Филан убран из-за тестов. Восстановить финал
NetworkRegistry.INSTANCE.newSimpleChannel(RSStats.MODID.toLowerCase());
public void preInit(FMLPreInitializationEvent event) {
int discriminator = 0;
// Когда сообщений станет много, их можно вынести в отдельный класс в метод init()
INSTANCE.registerMessage(RollPacketToServer.MessageHandler.class, RollPacketToServer.class, discriminator++, Side.SERVER); // Регистрация сообщения о пробросе статы
INSTANCE.registerMessage(PacketOpenRSStatsInventory.MessageHandler.class, PacketOpenRSStatsInventory.class, discriminator++, Side.SERVER);
INSTANCE.registerMessage(PacketOpenSSPPage.MessageHandler.class, PacketOpenSSPPage.class, discriminator++, Side.SERVER);
INSTANCE.registerMessage(PacketOpenWindow.MessageHandler.class, PacketOpenWindow.class, discriminator++, Side.SERVER);
INSTANCE.registerMessage(PacketSyncGUI.MessageHandler.class, PacketSyncGUI.class, discriminator++, Side.SERVER);
INSTANCE.registerMessage(PacketDialogAction.MessageHandler.class, PacketDialogAction.class, discriminator++, Side.SERVER);
INSTANCE.registerMessage(PacketSyncPlayer.MessageHandler.class, PacketSyncPlayer.class, discriminator++, Side.CLIENT);
INSTANCE.registerMessage(PacketCommandReponse.MessageHandler.class, PacketCommandReponse.class, discriminator++, Side.CLIENT);
// Регистрируем сообщения для библиотеки MinecraftTabInventory
NetworkUtils.registerMessages(INSTANCE, discriminator);
// Регистрация предметов
StatItems.registerItems();
SkillItems.registerItems();
OtherItems.registerItems();
MiscItems.registerItems();
PerkItems.registerItems();
DebugItems.registerDebugItems();
// Регистрация блоков
Blocks.registerBlocks();
// Это не срабатывает. Скорее всего, это решение предназначено для более поздних версий Forge
/*UpgradeStationBlock block3DWeb = (Block3DWeb)(new Block3DWeb().setUnlocalizedName("mbe05_block_3d_web_unlocalised_name"));
block3DWeb.setRegistryName("mbe05_block_3d_web_registry_name");
ForgeRegistries.BLOCKS.register(block3DWeb);
// We also need to create and register an ItemBlock for this block otherwise it won't appear in the inventory
ItemBlock itemBlock3DWeb = new ItemBlock(block3DWeb);
itemBlock3DWeb.setRegistryName(block3DWeb.getRegistryName());
ForgeRegistries.ITEMS.register(itemBlock3DWeb);*/
}
public void init(FMLInitializationEvent event) {
NetworkRegistry.INSTANCE.registerGuiHandler(instance, proxy);
registerKeyBindings();
}
public void postInit(FMLPostInitializationEvent event) {}
@Override
public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
switch (ID) {
case RSStats.GUI:
return ExtendedPlayer.get(player).container/*new MainContainer(player, player.inventory, ExtendedPlayer.get(player).statsInventory, ExtendedPlayer.get(player).skillsInventory, ExtendedPlayer.get(player).wearableInventory, ExtendedPlayer.get(player).otherTabsHost, ExtendedPlayer.get(player).otherTabsInventory)*/;
case RSStats.SSP_UI_CODE:
return new StatsContainer(player, player.inventory, ExtendedPlayer.get(player).statsInventory);
case RSStats.UPGRADE_UI_FROM_BLOCK_CODE: {
// Получение сущности по координатам блока, по которому кликнул игрок
TileEntity tileEntity = world.getTileEntity(x, y, z);
if (tileEntity instanceof UpgradeStationEntity) {
UpgradeStationEntity upgradeStationEntity = (UpgradeStationEntity) tileEntity;
return new UpgradeContainer(player.inventory, upgradeStationEntity.upgradeStationInventory);
}
break;
}
case RSStats.UPGRADE_UI_FROM_CMD_CODE:
return new UpgradeContainer(player.inventory, null);
}
return null;
}
@Override
public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
return null; // Переопределяется в ClientProxy
}
// Переопределяется в ClientProxy
public void registerKeyBindings() {}
}
Код:
package rsstats.client;
import cpw.mods.fml.client.registry.ClientRegistry;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.registry.GameRegistry;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraft.world.World;
import net.minecraftforge.client.MinecraftForgeClient;
import rsstats.blocks.Blocks;
import rsstats.blocks.UpgradeStationBlock;
import rsstats.blocks.UpgradeStationEntity;
import rsstats.blocks.UpgradeStationTESR;
import rsstats.client.gui.MainMenuGUI;
import rsstats.client.gui.SSPPage;
import rsstats.client.gui.UpgradeGUI;
import rsstats.common.CommonProxy;
import rsstats.common.RSStats;
import rsstats.common.event.KeyHandler;
import rsstats.data.ExtendedPlayer;
import rsstats.inventory.container.MainContainer;
import rsstats.inventory.container.UpgradeContainer;
/**
* Прокси, исполняемый на стороне клиента
* @author RareScrap
*/
public class ClientProxy extends CommonProxy {
/**
* Получает GUI для указанного ID
* @param ID идентификатор GUI, объект которого необходимо возвратить
* @param player Сущность игрока, вызывающего GUI
* @param world Мир
* @param x Местоположение сущности игрока по оси X
* @param y Местоположение сущности игрока по оси Y
* @param z Местоположение сущности игрока по оси Z
* @return Потомок класса GuiContainer, соответствующий указанному ID
*/
@Override
public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
// Проверяем, точно ли мы на клиете
// TODO: Не могу однозначно сказать что эта проверка делает
if (world instanceof WorldClient) {
// Ищем GUI, соответствующий данному ID
switch (ID) {
case RSStats.GUI: {
return new MainMenuGUI(
ExtendedPlayer.get(player),
ExtendedPlayer.get(player).container
/*new MainContainer(player,
player.inventory, // TODO: Уменьшить количество аргументов
ExtendedPlayer.get(player).statsInventory,
ExtendedPlayer.get(player).skillsInventory,
ExtendedPlayer.get(player).wearableInventory,
ExtendedPlayer.get(player).otherTabsHost,
ExtendedPlayer.get(player).otherTabsInventory)*/
);
}
/*
TODO: ВНИМАНИЕ! По туториалу, мне не нужно делать проверку в строке 25.
Более того, мне нужно свитч затолкать в CommonProxy. Но так как
у меня все работает и при таком раскладе, я пока оставлю все как есть
*/
case RSStats.SSP_UI_CODE: return new SSPPage(player, player.inventory, ExtendedPlayer.get(player).statsInventory);
case RSStats.UPGRADE_UI_FROM_BLOCK_CODE: return new UpgradeGUI(new UpgradeContainer(player.inventory, ((UpgradeStationEntity)world.getTileEntity(x, y, z)).upgradeStationInventory));
case RSStats.UPGRADE_UI_FROM_CMD_CODE: return new UpgradeGUI(new UpgradeContainer(player.inventory, null));
//case RSStats.DIALOG_GUI_CODE: return new MainMenuGUI.Dialog();
/* Тут не выйдет открывать диалоговое окно, потому что в один момент времени может быть открыт только
* один GuiScreen (см код откртия GUI). Выход - вызывать drawScreen() диалогового окна прямо из того
* GuiScreen, над которым нужно отобразить диалог. Используйте этот подход, если вам нужно отобразить
* один GuiScreen поверх другого. */
}
}
return null;
}
// Кей-хандлеры должны регистрироваться только на клиенте
@Override
public void registerKeyBindings() {
keyHandler = new KeyHandler();
FMLCommonHandler.instance().bus().register(keyHandler);
}
@Override
public void preInit(FMLPreInitializationEvent event) {
super.preInit(event);
//MinecraftForgeClient.registerItemRenderer(Item.getItemFromBlock(new UpgradeStationBlock()), new UpgradeStationTESR.Renderer(new UpgradeStationTESR(), new UpgradeStationEntity()));
}
// TODO: Непонятный пиздец. Хочет найти способ не создавать static поля в CommonProxy, но из-за метода ниже, я не могу этого сделать
@Override
public void init(FMLInitializationEvent event) {
super.init(event);
// Регистрируем рендереры
ClientRegistry.bindTileEntitySpecialRenderer(UpgradeStationEntity.class, new UpgradeStationTESR());
// Пытаюсь как-то получить item для блока
// Этот способ я использовал чтобы получить item блока, где сам блок не объявлен
// static переменной при регистрации в CommonProxy. Т.е. я простосоздавал локальную
// переменную через new UpgradeStationBlock() и регистрировал ее.
// Результат - любая попытка достать итем блока возвращает null, кроме явного создания нового ItemBlock из блока
Item d1 = Item.getItemFromBlock(new UpgradeStationBlock()); // null
ItemBlock d = new ItemBlock(new UpgradeStationBlock());
Item d12 = GameRegistry.findItem(RSStats.MODID, d.getUnlocalizedName()); // null
Item d13 = ItemBlock.getItemFromBlock(new UpgradeStationBlock()); // null
// А тут я делаю то же самое, но теперь использую ту же переменную блока, которая прошла регистрацию в CommonProxy
// Результат - все вызовы возвращают нормальнйы item
Item c1 = Item.getItemFromBlock(Blocks.upgradeStation);
ItemBlock c = new ItemBlock(Blocks.upgradeStation);
Item c12 = GameRegistry.findItem(RSStats.MODID, Blocks.upgradeStation.getUnlocalizedName());
Item c13 = ItemBlock.getItemFromBlock(Blocks.upgradeStation);
// Это не срабатывает, т.к. итем либо null, либо создан явно через консруктор (это, к моему удивлению, не регистрирует ItemRenderer
//UpgradeStationTESR.Renderer d2 = new UpgradeStationTESR.Renderer(new UpgradeStationTESR(), new UpgradeStationEntity());
//MinecraftForgeClient.registerItemRenderer(ItemBlock.getItemFromBlock(new UpgradeStationBlock()), new UpgradeStationTESR.Renderer(new UpgradeStationTESR(), new UpgradeStationEntity()));
// Только так так ItemRenderer успешно регистрируется и UpgradeStationTESR.Renderer#renderItem() успешно вызывается.
MinecraftForgeClient.registerItemRenderer(c1, new UpgradeStationTESR.Renderer(new UpgradeStationTESR(), new UpgradeStationEntity()));
// Как еще можно получить доступ к ItemRenderer'у. Где-то прочитал но зачем это надо - хз
//Minecraft.getMinecraft().entityRenderer.itemRenderer.
}
}
Я заранее извиняюсь за говнокод - он в процессе рефакторинга.
При входе в мир выдает краш-лог (приложил ниже). Я попытался пройти отладчиком, но вообще не понимаю что происходит. Вот на этой строе все нормально:
Но как только я делаю step into в конструктор EntityPlayerMP, то получается это
Как так получается? Вообще хз. Собстна, вот 2 вопроса:
- Годится ли такой способ синхронизации контейнера с клиентом? Приемлимо ли постоянно держать его открытым? Есть ли подводные камни?
- Что за дичь творится с отладчиков? Почему EntityPlayerMP не может создаться?
- Краш-лог
-
[20:00:28] [Server thread/INFO]: Preparing start region for level 0
[20:00:29] [Server thread/INFO]: Preparing spawn area: 3%
[20:00:30] [Server thread/INFO]: Preparing spawn area: 5%
[20:00:31] [Server thread/INFO]: Preparing spawn area: 9%
[20:00:32] [Server thread/INFO]: Preparing spawn area: 14%
[20:00:33] [Server thread/INFO]: Preparing spawn area: 20%
[20:00:34] [Server thread/INFO]: Preparing spawn area: 26%
[20:00:35] [Server thread/INFO]: Preparing spawn area: 34%
[20:00:36] [Server thread/INFO]: Preparing spawn area: 43%
[20:00:38] [Server thread/INFO]: Preparing spawn area: 54%
[20:00:39] [Server thread/INFO]: Preparing spawn area: 66%
[20:00:40] [Server thread/INFO]: Preparing spawn area: 79%
[20:00:41] [Server thread/INFO]: Preparing spawn area: 91%
[20:00:41] [Server thread/INFO]: Changing view distance to 12, from 10
[20:00:42] [Server thread/ERROR] [FML]: Exception caught during firing event net.minecraftforge.event.entity.EntityEvent$EntityConstructing@1fb21595:
java.lang.NullPointerException
at rsstats.inventory.container.MainContainer.addSlots(MainContainer.java:118) ~[MainContainer.class:?]
at rsstats.inventory.container.MainContainer.<init>(MainContainer.java:86) ~[MainContainer.class:?]
at rsstats.data.ExtendedPlayer.<init>(ExtendedPlayer.java:119) ~[ExtendedPlayer.class:?]
at rsstats.data.ExtendedPlayer.register(ExtendedPlayer.java:128) ~[ExtendedPlayer.class:?]
at rsstats.common.event.ModEventHandler.onEntityConstructing(ModEventHandler.java:40) ~[ModEventHandler.class:?]
at cpw.mods.fml.common.eventhandler.ASMEventHandler_10_ModEventHandler_onEntityConstructing_EntityConstructing.invoke(.dynamic) ~[?:?]
at cpw.mods.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:54) ~[ASMEventHandler.class:?]
at cpw.mods.fml.common.eventhandler.EventBus.post(EventBus.java:140) [EventBus.class:?]
at net.minecraft.entity.Entity.<init>(Entity.java:224) [Entity.class:?]
at net.minecraft.entity.EntityLivingBase.<init>(EntityLivingBase.java:155) [EntityLivingBase.class:?]
at net.minecraft.entity.player.EntityPlayer.<init>(EntityPlayer.java:165) [EntityPlayer.class:?]
at net.minecraft.entity.player.EntityPlayerMP.<init>(EntityPlayerMP.java:158) [EntityPlayerMP.class:?]
at net.minecraft.server.management.ServerConfigurationManager.createPlayerForUser(ServerConfigurationManager.java:443) [ServerConfigurationManager.class:?]
at net.minecraft.server.network.NetHandlerLoginServer.func_147326_c(NetHandlerLoginServer.java:105) [NetHandlerLoginServer.class:?]
at net.minecraft.server.network.NetHandlerLoginServer.onNetworkTick(NetHandlerLoginServer.java:64) [NetHandlerLoginServer.class:?]
at net.minecraft.network.NetworkManager.processReceivedPackets(NetworkManager.java:244) [NetworkManager.class:?]
at net.minecraft.network.NetworkSystem.networkTick(NetworkSystem.java:182) [NetworkSystem.class:?]
at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:726) [MinecraftServer.class:?]
at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:614) [MinecraftServer.class:?]
at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:118) [IntegratedServer.class:?]
at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:485) [MinecraftServer.class:?]
at net.minecraft.server.MinecraftServer$2.run(MinecraftServer.java:752) [MinecraftServer$2.class:?]
[20:00:43] [Server thread/ERROR] [FML]: Index: 1 Listeners:
[20:00:43] [Server thread/ERROR] [FML]: 0: NORMAL
[20:00:43] [Server thread/ERROR] [FML]: 1: ASM: rsstats.common.event.ModEventHandler@15b12bc5 onEntityConstructing(Lnet/minecraftforge/event/entity/EntityEvent$EntityConstructing;)V
[20:00:43] [Server thread/ERROR]: Encountered an unexpected exception
net.minecraft.util.ReportedException: Ticking memory connection
at net.minecraft.network.NetworkSystem.networkTick(NetworkSystem.java:198) ~[NetworkSystem.class:?]
at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:726) ~[MinecraftServer.class:?]
at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:614) ~[MinecraftServer.class:?]
at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:118) ~[IntegratedServer.class:?]
at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:485) [MinecraftServer.class:?]
at net.minecraft.server.MinecraftServer$2.run(MinecraftServer.java:752) [MinecraftServer$2.class:?]
Caused by: java.lang.NullPointerException
at rsstats.inventory.container.MainContainer.addSlots(MainContainer.java:118) ~[MainContainer.class:?]
at rsstats.inventory.container.MainContainer.<init>(MainContainer.java:86) ~[MainContainer.class:?]
at rsstats.data.ExtendedPlayer.<init>(ExtendedPlayer.java:119) ~[ExtendedPlayer.class:?]
at rsstats.data.ExtendedPlayer.register(ExtendedPlayer.java:128) ~[ExtendedPlayer.class:?]
at rsstats.common.event.ModEventHandler.onEntityConstructing(ModEventHandler.java:40) ~[ModEventHandler.class:?]
at cpw.mods.fml.common.eventhandler.ASMEventHandler_10_ModEventHandler_onEntityConstructing_EntityConstructing.invoke(.dynamic) ~[?:?]
at cpw.mods.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:54) ~[ASMEventHandler.class:?]
at cpw.mods.fml.common.eventhandler.EventBus.post(EventBus.java:140) ~[EventBus.class:?]
at net.minecraft.entity.Entity.<init>(Entity.java:224) ~[Entity.class:?]
at net.minecraft.entity.EntityLivingBase.<init>(EntityLivingBase.java:155) ~[EntityLivingBase.class:?]
at net.minecraft.entity.player.EntityPlayer.<init>(EntityPlayer.java:165) ~[EntityPlayer.class:?]
at net.minecraft.entity.player.EntityPlayerMP.<init>(EntityPlayerMP.java:158) ~[EntityPlayerMP.class:?]
at net.minecraft.server.management.ServerConfigurationManager.createPlayerForUser(ServerConfigurationManager.java:443) ~[ServerConfigurationManager.class:?]
at net.minecraft.server.network.NetHandlerLoginServer.func_147326_c(NetHandlerLoginServer.java:105) ~[NetHandlerLoginServer.class:?]
at net.minecraft.server.network.NetHandlerLoginServer.onNetworkTick(NetHandlerLoginServer.java:64) ~[NetHandlerLoginServer.class:?]
at net.minecraft.network.NetworkManager.processReceivedPackets(NetworkManager.java:244) ~[NetworkManager.class:?]
at net.minecraft.network.NetworkSystem.networkTick(NetworkSystem.java:182) ~[NetworkSystem.class:?]
... 5 more
[20:00:43] [Server thread/ERROR]: This crash report has been saved to: D:\Users\rares\Downloads\SavageWorldRP\eclipse\.\crash-reports\crash-2018-12-27_20.00.43-server.txt
[20:00:43] [Server thread/INFO]: Stopping server
[20:00:43] [Server thread/INFO]: Saving players
[20:00:43] [Server thread/INFO]: Saving worlds
[20:00:43] [Server thread/INFO]: Saving chunks for level 'Новый мир'/Overworld
[20:00:43] [Client thread/INFO] [STDOUT]: [net.minecraft.client.Minecraft:displayCrashReport:388]: ---- Minecraft Crash Report ----
// Sorry :(
Time: 27.12.18 20:00
Description: Ticking memory connection
java.lang.NullPointerException: Ticking memory connection
at rsstats.inventory.container.MainContainer.addSlots(MainContainer.java:118)
at rsstats.inventory.container.MainContainer.<init>(MainContainer.java:86)
at rsstats.data.ExtendedPlayer.<init>(ExtendedPlayer.java:119)
at rsstats.data.ExtendedPlayer.register(ExtendedPlayer.java:128)
at rsstats.common.event.ModEventHandler.onEntityConstructing(ModEventHandler.java:40)
at cpw.mods.fml.common.eventhandler.ASMEventHandler_10_ModEventHandler_onEntityConstructing_EntityConstructing.invoke(.dynamic)
at cpw.mods.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:54)
at cpw.mods.fml.common.eventhandler.EventBus.post(EventBus.java:140)
at net.minecraft.entity.Entity.<init>(Entity.java:224)
at net.minecraft.entity.EntityLivingBase.<init>(EntityLivingBase.java:155)
at net.minecraft.entity.player.EntityPlayer.<init>(EntityPlayer.java:165)
at net.minecraft.entity.player.EntityPlayerMP.<init>(EntityPlayerMP.java:158)
at net.minecraft.server.management.ServerConfigurationManager.createPlayerForUser(ServerConfigurationManager.java:443)
at net.minecraft.server.network.NetHandlerLoginServer.func_147326_c(NetHandlerLoginServer.java:105)
at net.minecraft.server.network.NetHandlerLoginServer.onNetworkTick(NetHandlerLoginServer.java:64)
at net.minecraft.network.NetworkManager.processReceivedPackets(NetworkManager.java:244)
at net.minecraft.network.NetworkSystem.networkTick(NetworkSystem.java:182)
at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:726)
at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:614)
at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:118)
at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:485)
at net.minecraft.server.MinecraftServer$2.run(MinecraftServer.java:752)
A detailed walkthrough of the error, its code path and all known details is as follows:
---------------------------------------------------------------------------------------
-- Head --
Stacktrace:
at rsstats.inventory.container.MainContainer.addSlots(MainContainer.java:118)
at rsstats.inventory.container.MainContainer.<init>(MainContainer.java:86)
at rsstats.data.ExtendedPlayer.<init>(ExtendedPlayer.java:119)
at rsstats.data.ExtendedPlayer.register(ExtendedPlayer.java:128)
at rsstats.common.event.ModEventHandler.onEntityConstructing(ModEventHandler.java:40)
at cpw.mods.fml.common.eventhandler.ASMEventHandler_10_ModEventHandler_onEntityConstructing_EntityConstructing.invoke(.dynamic)
at cpw.mods.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:54)
at cpw.mods.fml.common.eventhandler.EventBus.post(EventBus.java:140)
at net.minecraft.entity.Entity.<init>(Entity.java:224)
at net.minecraft.entity.EntityLivingBase.<init>(EntityLivingBase.java:155)
at net.minecraft.entity.player.EntityPlayer.<init>(EntityPlayer.java:165)
at net.minecraft.entity.player.EntityPlayerMP.<init>(EntityPlayerMP.java:158)
at net.minecraft.server.management.ServerConfigurationManager.createPlayerForUser(ServerConfigurationManager.java:443)
at net.minecraft.server.network.NetHandlerLoginServer.func_147326_c(NetHandlerLoginServer.java:105)
at net.minecraft.server.network.NetHandlerLoginServer.onNetworkTick(NetHandlerLoginServer.java:64)
at net.minecraft.network.NetworkManager.processReceivedPackets(NetworkManager.java:244)
-- Ticking connection --
Details:
Connection: net.minecraft.network.NetworkManager@448a3ead
Stacktrace:
at net.minecraft.network.NetworkSystem.networkTick(NetworkSystem.java:182)
at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:726)
at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:614)
at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:118)
at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:485)
at net.minecraft.server.MinecraftServer$2.run(MinecraftServer.java:752)
-- System Details --
Details:
Minecraft Version: 1.7.10
Operating System: Windows 10 (amd64) version 10.0
Java Version: 1.8.0_131, Oracle Corporation
Java VM Version: Java HotSpot(TM) 64-Bit Server VM (mixed mode), Oracle Corporation
Memory: 706855112 bytes (674 MB) / 1038876672 bytes (990 MB) up to 1038876672 bytes (990 MB)
JVM Flags: 3 total; -Xincgc -Xmx1024M -Xms1024M
AABB Pool Size: 0 (0 bytes; 0 MB) allocated, 0 (0 bytes; 0 MB) used
IntCache: cache: 1, tcache: 1, allocated: 12, tallocated: 94
FML: MCP v9.05 FML v7.10.99.99 Minecraft Forge 10.13.4.1558 5 mods loaded, 5 mods active
States: 'U' = Unloaded 'L' = Loaded 'C' = Constructed 'H' = Pre-initialized 'I' = Initialized 'J' = Post-initialized 'A' = Available 'D' = Disabled 'E' = Errored
UCHIJAAAA mcp{9.05} [Minecraft Coder Pack] (minecraft.jar)
UCHIJAAAA FML{7.10.99.99} [Forge Mod Loader] (forgeSrc-1.7.10-10.13.4.1558-1.7.10.jar)
UCHIJAAAA Forge{10.13.4.1558} [Minecraft Forge] (forgeSrc-1.7.10-10.13.4.1558-1.7.10.jar)
UCHIJAAAA rsstats{0.0.1a} [RSStats] (SavageWorldRP_main)
UCHIJAAAA examplemod{1.0} [Example Mod] (modid-1.0.jar)
GL info: ~~ERROR~~ RuntimeException: No OpenGL context found in the current thread.
Profiler Position: N/A (disabled)
Vec3 Pool Size: 0 (0 bytes; 0 MB) allocated, 0 (0 bytes; 0 MB) used
Player Count: 0 / 8; []
Type: Integrated Server (map_client.txt)
Is Modded: Definitely; Client brand changed to 'fml,forge'
[20:00:43] [Client thread/INFO] [STDOUT]: [net.minecraft.client.Minecraft:displayCrashReport:393]: #@!@# Game crashed! Crash report saved to: #@!@# .\crash-reports\crash-2018-12-27_20.00.43-server.txt
[20:00:43] [Client thread/INFO] [FML]: Waiting for the server to terminate/save.
[20:00:47] [Server thread/INFO]: Saving chunks for level 'Новый мир'/Nether
[20:00:47] [Server thread/INFO]: Saving chunks for level 'Новый мир'/The End
[20:00:51] [Server thread/INFO] [FML]: Unloading dimension 0
[20:00:51] [Server thread/INFO] [FML]: Unloading dimension -1
[20:00:51] [Server thread/INFO] [FML]: Unloading dimension 1
[20:00:51] [Server thread/INFO] [FML]: Applying holder lookups
[20:00:51] [Server thread/INFO] [FML]: Holder lookups applied
[20:00:51] [Server thread/INFO] [FML]: The state engine was in incorrect state SERVER_STOPPING and forced into state SERVER_STOPPED. Errors may have been discarded.
[20:00:51] [Client thread/INFO] [FML]: Server terminated.
Disconnected from the target VM, address: '127.0.0.1:60996', transport: 'socket'
AL lib: (EE) alc_cleanup: 1 device not closed
Java HotSpot(TM) 64-Bit Server VM warning: Using incremental CMS is deprecated and will likely be removed in a future release
Process finished with exit code -1
Краш-лог:
[20:00:28] [Server thread/INFO]: Preparing start region for level 0
[20:00:29] [Server thread/INFO]: Preparing spawn area: 3%
[20:00:30] [Server thread/INFO]: Preparing spawn area: 5%
[20:00:31] [Server thread/INFO]: Preparing spawn area: 9%
[20:00:32] [Server thread/INFO]: Preparing spawn area: 14%
[20:00:33] [Server thread/INFO]: Preparing spawn area: 20%
[20:00:34] [Server thread/INFO]: Preparing spawn area: 26%
[20:00:35] [Server thread/INFO]: Preparing spawn area: 34%
[20:00:36] [Server thread/INFO]: Preparing spawn area: 43%
[20:00:38] [Server thread/INFO]: Preparing spawn area: 54%
[20:00:39] [Server thread/INFO]: Preparing spawn area: 66%
[20:00:40] [Server thread/INFO]: Preparing spawn area: 79%
[20:00:41] [Server thread/INFO]: Preparing spawn area: 91%
[20:00:41] [Server thread/INFO]: Changing view distance to 12, from 10
[20:00:42] [Server thread/ERROR] [FML]: Exception caught during firing event net.minecraftforge.event.entity.EntityEvent$EntityConstructing@1fb21595:
java.lang.NullPointerException
at rsstats.inventory.container.MainContainer.addSlots(MainContainer.java:118) ~[MainContainer.class:?]
at rsstats.inventory.container.MainContainer.<init>(MainContainer.java:86) ~[MainContainer.class:?]
at rsstats.data.ExtendedPlayer.<init>(ExtendedPlayer.java:119) ~[ExtendedPlayer.class:?]
at rsstats.data.ExtendedPlayer.register(ExtendedPlayer.java:128) ~[ExtendedPlayer.class:?]
at rsstats.common.event.ModEventHandler.onEntityConstructing(ModEventHandler.java:40) ~[ModEventHandler.class:?]
at cpw.mods.fml.common.eventhandler.ASMEventHandler_10_ModEventHandler_onEntityConstructing_EntityConstructing.invoke(.dynamic) ~[?:?]
at cpw.mods.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:54) ~[ASMEventHandler.class:?]
at cpw.mods.fml.common.eventhandler.EventBus.post(EventBus.java:140) [EventBus.class:?]
at net.minecraft.entity.Entity.<init>(Entity.java:224) [Entity.class:?]
at net.minecraft.entity.EntityLivingBase.<init>(EntityLivingBase.java:155) [EntityLivingBase.class:?]
at net.minecraft.entity.player.EntityPlayer.<init>(EntityPlayer.java:165) [EntityPlayer.class:?]
at net.minecraft.entity.player.EntityPlayerMP.<init>(EntityPlayerMP.java:158) [EntityPlayerMP.class:?]
at net.minecraft.server.management.ServerConfigurationManager.createPlayerForUser(ServerConfigurationManager.java:443) [ServerConfigurationManager.class:?]
at net.minecraft.server.network.NetHandlerLoginServer.func_147326_c(NetHandlerLoginServer.java:105) [NetHandlerLoginServer.class:?]
at net.minecraft.server.network.NetHandlerLoginServer.onNetworkTick(NetHandlerLoginServer.java:64) [NetHandlerLoginServer.class:?]
at net.minecraft.network.NetworkManager.processReceivedPackets(NetworkManager.java:244) [NetworkManager.class:?]
at net.minecraft.network.NetworkSystem.networkTick(NetworkSystem.java:182) [NetworkSystem.class:?]
at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:726) [MinecraftServer.class:?]
at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:614) [MinecraftServer.class:?]
at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:118) [IntegratedServer.class:?]
at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:485) [MinecraftServer.class:?]
at net.minecraft.server.MinecraftServer$2.run(MinecraftServer.java:752) [MinecraftServer$2.class:?]
[20:00:43] [Server thread/ERROR] [FML]: Index: 1 Listeners:
[20:00:43] [Server thread/ERROR] [FML]: 0: NORMAL
[20:00:43] [Server thread/ERROR] [FML]: 1: ASM: rsstats.common.event.ModEventHandler@15b12bc5 onEntityConstructing(Lnet/minecraftforge/event/entity/EntityEvent$EntityConstructing;)V
[20:00:43] [Server thread/ERROR]: Encountered an unexpected exception
net.minecraft.util.ReportedException: Ticking memory connection
at net.minecraft.network.NetworkSystem.networkTick(NetworkSystem.java:198) ~[NetworkSystem.class:?]
at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:726) ~[MinecraftServer.class:?]
at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:614) ~[MinecraftServer.class:?]
at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:118) ~[IntegratedServer.class:?]
at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:485) [MinecraftServer.class:?]
at net.minecraft.server.MinecraftServer$2.run(MinecraftServer.java:752) [MinecraftServer$2.class:?]
Caused by: java.lang.NullPointerException
at rsstats.inventory.container.MainContainer.addSlots(MainContainer.java:118) ~[MainContainer.class:?]
at rsstats.inventory.container.MainContainer.<init>(MainContainer.java:86) ~[MainContainer.class:?]
at rsstats.data.ExtendedPlayer.<init>(ExtendedPlayer.java:119) ~[ExtendedPlayer.class:?]
at rsstats.data.ExtendedPlayer.register(ExtendedPlayer.java:128) ~[ExtendedPlayer.class:?]
at rsstats.common.event.ModEventHandler.onEntityConstructing(ModEventHandler.java:40) ~[ModEventHandler.class:?]
at cpw.mods.fml.common.eventhandler.ASMEventHandler_10_ModEventHandler_onEntityConstructing_EntityConstructing.invoke(.dynamic) ~[?:?]
at cpw.mods.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:54) ~[ASMEventHandler.class:?]
at cpw.mods.fml.common.eventhandler.EventBus.post(EventBus.java:140) ~[EventBus.class:?]
at net.minecraft.entity.Entity.<init>(Entity.java:224) ~[Entity.class:?]
at net.minecraft.entity.EntityLivingBase.<init>(EntityLivingBase.java:155) ~[EntityLivingBase.class:?]
at net.minecraft.entity.player.EntityPlayer.<init>(EntityPlayer.java:165) ~[EntityPlayer.class:?]
at net.minecraft.entity.player.EntityPlayerMP.<init>(EntityPlayerMP.java:158) ~[EntityPlayerMP.class:?]
at net.minecraft.server.management.ServerConfigurationManager.createPlayerForUser(ServerConfigurationManager.java:443) ~[ServerConfigurationManager.class:?]
at net.minecraft.server.network.NetHandlerLoginServer.func_147326_c(NetHandlerLoginServer.java:105) ~[NetHandlerLoginServer.class:?]
at net.minecraft.server.network.NetHandlerLoginServer.onNetworkTick(NetHandlerLoginServer.java:64) ~[NetHandlerLoginServer.class:?]
at net.minecraft.network.NetworkManager.processReceivedPackets(NetworkManager.java:244) ~[NetworkManager.class:?]
at net.minecraft.network.NetworkSystem.networkTick(NetworkSystem.java:182) ~[NetworkSystem.class:?]
... 5 more
[20:00:43] [Server thread/ERROR]: This crash report has been saved to: D:\Users\rares\Downloads\SavageWorldRP\eclipse\.\crash-reports\crash-2018-12-27_20.00.43-server.txt
[20:00:43] [Server thread/INFO]: Stopping server
[20:00:43] [Server thread/INFO]: Saving players
[20:00:43] [Server thread/INFO]: Saving worlds
[20:00:43] [Server thread/INFO]: Saving chunks for level 'Новый мир'/Overworld
[20:00:43] [Client thread/INFO] [STDOUT]: [net.minecraft.client.Minecraft:displayCrashReport:388]: ---- Minecraft Crash Report ----
// Sorry :(
Time: 27.12.18 20:00
Description: Ticking memory connection
java.lang.NullPointerException: Ticking memory connection
at rsstats.inventory.container.MainContainer.addSlots(MainContainer.java:118)
at rsstats.inventory.container.MainContainer.<init>(MainContainer.java:86)
at rsstats.data.ExtendedPlayer.<init>(ExtendedPlayer.java:119)
at rsstats.data.ExtendedPlayer.register(ExtendedPlayer.java:128)
at rsstats.common.event.ModEventHandler.onEntityConstructing(ModEventHandler.java:40)
at cpw.mods.fml.common.eventhandler.ASMEventHandler_10_ModEventHandler_onEntityConstructing_EntityConstructing.invoke(.dynamic)
at cpw.mods.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:54)
at cpw.mods.fml.common.eventhandler.EventBus.post(EventBus.java:140)
at net.minecraft.entity.Entity.<init>(Entity.java:224)
at net.minecraft.entity.EntityLivingBase.<init>(EntityLivingBase.java:155)
at net.minecraft.entity.player.EntityPlayer.<init>(EntityPlayer.java:165)
at net.minecraft.entity.player.EntityPlayerMP.<init>(EntityPlayerMP.java:158)
at net.minecraft.server.management.ServerConfigurationManager.createPlayerForUser(ServerConfigurationManager.java:443)
at net.minecraft.server.network.NetHandlerLoginServer.func_147326_c(NetHandlerLoginServer.java:105)
at net.minecraft.server.network.NetHandlerLoginServer.onNetworkTick(NetHandlerLoginServer.java:64)
at net.minecraft.network.NetworkManager.processReceivedPackets(NetworkManager.java:244)
at net.minecraft.network.NetworkSystem.networkTick(NetworkSystem.java:182)
at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:726)
at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:614)
at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:118)
at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:485)
at net.minecraft.server.MinecraftServer$2.run(MinecraftServer.java:752)
A detailed walkthrough of the error, its code path and all known details is as follows:
---------------------------------------------------------------------------------------
-- Head --
Stacktrace:
at rsstats.inventory.container.MainContainer.addSlots(MainContainer.java:118)
at rsstats.inventory.container.MainContainer.<init>(MainContainer.java:86)
at rsstats.data.ExtendedPlayer.<init>(ExtendedPlayer.java:119)
at rsstats.data.ExtendedPlayer.register(ExtendedPlayer.java:128)
at rsstats.common.event.ModEventHandler.onEntityConstructing(ModEventHandler.java:40)
at cpw.mods.fml.common.eventhandler.ASMEventHandler_10_ModEventHandler_onEntityConstructing_EntityConstructing.invoke(.dynamic)
at cpw.mods.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:54)
at cpw.mods.fml.common.eventhandler.EventBus.post(EventBus.java:140)
at net.minecraft.entity.Entity.<init>(Entity.java:224)
at net.minecraft.entity.EntityLivingBase.<init>(EntityLivingBase.java:155)
at net.minecraft.entity.player.EntityPlayer.<init>(EntityPlayer.java:165)
at net.minecraft.entity.player.EntityPlayerMP.<init>(EntityPlayerMP.java:158)
at net.minecraft.server.management.ServerConfigurationManager.createPlayerForUser(ServerConfigurationManager.java:443)
at net.minecraft.server.network.NetHandlerLoginServer.func_147326_c(NetHandlerLoginServer.java:105)
at net.minecraft.server.network.NetHandlerLoginServer.onNetworkTick(NetHandlerLoginServer.java:64)
at net.minecraft.network.NetworkManager.processReceivedPackets(NetworkManager.java:244)
-- Ticking connection --
Details:
Connection: net.minecraft.network.NetworkManager@448a3ead
Stacktrace:
at net.minecraft.network.NetworkSystem.networkTick(NetworkSystem.java:182)
at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:726)
at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:614)
at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:118)
at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:485)
at net.minecraft.server.MinecraftServer$2.run(MinecraftServer.java:752)
-- System Details --
Details:
Minecraft Version: 1.7.10
Operating System: Windows 10 (amd64) version 10.0
Java Version: 1.8.0_131, Oracle Corporation
Java VM Version: Java HotSpot(TM) 64-Bit Server VM (mixed mode), Oracle Corporation
Memory: 706855112 bytes (674 MB) / 1038876672 bytes (990 MB) up to 1038876672 bytes (990 MB)
JVM Flags: 3 total; -Xincgc -Xmx1024M -Xms1024M
AABB Pool Size: 0 (0 bytes; 0 MB) allocated, 0 (0 bytes; 0 MB) used
IntCache: cache: 1, tcache: 1, allocated: 12, tallocated: 94
FML: MCP v9.05 FML v7.10.99.99 Minecraft Forge 10.13.4.1558 5 mods loaded, 5 mods active
States: 'U' = Unloaded 'L' = Loaded 'C' = Constructed 'H' = Pre-initialized 'I' = Initialized 'J' = Post-initialized 'A' = Available 'D' = Disabled 'E' = Errored
UCHIJAAAA mcp{9.05} [Minecraft Coder Pack] (minecraft.jar)
UCHIJAAAA FML{7.10.99.99} [Forge Mod Loader] (forgeSrc-1.7.10-10.13.4.1558-1.7.10.jar)
UCHIJAAAA Forge{10.13.4.1558} [Minecraft Forge] (forgeSrc-1.7.10-10.13.4.1558-1.7.10.jar)
UCHIJAAAA rsstats{0.0.1a} [RSStats] (SavageWorldRP_main)
UCHIJAAAA examplemod{1.0} [Example Mod] (modid-1.0.jar)
GL info: ~~ERROR~~ RuntimeException: No OpenGL context found in the current thread.
Profiler Position: N/A (disabled)
Vec3 Pool Size: 0 (0 bytes; 0 MB) allocated, 0 (0 bytes; 0 MB) used
Player Count: 0 / 8; []
Type: Integrated Server (map_client.txt)
Is Modded: Definitely; Client brand changed to 'fml,forge'
[20:00:43] [Client thread/INFO] [STDOUT]: [net.minecraft.client.Minecraft:displayCrashReport:393]: #@!@# Game crashed! Crash report saved to: #@!@# .\crash-reports\crash-2018-12-27_20.00.43-server.txt
[20:00:43] [Client thread/INFO] [FML]: Waiting for the server to terminate/save.
[20:00:47] [Server thread/INFO]: Saving chunks for level 'Новый мир'/Nether
[20:00:47] [Server thread/INFO]: Saving chunks for level 'Новый мир'/The End
[20:00:51] [Server thread/INFO] [FML]: Unloading dimension 0
[20:00:51] [Server thread/INFO] [FML]: Unloading dimension -1
[20:00:51] [Server thread/INFO] [FML]: Unloading dimension 1
[20:00:51] [Server thread/INFO] [FML]: Applying holder lookups
[20:00:51] [Server thread/INFO] [FML]: Holder lookups applied
[20:00:51] [Server thread/INFO] [FML]: The state engine was in incorrect state SERVER_STOPPING and forced into state SERVER_STOPPED. Errors may have been discarded.
[20:00:51] [Client thread/INFO] [FML]: Server terminated.
Disconnected from the target VM, address: '127.0.0.1:60996', transport: 'socket'
AL lib: (EE) alc_cleanup: 1 device not closed
Java HotSpot(TM) 64-Bit Server VM warning: Using incremental CMS is deprecated and will likely be removed in a future release
Process finished with exit code -1