Jump to content

Exsaliver

Members
  • Posts

    6
  • Joined

  • Last visited

Everything posted by Exsaliver

  1. Yeah sorry about the code, never really worked with patch files so didn't think about that, though it's easy to find what I've changed, there's a large block off comments around it. Anyway, you're right, it should work in the majority of cases when our does fail is when you try to dump a lot of items into an inventory. According to the issue on EE3 repository it happens when trying to move about 27 slots worth of items the is a bit more than 1,728 recursive calls. If you really need patch files I can add them when I get home.
  2. The method slotClick in the Container class is recursive (call path is slockClick -> retryClick -> slotClick) this result in a stack overflow when moving a large amount of items when shift-clicking. It will never happen with Vanilla Minecraft but it currently happens with EE3 (https://github.com/pahimar/Equivalent-Exchange-3/issues/901). I changed the functionality to work as loop, the code have many comments to show that it does not change any of the current functionality. Note that the current code uses reflection since I don't have access to Minecraft's private variables. Code: (http://pastebin.com/RT74SBF5) /* Credit Mr-J: * https://github.com/Mr-J/AdvancedBackpackMod/blob/master/unrelated/slotClick%2BComments%2BRename%2BHelpers.java.txt * * values for flag: * 0 = standard single click * 1 = single click + shift modifier * 2 = hotbar key is pressed (keys 0-9) * 3 = click with the middle button * 4 = click outside of the current gui window * 5 = button pressed & hold with the cursor holding an itemstack * 6 = double left click */ @Override public ItemStack slotClick(int slotId, int button, int flags, EntityPlayer player) { ItemStack result = null; ItemStack movedItemStack; InventoryPlayer playerInventory = player.inventory; int sizeOrID; if (flags == 5) // DRAGGED DISTRIBUTION { int l = this.getDistributeState(); this.setDistributeState(func_94532_c(button)); if ((l != 1 || this.getDistributeState() != 2) && l != this.getDistributeState()) { this.func_94533_d(); } else if (playerInventory.getItemStack() == null) { this.func_94533_d(); } else if (this.getDistributeState() == 0) { this.setPressedKeyInRange(func_94529_b(button)); if (func_94528_d(this.getPressedKeyInRange())) { this.setDistributeState(1); this.getDistributeSlotSet().clear(); } else { this.func_94533_d(); } } else if (this.getDistributeState() == 1) { Slot slot = (Slot) this.inventorySlots.get(slotId); if (slot != null && func_94527_a(slot, playerInventory.getItemStack(), true) && slot.isItemValid(playerInventory.getItemStack()) && playerInventory.getItemStack().stackSize > this.getDistributeSlotSet().size() && this.canDragIntoSlot(slot)) { this.getDistributeSlotSet().add(slot); } } else if (this.getDistributeState() == 2) { if (!this.getDistributeSlotSet().isEmpty()) { movedItemStack = playerInventory.getItemStack().copy(); sizeOrID = playerInventory.getItemStack().stackSize; Iterator iterator = this.getDistributeSlotSet().iterator(); while (iterator.hasNext()) { Slot slot1 = (Slot) iterator.next(); if (slot1 != null && func_94527_a(slot1, playerInventory.getItemStack(), true) && slot1.isItemValid(playerInventory.getItemStack()) && playerInventory.getItemStack().stackSize >= this.getDistributeSlotSet().size() && this.canDragIntoSlot(slot1)) { ItemStack itemstack1 = movedItemStack.copy(); int j1 = slot1.getHasStack() ? slot1.getStack().stackSize : 0; func_94525_a(this.getDistributeSlotSet(), this.getPressedKeyInRange(), itemstack1, j1); if (itemstack1.stackSize > itemstack1.getMaxStackSize()) { itemstack1.stackSize = itemstack1.getMaxStackSize(); } if (itemstack1.stackSize > slot1.getSlotStackLimit()) { itemstack1.stackSize = slot1.getSlotStackLimit(); } sizeOrID -= itemstack1.stackSize - j1; slot1.putStack(itemstack1); } } movedItemStack.stackSize = sizeOrID; if (movedItemStack.stackSize <= 0) { movedItemStack = null; } playerInventory.setItemStack(movedItemStack); } this.func_94533_d(); } else { this.func_94533_d(); } } else if (this.getDistributeState() != 0) { this.func_94533_d(); } else // NORMAL SLOTCLICK { Slot targetSlotCopy; int l1; ItemStack itemstack5; if ((flags == 0 || flags == 1) && (button == 0 || button == 1)) { // Not valid stack if (slotId == -999) { /* * on leftclick drop the complete itemstack from the inventory * on rightclick drop a single item from the itemstack */ if (playerInventory.getItemStack() != null && slotId == -999) { if (button == 0) { player.dropPlayerItemWithRandomChoice(playerInventory.getItemStack(), true); playerInventory.setItemStack((ItemStack) null); } if (button == 1) { player.dropPlayerItemWithRandomChoice(playerInventory.getItemStack().splitStack(1), true); if (playerInventory.getItemStack().stackSize == 0) { playerInventory.setItemStack((ItemStack) null); } } } } else if (flags == 1) // SHIFT-CLICK { while (true) { if (slotId < 0) // Invalid Slot { return null; } targetSlotCopy = (Slot) this.inventorySlots.get(slotId); //if targetSlotCopy is not null and the stack inside the slot can be moved if (targetSlotCopy != null && targetSlotCopy.canTakeStack(player)) { //transfer the picked up stack to targetSlotID in the player inverntory movedItemStack = this.transferStackInSlot(player, slotId); //if the movedItemStack was not transferred completely if (movedItemStack != null) { Item item = movedItemStack.getItem(); //set the return value to the rest result = movedItemStack.copy(); if (targetSlotCopy.getStack() != null && targetSlotCopy.getStack().getItem() == item) { // Original recursive bugged MC code // this.retrySlotClick(targetSlotID, mouseButtonPressed, true, player); // variables are: // * [0]:: targetSlotID -> same as entry // * [1]:: mouseButtonPressed -> same as entry // * [2]:: true -> const -> 1 // if (flags == 1) -> same as entry // * [3]:: player -> same as entry // // Final call: slotClick(slotId, button, 1, player) /* Execution path summery: * [entry]-> * {{ * [set only] result -> null * [set first] movedItemStack -> null * [const] playerInventory -> player.inventory * [unused] sizeOrID -> 0 * }} * [if (1 == 5) -> false] * [else if (this.getDistributeState() != 0)] -> false] * [else -> true] -> * {{ * [set first] targetSlotCopy -> null; * [unused] l1 -> 0; * [unused] itemstack5 -> null; * }} * [if ((flags == 0 || flags == 1) && (button == 0 || button == 1)) -> true] -> * {{}} * [if (slotId == -999) -> false] * [else if (flags == 1) -> true] * {{loop body}} * */ continue; //retry with the shift-click } } } break; //Keep current route } } else //if a click with NO shift modifier is performed { if (slotId < 0) { return null; } targetSlotCopy = (Slot) this.inventorySlots.get(slotId); if (targetSlotCopy != null) { movedItemStack = targetSlotCopy.getStack(); ItemStack itemstack4 = playerInventory.getItemStack(); if (movedItemStack != null) { result = movedItemStack.copy(); } if (movedItemStack == null) { if (itemstack4 != null && targetSlotCopy.isItemValid(itemstack4)) { l1 = button == 0 ? itemstack4.stackSize : 1; if (l1 > targetSlotCopy.getSlotStackLimit()) { l1 = targetSlotCopy.getSlotStackLimit(); } if (itemstack4.stackSize >= l1) { targetSlotCopy.putStack(itemstack4.splitStack(l1)); } if (itemstack4.stackSize == 0) { playerInventory.setItemStack((ItemStack) null); } } } else if (targetSlotCopy.canTakeStack(player)) { if (itemstack4 == null) { l1 = button == 0 ? movedItemStack.stackSize : (movedItemStack.stackSize + 1) / 2; itemstack5 = targetSlotCopy.decrStackSize(l1); playerInventory.setItemStack(itemstack5); if (movedItemStack.stackSize == 0) { targetSlotCopy.putStack((ItemStack) null); } targetSlotCopy.onPickupFromSlot(player, playerInventory.getItemStack()); } else if (targetSlotCopy.isItemValid(itemstack4)) { if (movedItemStack.getItem() == itemstack4.getItem() && movedItemStack.getItemDamage() == itemstack4.getItemDamage() && ItemStack.areItemStackTagsEqual(movedItemStack, itemstack4)) { l1 = button == 0 ? itemstack4.stackSize : 1; if (l1 > targetSlotCopy.getSlotStackLimit() - movedItemStack.stackSize) { l1 = targetSlotCopy.getSlotStackLimit() - movedItemStack.stackSize; } if (l1 > itemstack4.getMaxStackSize() - movedItemStack.stackSize) { l1 = itemstack4.getMaxStackSize() - movedItemStack.stackSize; } itemstack4.splitStack(l1); if (itemstack4.stackSize == 0) { playerInventory.setItemStack((ItemStack) null); } movedItemStack.stackSize += l1; } else if (itemstack4.stackSize <= targetSlotCopy.getSlotStackLimit()) { targetSlotCopy.putStack(itemstack4); playerInventory.setItemStack(movedItemStack); } } else if (movedItemStack.getItem() == itemstack4.getItem() && itemstack4.getMaxStackSize() > 1 && (!movedItemStack.getHasSubtypes() || movedItemStack.getItemDamage() == itemstack4.getItemDamage()) && ItemStack.areItemStackTagsEqual(movedItemStack, itemstack4)) { l1 = movedItemStack.stackSize; if (l1 > 0 && l1 + itemstack4.stackSize <= itemstack4.getMaxStackSize()) { itemstack4.stackSize += l1; movedItemStack = targetSlotCopy.decrStackSize(l1); if (movedItemStack.stackSize == 0) { targetSlotCopy.putStack((ItemStack) null); } targetSlotCopy.onPickupFromSlot(player, playerInventory.getItemStack()); } } } targetSlotCopy.onSlotChanged(); } } } // If a hotbar key is pressed (flag == 2) else if (flags == 2 && button >= 0 && button < 9) { targetSlotCopy = (Slot) this.inventorySlots.get(slotId); if (targetSlotCopy.canTakeStack(player)) { movedItemStack = playerInventory.getStackInSlot(button); boolean flag = movedItemStack == null || targetSlotCopy.inventory == playerInventory && targetSlotCopy.isItemValid(movedItemStack); l1 = -1; if (!flag) { l1 = playerInventory.getFirstEmptyStack(); flag |= l1 > -1; } if (targetSlotCopy.getHasStack() && flag) { itemstack5 = targetSlotCopy.getStack(); playerInventory.setInventorySlotContents(button, itemstack5.copy()); if ((targetSlotCopy.inventory != playerInventory || !targetSlotCopy.isItemValid(movedItemStack)) && movedItemStack != null) { if (l1 > -1) { playerInventory.addItemStackToInventory(movedItemStack); targetSlotCopy.decrStackSize(itemstack5.stackSize); targetSlotCopy.putStack((ItemStack) null); targetSlotCopy.onPickupFromSlot(player, itemstack5); } } else { targetSlotCopy.decrStackSize(itemstack5.stackSize); targetSlotCopy.putStack(movedItemStack); targetSlotCopy.onPickupFromSlot(player, itemstack5); } } else if (!targetSlotCopy.getHasStack() && movedItemStack != null && targetSlotCopy.isItemValid(movedItemStack)) { playerInventory.setInventorySlotContents(button, (ItemStack) null); targetSlotCopy.putStack(movedItemStack); } } } else if (flags == 3 && player.capabilities.isCreativeMode && playerInventory.getItemStack() == null && slotId >= 0) { targetSlotCopy = (Slot) this.inventorySlots.get(slotId); if (targetSlotCopy != null && targetSlotCopy.getHasStack()) { movedItemStack = targetSlotCopy.getStack().copy(); movedItemStack.stackSize = movedItemStack.getMaxStackSize(); playerInventory.setItemStack(movedItemStack); } } else if (flags == 4 && playerInventory.getItemStack() == null && slotId >= 0) { targetSlotCopy = (Slot) this.inventorySlots.get(slotId); if (targetSlotCopy != null && targetSlotCopy.getHasStack() && targetSlotCopy.canTakeStack(player)) { movedItemStack = targetSlotCopy.decrStackSize(button == 0 ? 1 : targetSlotCopy.getStack().stackSize); targetSlotCopy.onPickupFromSlot(player, movedItemStack); player.dropPlayerItemWithRandomChoice(movedItemStack, true); } } else if (flags == 6 && slotId >= 0) { targetSlotCopy = (Slot) this.inventorySlots.get(slotId); movedItemStack = playerInventory.getItemStack(); if (movedItemStack != null && (targetSlotCopy == null || !targetSlotCopy.getHasStack() || !targetSlotCopy.canTakeStack(player))) { sizeOrID = button == 0 ? 0 : this.inventorySlots.size() - 1; l1 = button == 0 ? 1 : -1; for (int i2 = 0; i2 < 2; ++i2) { for (int j2 = sizeOrID; j2 >= 0 && j2 < this.inventorySlots.size() && movedItemStack.stackSize < movedItemStack.getMaxStackSize(); j2 += l1) { Slot slot3 = (Slot) this.inventorySlots.get(j2); if (slot3.getHasStack() && func_94527_a(slot3, movedItemStack, true) && slot3.canTakeStack(player) && this.func_94530_a(movedItemStack, slot3) && (i2 != 0 || slot3.getStack().stackSize != slot3.getStack().getMaxStackSize())) { int k1 = Math.min(movedItemStack.getMaxStackSize() - movedItemStack.stackSize, slot3.getStack().stackSize); ItemStack itemstack2 = slot3.decrStackSize(k1); movedItemStack.stackSize += k1; if (itemstack2.stackSize <= 0) { slot3.putStack((ItemStack) null); } slot3.onPickupFromSlot(player, itemstack2); } } } } this.detectAndSendChanges(); } } return result; }
  3. I thought this "low level" layer was what FML is, regardless I have a few more things I'd like to discuses would you mind having a chat in a bit more convenient way (IRC, Skype whatever suites you)?
  4. I'm interested to know why Forge is against this kind of design, I understand the limitations coming from Minecraft, but other then that I don't see why IoC and intent oriented design won't fit, do you feel it's too much of a change or is there another reason?
  5. Greetings, forge community! Before I get to the actual suggestions I wish to make I would like to say a few things that may or may not be important (preface), for you convenience I've made the post into sections and sub-sections for easy reading. 1. Preface I will start off by expressing my appreciation to everything you have done so far, most poeple like to thing that you job is easy, "it's just a mod", well I know that it's comleately the opposite, not only you don't have documentation to work with, there is actually nothing to document (atleast at forge level) considering you are working with an un-obfuscated decompilation of java bytecode (or bitcode, can't remember what it's called). So having said that I want to be abosultly clear about one thing, I'm not criticising your work, I may have different ideas about how things should be done, but that's only my opinion which I formed over the years. So as I implicitly stated, I have some experience with the sort of things forge is doing (not exactly the same things but close). Let me give you a little background - if I'm not mistaking I've started programming about 7 years ago as an hobbyist, I was working mainly on development frameworks rather that software, as I gained more experience I became more and more "obsessed" with extensibility. I started to develop more and more frameworks around that that subject, from plugin loaders (similarly to FML) to IoC frameworks. I learned what makes things work and what doesn't, and I'd love to share that with you. 2. General rules in making extensible software The following list is a compilation of things I believe are necessary to make good extensible software. 2. 1. Plugins should not load themselves I believe that once plugins are able to load themselves they have too much control over the system. In forge Mods are loaded through some sort of pipeline where they are first activated (in the sense of instance activation of the '@Mod' decorated classes) and then are "pre-initialized", "initialized" and "post-initialized". The concept behind this process seems right but in my opinion all that is is a glorified hook that lets Mods do whatever they wish to do. It seems that Forge is loading the Mods but in practice the mods are actually loading themselves - they register blocks, items, entities etc. by themselves. 2. 2. Inversion of Control is mandatory (almost) Every time I made something plug-able I found IoC to the perfect solution, although is it not really mandatory (singletons and factories can achieve the same thing), it's just a joy for 3rd-parties to use the system, it's less error-prone and to be honest everyone is incorporating it into their software right now. In forge I haven't seen IoC being used - singletons (like FMLCommonHandler.instance) and static classes (like GameRegistry) (static classes are much worse than singletons and should not be used in my opinion) are used instead. 2. 3. Never ever let 3rd-parties access internals Well I know I'm unfair about that but 3rd-parties should never have access to internals and all interactions should be done through an API. I know that it's very difficult to achieve using forge because of Minecraft itself, so lets disregard that for now. The same goes for security and sandboxing. 2. 4. 3rd-parties have to ask first To explain this point I'll tell about some framework I've made some time ago - "Overlay" is an Mvvm framework I made some time ago it's purpose was to completely separate Presentation code from Business Logic, controls are bound to data and events through properties by the framework. Some properties allow read-write access and some allow read-only access. That means that, for example, a Textbox bound to a read only property should not be editable - ever, initially code that was using the framework could changed any aspect of the control including its editable, if a Textbox that is bound to a read-only property is made editable and it attempts to change its underlying property an exception will be thrown resulting in a crush most likely. Another problem to arise are race conditions - say one plugin colors all the buttons blue by default and another plugin colors all the buttons inside valid forms green which one should take presence? Well the second one should but who's to overrule that? These two problems made birth to the Aim framework which ask everyone to express their intents - i.e. changing the color of the button. All intents are given a priority and then the intent with the highest priority gets to act. In forge this could solve mod conflicts and (partially) the need for the ore dictionary, each mod could express an intent to add copper ore and only one will ever be added. 2. 5. This is it for now For now I can't think about more practices that should be integrated into forge, once I find more things to be improved I'll edit them into this section. 3. How I can help I've read the post that asks for pull requests from those who can help with them, I absolutely can help with that but as you can see the features I'm suggesting may require significant effort and will most likely result in some API changes, so I really want to hear what you think first. As I said I have a lot of the code base already written the only problem is that it needs to ported from .Net to JVM (C# to java). Other then I'm on vacation for the next month or so after that I'll be gone for about 3 or 4 weeks and the then I'll be on a mostly standard 8 hours work schedule. Thanks for reading! P.S. while writing the post I noticed you don't have a 'WYSIWYG' editor in place, I once worked with PhpBB forums and I used CKEdit (completely free) for that, it's a grate tool .
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.