Jump to content

Scribblon

Members
  • Posts

    22
  • Joined

  • Last visited

Converted

  • Gender
    Male
  • Location
    Netherlands
  • Personal Text
    Over Implementation FTW \o/

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

Scribblon's Achievements

Tree Puncher

Tree Puncher (2/8)

0

Reputation

  1. Thank you for your reply. It happens to be that I have been reading your particle tutorial. Also a thank you for that one. I am not certain that I can do this with JSON models. It is a it too dynamic for that, or I must consider using a texture animation just like the aura nodes from thaumcraft. Or that is what I suspect, I cannot check how that is really done as that is closed source. Currently I am researching the entity's model/renderer structure in minecraft. But I am suspecting that might be overkill for what it has to be. But right now it seems to be the only solution if I want an animated model. I will return whenever I finally understand all of this.
  2. Okay. In a last ditch effort I just started mash the prev-> button and skim titles. I found some possible answers. Some of which I found earlier through the search function but just couldn't determine if it applies on my issue or not. For reference: Tutorial on Customize rendering a non-tile-entity block. http://www.minecraftforge.net/forum/index.php/topic,28714.0.html Previous question with TESR rendering http://www.minecraftforge.net/forum/index.php/topic,29934.0.html A resource link seen in many https://github.com/TheGreyGhost/MinecraftByExample Render single quad http://www.minecraftforge.net/forum/index.php/topic,29848.0.html Unfortunately, I just don't know where to start on this. Everything I found has a stationary non moving model. I do know using JSON on dynamic models is a hell. But where should I begin? Let me add the following on this opportunity. The entity will be a faint line, two blocks tall. I want to copy these aesthetic http://guides.gamepressure.com/bioshockinfinite/gfx/word/612990500.jpg and https://www.youtube.com/watch?v=vpfDjHL1x9s. But a bit more blocky and less sparkly as that would probably kill fps everywhere. Maybe when I get further with my development it will become more flashy than a single wobbly line with a few break points slowly moving and rotating... but for now I want to settle with just that. Again, I am aware this is probably not the first time this is asked. But I am just not sure where to start with this as I see many different ways, and I am not certain which way is the right one.
  3. Good day, My subject head says it almost all. If you don't care about background look below the break line. For only the question go to the bold text. Many years I tried my hand on modding. Unfortunately whenever I hit something I don't know anything about and have done plenty of work I tend to just focus too much on the stuff I do know. This most of the time results in over engineered implementation and no graphical front end to interact with... Resulting in a burned out I, and 100s of hours of in the bin. Still wanting to finish this mod, I start again. Starting with the stuff I find most difficult: Rendering and graphics. Hence my question as I really do not know where to start, even after a week of searching google and this forum for tutorials or examples. So, the mod is about teleportation through rifts. Think in the style of bioshock infinite. In 1.7 I had a vague idea how to do this. But since 1.8 changed it all, I have no clue where to start. The rift does not move in between blockspaces after spawning, but they do continually move slightly in its blockspace. The entity spawns particles for effects. And can despawn after use or over time. Question: when an entity has to rendered which dynamically updates on collision or just by existing. The entities do not move after spawning to other block spaces. What type ( tile entity, living, or just from the base entity ) is best here? And what render system is the best. The rifts are non complex structures, most of the time just a faint thin line wobbling around a bit? Pointers, tips, are highly appreciated. Many thanks in advance.
  4. When you use DeBug mode you might bypass some systems that will help MC/FML/Forge recognize your TileEntity has been altered. I never actually thought this would work... Does the TileEntity function in-game as if it has power set at 500? Checklist for TileEntities not behaving: 1. Has TileEntity been registered ( GameRegistry.registerTileEntity(..) ) 2. Upon modifying internal data (through the game/ not debug-mode) outside vanilla methods... Is the entity marked as dirty? (this.markDirty()) Basically tells MC that your TileEntity has been altered and NBT-data should be updated. 3. If all that fails, MC/FML/Forge isn't able to sync your TileEntity through normal means, you will need to create your own DescriptionPacket. Disclaimer: This list has been compiled from personal experience. It is probably not complete. Use at own risk. Addition: What I am guessing here is that OP uses the Debug-function in their IDE. It will launch MC with the ability to alter the variables through the IDE while the game is running... I guess a breakpoint on the last line in the constructor can allow you to do this kind of stuff OP tries to explain.
  5. I am in the middle of the development of my own mod, and I really hoped nbt-data would sync correctly between the server and client whenever you don't pull anything fancy. But I guess I was wrong... Basically, you need to override #getDiscriptionPacket() and #onDataPacket(). I found a tutorial here: http://cazzar.net/minecraft/tutorials/Tile-Entity-Updates-The-Quick-and-Dirty-Method/ Make sure whenever an internal state changes you #markDirty(), or worldObj.markForUpdate(xCoord, yCoord, zCoord). The difference being that #markDirty() seems to mark the whole chunk to be updated, and the #markForUpdate(...) one you can force only one block to change.
  6. Does this also happen when you just move away from it and let it unload? Did you let it run at some point while testing? Well on the reload part that is... As far as I can see, whenever you just put in items, and don't let it run. It will never be #markDirty()-ied... Or I am missing something...
  7. I will report back when I am at the stage where I can benchmark this. (And I remember to check my //TODO list from time to time) Currently I am finalizing the ground work for the implementation of the items. Last thing to come are the renders and the textures... I am pretty much a programmer over artist '
  8. I would agree with you if I weren't going to add and remove elements in the ArrayList upon loading and unloading of TileEntities. Dynamic Arrays would have an O(n) on insertion and deletion. While HashSet has a O(1) on those. Both implementations are equal in providing iterators and for me the order in which the iterator presents all stored elements don't need to be in a particular order. In all other cases, I would go for an ArrayList. Even though I know these Big O notations only becomes a thing on large element numbers. I think I can still justify it here on the basis of the expected amount of insertions and deletions in the Collection. http://bigocheatsheet.com/
  9. Ah... Wait, I see the massive error in the example-Class. It missed the @Override-annotation. But if you say it was not going to be called the way I thought it would, it wouldn't have mattered anyway. But like I said, I do know what interfaces are on how they work on the high end. It is the actual compiled low end I am not aware of how interfaces work and interact with each other. If what you have just typed is how it works, I think I need to delve deeper into that. But what happens when a chunk is loaded? It is loading in the chunk... It is not setting blocks to be TileEntities or at least 6 'usage analysis'-s deep it seems to never be touched by something that loads in the TileEntity. There is only one way I guess I can test this. Just try it. And if that fails, back to the drawing board I must go. That explains the overhead it would have created whenever I would just add and delete these methods of different TileEntities on a Load/Unload basis. Guess modifying these files is a bit more of a hassle than adding and/or removing it from a Collection-implementation. And yes, if I was to go ahead with my plan without consulting. It would probably made the EventBus slow whenever a lot of Entities needed to be written in and out of the resulting Event-Super-Class. On the regular tick by tick basis it wouldn't have mattered if I understand it now. But I gotta say, it contradicts a bit what you said earlier. If an EventBus would have no overhead, adding complexity to the EventBus would result in no overhead either as it stands would have no overhead to begin with. It is that the native EventBus code isn't coded to handle registration and deletion of listeners beyond the Post-Init stage. But that is how Forge/FML decided to implement the Event Driven Architecture. In the end I came up with the following: I have an EventManager which contains a Collection (Type pending, EnumMap<EventType, HashSet<ISubsribeable<? extends RiftCraftEvent>> is currently the implementation I am heading for). In which I can register the ISubscribable interface implementations. Whenever an event is fired concerning my mod, the Manager will catch it and deligate it to the right Set, where it will iterate through the registered TileEntities. The registering and unregistering of TileEntities is done from the .invalidate(), validate() methods in the TileEntity. Currently I am not able to test this yet, so I keep my ChunkHandler around whenever I am actually able to test a rift. (I would love to be able to write JUnit tests for these kind of things... It only is not possible as it requires to interact with Minecraft-code itself to see how it loads and unloads Tile-Entities.) NEVERTHELESS, thanks for the info and the help.
  10. Ah, I wasn't aware. Well that rules out the TileEntity listens to the Events way I thought was the way. I am aware that the EventBus accepts any method name as long the name is accepted by the Java-compiler. But it always expect the method to have only 1 parameter, and this parameter should be a SubClass of the cpw.mods.fml.common.eventhandler.Event - class. It is that I actually never figured out how interfaces work at the low-end-level of Java. How is an object which implemented an interface presented to other objects. Will the Interface reference to the actual object making the ClassWriter read the right method with the correct Event Type. Or will the object stored as interface be reference to its own kind of Object which has only that interfaced (generalized) method available and has the real implementation masked as a Java-Compiled-Code-Fu-Reference underneath it. This does not mean that I don't know how they work and help in polymorphism on the high-end side of the code, what I am trying to achieve here. That explains why I couldn't find the .onChunkLoad() equivalent of the .onChunkUnload() method in TileEntity. But analyzing these methods leaves me wondering why .validate() is only used in two classes: BlockFurnace and Chunk. In chunk it is called in the obfuscated func_150812_a . Which is called in World#setTileEntity() and Chunk#addTileEntity()... Going deeper only makes it a confusing looping dependency mess back to Chunk, deeper again back to World?! Still, if I were to make my own manager with a list that listens to this event and delegate it to the whole registered list whenever this event is fired. I think the overhead will be about the same. Now I am registering to a Manager-class instead of the EventBus. The EventBus has already its own overhead when posting and registering an Event to the manager. And then you also got the Manager with its own Collection implementation to iterate it through. But yeah, I do not understand the EventBus well enough to know if this implementation would be worse or not. And it seems it is the only way since my original plan is already out the water. It does lack the ChunkHandler though if I can just use the TileEntites own unload/load implementations.
  11. This is a feasibility question, there is not much code yet. I am sure the route I am taking is possible, but as someone who likes to write as little code in the final implementation, but overwrites to get a solid generalized framework going... This thread is going to be about generalized/parameterized interfaces in the EVENT_BUS. Background: Skip to the line if you do not care. I am working on a mod which is focussed on balanced teleportation. This 'balanced' aspect includes many things. But I aim it to be easily accessible in the early game. Teleportation takes time and isn't instant. To get to the mid-game of the mod you need quite some resources. But when you reach it there are ways to create more of the same rather and in a put-resources-in-and-forget fashion. And towards the end-game you need to worry about an energy system (I can hear the cringes >:] , but yeah it is mostly in the end-game this is needed). And... it is inspired by the tears of Bioshock Infinite. So lots of tear-static noises, icons with static noise backgrounds, and the player being in two places at the same time. Probability Space, but also adds the Unmaking, and Void Space. But most of all, the biggest aspect is that a player can defend himself against unwanted incoming rifts as soon as the player found redstone and its first ender-shard. The players can place crafted blocks which will 'push away' or 'attract' the incoming rift. Allowing the player to set-up traps, force players to teleport inside walls etc etc. I understand I could do this in many ways. I could request the chunk data and check the TileEntity List there. Or iterate through the whole 'Loaded TileEntity'-list searching for my blocks whenever someone tries to teleport. But to think ALL tile-entities needed to be iterated through and `instanceof`-ed and casted. Makes me cringe when I think of the tubing in someones average base would take... So, I thought of the following: All TileEntities who should react to a player teleporting in, subscribes to a RiftFormationEvent. This way the iteration is only done upon loading of the TileEntity, and only on chunk-level. And whenever they are already loaded the event will make sure the right TileEntities will be invoked and no iteration of TileEntity lists (in my mod) is required. It seems I was wrong about some things concerning TileEntity loading on chunk-level. They aren't loaded in like that, unlike normal Entities... Still I plan on doing the event subscription like I made known. The question remains. And again it seems I was wrong. There IS a Map containing TileEntities and this is in chunk-sized (each chunk has its own). It is just that it is by no means clear what is stored inside this. On declaration it just makes a new HashMap (Assuming <Object, Object> it becomes clear that the Key Value is a ChunkPosition Object). -Player initiates teleport. -Chunk is force loaded (if it isn't loaded already) where exitEntity should spawn. (>Chunk.Load event is fired and subscribes TileEntities <<<<) -Chunk checks NBTData for possible interactions from neighboring chunks (Stored in chunk NBT). If true > >Chunk.Load event is fired and subscribes TileEntities <<<< (Repeat on a breath-First approach with a maximum depth of a TBD number. Currently it depends on what has been found so far. When it is an end-game disruptor it will break of the search and use that as the main force. When it is a network of simple disruption stones, I think 3 x 3 chunks should be the max... But that is subject to change) -RiftFormationEvent is fired. >All subscribed TileEntities will check if ExitEntity is in range. If so, it will register its own displacement vector in the event. If not, nothing will happen. >All displacements are calculated to the final destination of the ExitEntity -ExitEntity is spawned -EnterEntity is spawned -Player enters ProbabilitySpace -If both entities remain undisturbed when the player reaches his exit-gate. Player rifts through. (Currently the Player doesn't move on its own, and is more forced to go through an 'animated progress bar' of some sort.) If not, player will be thrown back to his start location with a bunch of damage... Or thrown into the Void (A dimension, not the one below the world). RnGesus (or the config option) will decide the players fate. -Ticket of the forced chunks is invalidated. The current points I am working at is notated with the "<<<<"-pointers. As far as I understand the Event system in Forge is that it will write a class on the fly which will call-back on the function you subscribe it to. But the problem with TileEntities is that they aren't always there, so I expect there to be problems whenever I leave a TileEntity subscribed. In the WorldManager who tries to unload an entity which is still referenced in an other part of the program. Or when the Event is fired and tries to invoke a subscribed method which isn't loaded in the memory. So, I have a ChunkEvent handler which subscribes and unsubscribes these Entities. To prevent writing a massive method to subscribe and unsubscribe every possible class event type combination, or go with a subscription system where all TileEntities are subscribed to the root-event-type of my mod and cast it to whatever is needed, I thought of the the following: I made a parameterized interface: package nl.scribblon.riftcraft.util; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import nl.scribblon.riftcraft.event.RiftCraftEvent; /** * Created by Scribblon for RiftCraft. * Date Creation: 04-09-14 * * Interface which needs to be implemented to whenever a TileEntity wants to be subscribed upon Chunk.Load. */ public interface ISubscribeable<T extends RiftCraftEvent> { void onEvent(T t); } The `@SubscribeEvent` will be placed in the implementation. When implementing this interface it will look like this: public class DisruptionStone implements ISubsribeable<RiftFormationEvent> { @SubscribeEvent public void onEvent(RiftFormationEvent event) { // do stuff } } Now here I start to enter a zone where I am questioning my own knowledge of java and how this interacts with the EVENT_BUS#register(...)-method. I have no clue how the ClassWriter inside the EventBus will interpret my interface. Or even how the ClassWriter does its magic. (It can be found in the ASMEventHandler) When I implement ISubscribable I must give the interface a Type. T will be for example a RiftFormationEvent. However, upon loading the Chunk it will be passed on to the EVENT_BUS#register(iSubscribed) as the generalized interface-form. Will the ClassWriter interpret the interface as it solved Parameter-Type? Or will it pick the RiftCraftEvent as the event type as it is the root or will all go well and I am just worrying about nothing? I hope someone knows, as Google and my Google-Fu has forsaken me. I am always open for suggestions, improvements, etc. EDIT: see strikethrough section.
  12. I remembered some post a while back... Let me look: http://minalien.com/minecraft-forge-feature-spotlight-api-annotation/ http://minalien.com/minecraft-forge-feature-spotlight-optional-annotation/ I think this is what you are looking for, if I am wrong apologies... I think you need the @Optional annotation over the @API one. As @API is meant to be package wide. But you can define it as a soft dependency on your mod to prevent it being unloadable whenever Fireplace Core isn't there... Then again, I am not sure myself when re-reading your problem. This really requires the Fireplace Core to have an API you want to implement. You will be calling a method in your own mod, and not some external mod expecting you to have some functionality implemented... Still, sorry if it doesn't really fit your problem.
  13. Although the author already chose a solution. I have been thinking about this problem too as I might be implementing such a feature in my mod as well. Maybe writing it down will answer it for myself. So here it goes: Initial reaction: This discussion can be made both ways, without contradicting the 'proper programming ethic'. But first, lets make clear what the different implementations I am thinking of using in my own mod. 1. Player-Attribute: A Block that has a meta-data value set on WorldGen and store it in the NBT-data of the Player and keep track of it by an OnInteractionEvent. The meta-data would be needed to identify which block the player has clicked. 2. TileEntity-Attribute: A TileEntity which keeps a list of playerIDs in its NBT-data structure. * Player-Attribute (Block+OnInteractionEvent) over TileEntity-Attribute: Reason: The player is the one who interacts with the block. The player either has clicked the TileEntity, or the player hasn't. So you keep track of this attribute on the player which of these blocks it has clicked. It is the player who can't access the TileEntity. Pros: Checking on player is quicker, as the list is shorter. Five always present booleans are easier to write away in, and read from, NBT-data. Whenever something happens to the block, the data is not lost as it is stored in the player. Cons: You cannot account for multiple worlds in which these blocks can be generated, unless that is intended. Every, every, time a player interacts with any block, your event is fired. Maybe not big for a single player world, but on the 100 player example all mining and interacting with blocks and tileEntities can start to contribute to the lag which is probably already present. Personal Note: I would personally not use the booleans in the fashion described above, but use a MapCollection and .add(BlockReference, boolean) to prevent the need to change this every time I (you) decide to change the number of these structures. This way you can support multiple worlds easily. * TileEntity-Attribute over Player-Attribute: Reason: The TileEntity can be clicked by the player. The TileEntity keeps track who has or hasn't. It is the TileEntity which denies access to the Player. So, this is a TileEntity-attribute. Pros: Each block has its own list. If the owner of a server decides to regenerate the world (without resetting the player data), players will be able to pick up the books from the new structures without the need to reset the values on every player. All code that regulate this is contained in one class which is the Object. Cons: If something happens to that block, that list will be lost (can be exploited). When the TileEntity gathers a large number of elements the whole list has to be written and read whenever the TileEntity is loaded or unloaded. Personal Note: There are collection implementations which guarantee an insertion and look-up time of O(1), http://bigocheatsheet.com/ . This means that each element added to the list will not increase the time needed to look-up or insert another element, but stays constant for each and everyone of them. A HashSet would probably be my choice. Even though it sounds weird, I would go for the Tile-Entity attribute. It keeps the code in one Class which is (IMHO) more along the Object-Orientation Ethic. However, that is my opinion on the code ethic. There are many ways to see this.
  14. Hello all, The short version: More CPU-Cycles or Higher Memory Usage: which is more preferred to have more (or less) of? A class with the least amount of, or as few as possible, fields and having all attributes which can be calculated from these variables whenever the #getAttribute() is called (on the fly). Or, a class that only does these calculations up front or when an update to any of the fields occur and store each attribute of an Object in a field (keeping in mind changes to any of the fields is correctly passed on to others). Or, something in between... But where is this 'sweet spot'? Understandably this discussion has probably been done many times before. However, I couldn't find a recent discussion on it through Google or the Search function on this forum. That is why I am just posting it here, I probably just used the wrong search queries so point me to the discussion where this has been discussed before. How I came to this question (Really, just background, only to support how I came to this question): Recently I have been working on a mod which is getting along nicely but slowly. Slowly, as I currently am working on the foundation... A.k.a. over-implementing the util-package. As the mod relies heavily on multi-block structures I have been focussing on that a lot to make it as simple as possible when I get to implementing them. But along the way I have been slowed down a lot by of instances of the question I have asked above. As someone who has learned and trained to always try to be as memory-efficient as possible, I tend to go for more CPU cycles. Let me give you the code I currently have. You don't need to go through it all, I will point out stuff with snippets further down. [spoiler=Whole bunch of code] This class is mainly used for multi-block structures. Don't let the lack of function-documentation fool you, the documentation is placed in the interfaces the classes implement. package nl.scribblon.riftcraft.util; import net.minecraft.block.Block; import net.minecraft.tileentity.TileEntity; import nl.scribblon.riftcraft.util.iplace.ILocationRC; import nl.scribblon.riftcraft.util.iplace.IRelativeLocationRC; /** * Created by Scribblon for RiftCraft. * Date Creation: 28-8-2014 */ public class RelativeLocation implements IRelativeLocationRC { public static final RelativeLocation ROOT = new RelativeLocation(0,0,0); private boolean isInterDimensional; private double x, y, z; public RelativeLocation(boolean isInterDimensional, double x, double y, double z) { this.isInterDimensional = isInterDimensional; this.x = x; this.y = y; this.z = z; } public RelativeLocation(double x, double y, double z) { this(false, x, y, z); } public RelativeLocation(ILocationRC location1, ILocationRC location2) { this.isInterDimensional = location1.getDimensionID() == location2.getDimensionID(); this.x = location2.getX() - location1.getX(); this.y = location2.getY() - location1.getY(); this.z = location2.getZ() - location1.getZ(); } /*_********************************************************************************************************* * Vector Functions */ @Override public IRelativeLocationRC getInverse() { return new RelativeLocation(this.isInterDimensional, -this.x, -this.y, -this.z); } @Override public IRelativeLocationRC sum(IRelativeLocationRC relativeLocation) { return new RelativeLocation( this.x + relativeLocation.getXShift(), this.y + relativeLocation.getYShift(), this.z + relativeLocation.getZShift()); } @Override public IRelativeLocationRC subtract(IRelativeLocationRC relativeLocation){ return new RelativeLocation( this.x - relativeLocation.getXShift(), this.y - relativeLocation.getYShift(), this.z - relativeLocation.getZShift() ); } /*_********************************************************************************************************* * Literal getters of various kinds */ @Override public double getXShift() { return this.x; } @Override public double getYShift() { return this.y; } @Override public double getZShift() { return this.z; } @Override public int getIntXShift() { return (int) Math.floor(this.x); } @Override public int getIntYShift() { return (int) Math.floor(this.y); } @Override public int getIntZShift() { return (int) Math.floor(this.z); } @Override public boolean isInterDimensional() { return this.isInterDimensional; } /*_********************************************************************************************************* * Relative getters of various kinds */ @Override public double getShiftedXTo(ILocationRC location) { return location.getX() + this.x; } @Override public double getShiftedYTo(ILocationRC location) { return location.getY() + this.y; } @Override public double getShiftedZTo(ILocationRC location) { return location.getZ() + this.z; } @Override public int getShiftedIntXTo(ILocationRC location) { return (int) this.getShiftedXTo(location); } @Override public int getShiftedIntYTo(ILocationRC location) { return (int) this.getShiftedYTo(location); } @Override public int getShiftedIntZTo(ILocationRC location) { return (int) this.getShiftedZTo(location); } @Override public ILocationRC getILocationRelativelyFrom(ILocationRC location) { return new Location(location.getWorld(), this.getShiftedXTo(location), this.getShiftedYTo(location), this.getShiftedZTo(location)); } @Override public Block getBlockRelativelyFrom(ILocationRC location) { return this.getILocationRelativelyFrom(location).getBlockAtLocation(); } @Override public TileEntity getTileEntityRelativelyFrom(ILocationRC location) { return this.getILocationRelativelyFrom(location).getTileEntityAtLocation(); } /*_********************************************************************************************************* * Auto-Generated things */ @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof RelativeLocation)) return false; RelativeLocation that = (RelativeLocation) o; if (isInterDimensional != that.isInterDimensional) return false; if (Double.compare(that.x, x) != 0) return false; if (Double.compare(that.y, y) != 0) return false; if (Double.compare(that.z, z) != 0) return false; return true; } @Override public int hashCode() { int result; long temp; result = (isInterDimensional ? 1 : 0); temp = Double.doubleToLongBits(x); result = 31 * result + (int) (temp ^ (temp >>> 32)); //I don't understand this... ' temp = Double.doubleToLongBits(y); result = 31 * result + (int) (temp ^ (temp >>> 32)); //I know these are bit shifts and are basically applying *2 to the value temp = Double.doubleToLongBits(z); result = 31 * result + (int) (temp ^ (temp >>> 32)); //But the '^' part and the need to do this... I don't... return result; } } This is basically RelativeLocation with a bit more information about what is allowed to be there. package nl.scribblon.riftcraft.util; import net.minecraft.block.Block; import nl.scribblon.riftcraft.util.imulti.IMultiTiledMaster; import nl.scribblon.riftcraft.util.iplace.IRelativeLocationRC; import java.util.Arrays; /** * Created by Scribblon for RiftCraft. * Date Creation: 29-8-2014 */ public class RelativeStructureBlock extends RelativeLocation implements Comparable { public static final RelativeStructureBlock ROOT = new RelativeStructureBlock(0,0,0); private Block[] allowedStructureParts; //Should maybe make this dynamic (Set)... But as far as I can see this is not needed. //Might be changed to OreDictionary compatible things... Maybe it is already... //Only for testing and defining a ROOT position is this here private RelativeStructureBlock(double x, double y, double z) { super(false, x,y,z); this.allowedStructureParts = null; } public RelativeStructureBlock(boolean isInterDimensional, double x, double y, double z, Block... allowedStructureParts) { super(isInterDimensional, x, y, z); this.allowedStructureParts = allowedStructureParts; } public RelativeStructureBlock(double x, double y, double z, Block... allowedStructureParts) { super(x, y, z); this.allowedStructureParts = allowedStructureParts; } public Block[] getAllowedStructureParts(){ return this.allowedStructureParts; } public boolean isBlockSupported(Block block) { if (allowedStructureParts.length <= 0) return false; for (Block allowedPart : allowedStructureParts) { if (allowedPart.equals(block)) return true; } return false; } public boolean isBlockSupportedRelativeTo(IMultiTiledMaster master) { return this.isBlockSupported(this.getBlockRelativelyFrom(master.getLocation())); } /*_********************************************************************************************************* * Auto-Generated things */ @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof RelativeStructureBlock)) return false; if (!super.equals(o)) return false; RelativeStructureBlock that = (RelativeStructureBlock) o; if (!Arrays.equals(allowedStructureParts, that.allowedStructureParts)) return false; return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + Arrays.hashCode(allowedStructureParts); return result; } /*_********************************************************************************************************* * Semi-Auto-Generated */ @Override public int compareTo(Object o) throws ClassCastException { if(!(o instanceof IRelativeLocationRC)) throw new ClassCastException("An IRelativeLocationRC implementations was expected."); IRelativeLocationRC that = (IRelativeLocationRC) o; if(this.getYShift() < that.getYShift()) return -1; if(this.getYShift() > that.getYShift()) return +1; if(this.getZShift() < that.getZShift()) return -1; if(this.getZShift() > that.getZShift()) return +1; if(this.getXShift() > that.getXShift()) return -1; if(this.getXShift() < that.getXShift()) return +1; return 0; } } This is the class which will contain all the information which defines a multi-block structure. This class will be implemented in the block that will become the master block as a public static. The current implementation does not support rotations but well... That is something for later. It uses a SortedSet implementation (TreeSet) to ensure that the blocks are checked in the order I intended them to be. Although I am aware of the log(n) speed of the add, remove, look-up, of this implementation. I intend to add the parts in order, it is just when I mess up it will ensure it still will. package nl.scribblon.riftcraft.util; import nl.scribblon.riftcraft.util.imulti.IMultiTiledMaster; import nl.scribblon.riftcraft.util.iplace.IRelativeStructure; import java.util.Set; import java.util.TreeSet; /** * Created by Scribblon for RiftCraft. * Date Creation: 29-8-2014 */ public class RelativeStructure implements IRelativeStructure { private TreeSet<RelativeStructureBlock> parts; public RelativeStructure(RelativeStructureBlock... parts){ this.parts = new TreeSet<RelativeStructureBlock>(); for(RelativeStructureBlock part : parts) this.parts.add(part); } @Override public Set<RelativeStructureBlock> getParts() { return this.parts; } @Override public RelativeStructureBlock getRoot() { return parts.subSet(RelativeStructureBlock.ROOT, RelativeStructureBlock.ROOT).first(); } @Override public boolean structureSupportsMaster(IMultiTiledMaster master) { return this.getRoot().isBlockSupported(master.getBlockType()); } @Override public boolean isStructureCorrectFrom(IMultiTiledMaster master) { if(!this.structureSupportsMaster(master)) return false; for(RelativeStructureBlock structureBlock : this.parts) { if(!structureBlock.isBlockSupportedRelativeTo(master)) return false; } return true; } } The leveled variant of the RelativeStructure. The class that has given me most headaches about the cpu vs memory question. The data-type of the 'levels' field has changed a lot. It could have been easily an array, but somewhere I needed it to be dynamic in some way. But that is not the biggest thing. It is the amount of 'for' loops it implement to calculate the other properties of the Object. But I will go on further down below. package nl.scribblon.riftcraft.util; import nl.scribblon.riftcraft.util.imulti.IMultiTiledMaster; import nl.scribblon.riftcraft.util.iplace.ILeveledRelativeStructure; import java.util.*; /** * Created by Scribblon for RiftCraft. * Date Creation: 29-8-2014 */ public class LeveledRelativeStructure implements ILeveledRelativeStructure { private ArrayList<RelativeStructure> levels; public LeveledRelativeStructure(RelativeStructure... levels){ this.levels = new ArrayList<RelativeStructure>(levels.length); for(RelativeStructure level : levels) this.levels.add(level); } @Override public Set<RelativeStructureBlock> getParts(int level) { return this.levels.get(level).getParts(); } @Override public boolean isStructureCorrectFrom(IMultiTiledMaster master, int toLevel) { return this.detectLevel(master) == toLevel; } @Override public int detectLevel(IMultiTiledMaster master) { if(!this.structureSupportsMaster(master)) return INVALID; for(Map.Entry<RelativeStructureBlock, Integer> set : this.getLevelMap(ROOT_LEVEL, levels.size()).entrySet()) { if(!set.getKey().isBlockSupportedRelativeTo(master)) return set.getValue() - 1; } return INVALID; } @Override public Map<RelativeStructureBlock, Integer> getLevelMap(int fromLevel, int toLevel) { LinkedHashMap<RelativeStructureBlock, Integer> levelMap = new LinkedHashMap<RelativeStructureBlock, Integer>(); for(int levelValue = fromLevel; levelValue < toLevel && levelValue < this.levels.size(); ++levelValue) { for(RelativeStructureBlock block : this.getParts(levelValue)) { levelMap.put(block, levelValue); } } return levelMap; } @Override public Set<RelativeStructureBlock> getParts() { TreeSet<RelativeStructureBlock> all = new TreeSet<RelativeStructureBlock>(); for(RelativeStructure structureLevel : this.levels) all.addAll(structureLevel.getParts()); return all; } @Override public RelativeStructureBlock getRoot() { return this.levels.get(ROOT_LEVEL).getRoot(); } @Override public boolean structureSupportsMaster(IMultiTiledMaster master) { return this.levels.get(ROOT_LEVEL).structureSupportsMaster(master); } @Override public boolean isStructureCorrectFrom(IMultiTiledMaster master) { return this.isStructureCorrectFrom(master, levels.size()); } } I am always open for suggestions, improvements and such. package nl.scribblon.riftcraft.util; <snip> public class LeveledRelativeStructure implements ILeveledRelativeStructure { private ArrayList<RelativeStructure> levels; <snip> @Override public int detectLevel(IMultiTiledMaster master) { if(!this.structureSupportsMaster(master)) return INVALID; for(Map.Entry<RelativeStructureBlock, Integer> set : this.getLevelMap(ROOT_LEVEL, levels.size()).entrySet()) { if(!set.getKey().isBlockSupportedRelativeTo(master)) return set.getValue() - 1; } return INVALID; } @Override public Map<RelativeStructureBlock, Integer> getLevelMap(int fromLevel, int toLevel) { LinkedHashMap<RelativeStructureBlock, Integer> levelMap = new LinkedHashMap<RelativeStructureBlock, Integer>(); for(int levelValue = fromLevel; levelValue < toLevel && levelValue < this.levels.size(); ++levelValue) { for(RelativeStructureBlock block : this.getParts(levelValue)) { levelMap.put(block, levelValue); } } return levelMap; } @Override public Set<RelativeStructureBlock> getParts() { TreeSet<RelativeStructureBlock> all = new TreeSet<RelativeStructureBlock>(); for(RelativeStructure structureLevel : this.levels) all.addAll(structureLevel.getParts()); return all; } <snip> } The point of my question being. The methods not snipped out in the class above can be implemented in two ways. One way is that it is a simple getter with the fields being calculated in the constructor. Or like this. However, TreeSet has a O(log(n)) on add, remove and contains. For traverse this is not much of an issue. It only becomes a thing in #getParts(), even though I am not planning on making massive structures where the Big-O can become an issue. And this method will be only called rarely. But even then the O(log(n)) will have the highest increase in time per element in the lower numbers... The other methods however (#detectLevel(..) and #getLevelMap(..) will be called every time the structure requires to check its structure onBlockBreak (once, if the structure breaks it will not check further), but when the upgrade is on the way this is probably called every time a block is placed or broken in the vicinity of the structure. Or an update is invoked with a what of a certain item. As someone who has learned and trained to use as less fields, the way I do it here is 'the correct way as I have been toughed'. However, of course, this is not the only way. As I know tick-lag is a big thing... I am certain memory-usage is one too. And I don't know where the right middle-ground is in this case. So, a slightly different formulated question from above. Where is the 'best place to be' in the 'CPU-Cycles vs Memory Usage' spectrum when modding for minecraft? My gratitude in advance,
  15. This is the final version of the code which handles the custom EntityItem. The solution to the 'Removed packet' is simply canceling the event. Back in my bukkit modding days I remembered it would cancel the whole processing of the event. Event things modified/added/spawned in the event-handler. I expected it would back-track everything: undo the spawn, undo the drop, and return the item to the itemstack. In short, I kept myself from it. But in the end it was the answer for the console spam. News from the Pull Request which will make this unified under Forge is still pending. It could be my own bad. I was a bit snarky towards people criticizing my code. I am used to defending my code by following the document to the letter. There was just no information about what to do when coding for a .java.patch file. I feel I should apologize, but I think it will not matter anyway. package nl.scribblon.riftcraft.handler; import cpw.mods.fml.common.eventhandler.Event; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import net.minecraft.client.Minecraft; import net.minecraft.entity.Entity; import net.minecraft.entity.item.EntityItem; import net.minecraft.init.Items; import net.minecraft.item.Item; import net.minecraft.item.ItemEnderPearl; import net.minecraft.item.ItemStack; import net.minecraft.world.World; import net.minecraftforge.event.entity.EntityJoinWorldEvent; import net.minecraftforge.oredict.OreDictionary; import nl.scribblon.riftcraft.entity.RCEntityItem; import nl.scribblon.riftcraft.event.EntityItemDeathEvent; import nl.scribblon.riftcraft.event.EntityItemHurtEvent; import nl.scribblon.riftcraft.init.ModItems; import nl.scribblon.riftcraft.reference.Settings; import nl.scribblon.riftcraft.util.LogHelper; import nl.scribblon.riftcraft.util.RandomHelper; import java.util.Random; /**f * Created by Scribblon for RiftCraft. * Date Creation: 31-7-2014 * More ore less the crafting mechanic for EnderShards. A multi-block will also be added to simplify things, but that is also a 'carfting by world-interaction'- thingy */ public class EnderPearlHandler { //TODO extract to reference (?) //SNIPED many public statics which is probably there until it gets extracted to the reference package @SubscribeEvent public void onEntityJoinWorld(EntityJoinWorldEvent event){ //Simple checks for quick returns, could not be memory friendly. if (event.world.isRemote) return; if (!(event.entity instanceof EntityItem)) return; if (event.entity instanceof RCEntityItem){ if(Settings.Debug.isDebugging) LogHelper.info("RCEntityItem just entered the world! " + event.entity); return; } //if (Settings.Debug.isDebugging) LogHelper.info("EntityItemJoinWorldEvent Triggered! " + event.entity); //if(event.entity.ticksExisted < 1) return; //Process entityItem EntityItem entityItem = (EntityItem) event.entity; //Again checking if items has been removed since the event triggered. if (entityItem.getEntityItem() == null) return; //if (Settings.Debug.isDebugging) LogHelper.info("Is this an EnderPearl? " + (entityItem.getEntityItem().getItem().getUnlocalizedName().equals(Items.ender_pearl.getUnlocalizedName()))); //Check if item in question is an EnderPearl if (isEnderPearl(entityItem)){ RCEntityItem rcEntityItem = RCEntityItem.convert(entityItem); event.setCanceled(true); event.world.spawnEntityInWorld(rcEntityItem); } } @SubscribeEvent public void onItemDeath(EntityItemDeathEvent event){ if(Settings.Debug.isDebugging) LogHelper.info("ItemDeath Triggered"); if (isEnderPearl(event.entityItem) && event.damageSource.isExplosion()){ //Calculate chances int spawnTotal = 0; for(int i = 0; i < event.entityItem.getEntityItem().stackSize; i++) { if (RandomHelper.rollD100(EXPLOSION_FIRST_CHANCE)) { ++spawnTotal; if (RandomHelper.rollD100(EXPLOSION_SECOND_CHANCE)) { ++spawnTotal; if (RandomHelper.rollD100(EXPLOSION_THIRD_CHANCE)) { ++spawnTotal; if (RandomHelper.rollD100(EXPLOSION_FOURTH_CHANCE)) ++spawnTotal; } } } } if(Settings.Debug.isDebugging) LogHelper.info("Chances rolled: " + spawnTotal); if(spawnTotal > 0){ event.entity.worldObj.spawnEntityInWorld(createShardEntity(event.entity.worldObj, event.entity, spawnTotal)); } } } private boolean isEnderPearl(EntityItem entityItem){ return entityItem.getEntityItem().getItem().getUnlocalizedName().equals(Items.ender_pearl.getUnlocalizedName()); } private EntityItem createShardEntity(World world, Entity entity, int amount){ return new EntityItem(world, entity.posX, entity.posY, entity.posZ, new ItemStack(ModItems.enderShard, amount)); } }
×
×
  • Create New...

Important Information

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