Jump to content

SnowyEgret

Members
  • Posts

    112
  • Joined

  • Last visited

Everything posted by SnowyEgret

  1. Ok. Thanks for the help Ernio and Deiseiben!
  2. The way I see it, there are two issues here: 1) develop a strategy for the server to write and read undo data to and from disk when undo data is too large to manage in ram. 2) develop a strategy for the server to set blocks and update clients in chunks. This is applied to both undo data (which may have been read from disk), and also to new changes to the terrain (which will in turn create new undo data). Does this make sense to you? I'd like to tackle them one at a time, probably 2 first. There are temporary fixes for 1, like just throwing out the undo if it is too large. One chunk = 16x16x256 = 65,536 blocks. Say at the end of one tick I've set 10,000 blocks spread over 10 chunks. That's 1,000 changes per chunk. Is it really more efficient to send 10 whole chunks (655,360 blocks)? Is there a way to send just my 10,000 changes to the client in one packet?
  3. Thanks for the advice Ernio (and deisieben), I will get to work on that today. I would like a general strategy for the server to set blocks, whether it is from an undo or a change. If I understand correctly, on the server side, I should call setBlockState for each block with no client update, then in my subscription to ServerTickEvent I update the client on every tick? What method do I call to update the client? Is there one available that knows what blocks need updating, or do I have to keep track of that myself? Do ServerTickEvents need to be created, or do you mean subscribe to ServerTickEvent?
  4. The undoManager maintains a list of transactions modifying the world. The player can undo and redo any changes he makes to the world. If in one transaction he creates a huge cube 100x100x100 blocks, there is a transaction with a million previous block states in it. When ctrl-z is pressed the world is set to the state before the transaction. If the undoManager is remembering the last 10 transactions he might be asking for a lot of memory. The player would get a feel for how far he can push his machine. Likewise for the selectionManager. When blocks are placed they are selected, and the last selection is remembered. If you think a server could deal with this, I am coming around to idea of them being on the server. I suppose I would maintain a map of player to managers constructed whenever a new player joins the game. It certainly would reduce network traffic. If the player were to hit the delete key with 100,000 blocks selected, that would be a lot of SetBlockStateMessages to the server.
  5. Dieseiben, I am really not trying to be stubborn here. I will accept as gospel that wait is bad. Fine, I'll look for another way. Thank you for the advice. As for as a blanket statement like "The client has no say in what happens in the world", Try as I might, I simply cannot understand it. If there was only a server, nothing would happen in the world. Anything that happens in the world originates from the player, in the client thread. The player plays the game. The server serves. This is starting to sound like some sort of free will debate. If the statement were "The client doesn't have final say about what happens in the world", then I could understand it. If this *is* what you mean, would a test like this satisfy you? (please note the renamed message class): private void processRequest(PolitePlaceBlockRequest request, EntityPlayerMP player) { World world = player.worldObj; BlockPos pos = request.getPos(); IBlockState state = request.getState(); if (canPlayerPlaceBlock(player, pos, state)) { world.setBlockState(pos, state); } } As far as all the player's context being on the server, allow me to be more specific. Each player's client thread has a SelectionManager, a PickManager, and an UndoManager. There might be tens of thousands of selections and even more undos. If there were 10 players in the game, with a set of managers for each player, the server would be over burdened. Note that if a player undos, it is only his work that is undone. I really can not see this working with the managers on the server.
  6. I'll rephrase : I should have been testing my mod over my lan with two players from the beginning. Not entirely the same thing. Pausing the client thread would only delay that player's game, no? Pausing the server thread would delay every player's game. If my wait is 10 milliseconds a few times every minute, I might decide that I can live with that. The important thing is that I'm not imposing it on other players in the game. When I said "I am letting the server set block states on the client" I meant that the client is not setting it's own block then asking the server to set its block. Instead, it is sending a message to the server to set the block and the change is propagated back to the client as in World.setBlockState(BlockPos, IBlockState) implies flag=3, which will update both server and client. I am letting the server do its work to keep clients in sync. But this is precisely what I want. Anyway, I appreciate your approach to solving the problem. You are saying, instead of messing around with stuff like wait(), try things a different way and your problems will just go away. And I have been busy trying do so. The problem I am running into is that there is a context for every client thread that the server must have in order to set the blocks. The way I have things set up now, each client manages its own context. The context is complex and it is infeasible sending it back and forth in a message.
  7. Thanks guys for patience. I should have started my mod in multiplayer from the beginning. I dealing with this now I can understand why you would not want to pause the server thread, but why not call wait() on the client thread? Most of the time I am setting blocks from onItemUse() on the server thread, and the server updates the client (flag 3 on setBlockState). Sometimes I'm on the client thread, either from a MousEvent or a KeyInputEvent. In these cases I am sending a series of messages to the server asking it to set a BlockState for me at a BlockPos. Whether I name my message SetBlockStateMessage or IHavePressedTheDKeyMessage, it comes down to the same thing, no? I still have to let the server know what state to set and at what position. The important thing is that I'm letting the server set block states on the client. Am I wrong? Just as a note, in my test class that I presented at the beginning of my thread, I was calling wait() on the server thread. That was not my intention. I have rewritten the test so that it is running on the client thread. I get the client thread for my println on the console now: [21:23:37] [Client thread/INFO] [sTDOUT]: [ds.plato.event.Test:<init>:23]: state=minecraft:dirt[snowy=false,variant=dirt]
  8. Thanks Ernio, This would be fine if there was one typical response on the client after setting a block via message to the server, but that is not the case. There are many situations where I set blocks, and the next thing I do is usually different. Trying to determine if this is a "NEVER, NEVER" case or a "know what you are doing case". It would help me understand if you could tell me what sort of problems I might typically run into using wait.
  9. But what if there are many things I might want to do after the message is complete? I have this running: public class SetBlockStateDoneMessageHandler implements IMessageHandler<SetBlockStateDoneMessage, IMessage> { @Override public IMessage onMessage(final SetBlockStateDoneMessage message, MessageContext ctx) { Plato.setBlockMessageDone = true; return null; } } where the message is sent like this: Plato.network.sendToServer(new SetBlockStateMessage(pos, Blocks.dirt.getDefaultState())); try { synchronized (this) { while (Plato.setBlockMessageDone == false) { wait(10); } System.out.println("state=" + world.getBlockState(pos)); } } catch (InterruptedException e) { e.printStackTrace(); } Plato.setBlockMessageDone = false; but I had to remove the Runnable from SetBlockStateMessageHandler as advised by TGG in mbe60: public class SetBlockStateMessageHandler implements IMessageHandler<SetBlockStateMessage, IMessage> { @Override public IMessage onMessage(final SetBlockStateMessage message, MessageContext ctx) { final EntityPlayerMP player = ctx.getServerHandler().playerEntity; // player.getServerForPlayer().addScheduledTask(new Runnable() { // public void run() { processMessage(message, player); // } // }); return new SetBlockStateDoneMessage(); } private void processMessage(SetBlockStateMessage message, EntityPlayerMP player) { player.worldObj.setBlockState(message.getPos(), message.getState()); } }
  10. Ok, now my handler returns a message: package ds.plato.network; import io.netty.buffer.ByteBuf; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.util.BlockPos; import net.minecraft.world.World; import net.minecraftforge.fml.common.network.simpleimpl.IMessage; import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; //https://github.com/TheGreyGhost/MinecraftByExample/tree/master/src/main/java/minecraftbyexample/mbe60_network_messages public class SetBlockStateMessageHandler implements IMessageHandler<SetBlockStateMessage, IMessage> { @Override public IMessage onMessage(final SetBlockStateMessage message, MessageContext ctx) { final EntityPlayerMP player = ctx.getServerHandler().playerEntity; player.getServerForPlayer().addScheduledTask(new Runnable() { public void run() { processMessage(message, player); } }); return new DoneMessage(); } private void processMessage(SetBlockStateMessage message, EntityPlayerMP player) { System.out.println("state="+message.getState()); World world = player.worldObj; world.setBlockState(new BlockPos(message.getX(), message.getY(), message.getZ()), message.getState()); } } I have registered a handler: network.registerMessage(DoneMessageHandler.class, DoneMessage.class, 4, Side.CLIENT); How do I wait until the message is returned? SimpleNetworkWrapper.sendToServer returns void.
  11. I am having problems with the asynchronous nature of a message. Setting sand to dirt like this: world.setBlockState(pos, Blocks.dirt.getDefaultState()); System.out.println("state=" + world.getBlockState(pos)); results in: [15:05:48] [server thread/INFO] [sTDOUT]: [ds.plato.Test:test:33]: state=minecraft:dirt[snowy=false,variant=dirt] where as: Plato.network.sendToServer(new SetBlockStateMessage(pos, Blocks.dirt.getDefaultState())); System.out.println("state=" + world.getBlockState(pos)); results in: [14:58:06] [server thread/INFO] [sTDOUT]: [ds.plato.Test:test:33]: state=minecraft:sand[variant=sand] [14:58:06] [server thread/INFO] [sTDOUT]: [ds.plato.network.SetBlockStateMessageHandler:processMessage:25]: state=minecraft:dirt[snowy=false,variant=dirt] How can I wait for the server to process the message? Here is my class SetBlockStateMessageHandler: package ds.plato.network; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.util.BlockPos; import net.minecraft.world.World; import net.minecraftforge.fml.common.network.simpleimpl.IMessage; import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; //https://github.com/TheGreyGhost/MinecraftByExample/tree/master/src/main/java/minecraftbyexample/mbe60_network_messages public class SetBlockStateMessageHandler implements IMessageHandler<SetBlockStateMessage, IMessage> { @Override public IMessage onMessage(final SetBlockStateMessage message, MessageContext ctx) { final EntityPlayerMP player = ctx.getServerHandler().playerEntity; player.getServerForPlayer().addScheduledTask(new Runnable() { public void run() { processMessage(message, player); } }); return null; } private void processMessage(SetBlockStateMessage message, EntityPlayerMP player) { System.out.println("state="+message.getState()); World world = player.worldObj; world.setBlockState(new BlockPos(message.getX(), message.getY(), message.getZ()), message.getState()); } }
  12. Works fine. The server is still available to process the message. @SubscribeEvent public void onGuiIngameMenuQuit(GuiScreenEvent.ActionPerformedEvent event) { if (event.gui instanceof GuiIngameMenu && event.button.id == 1) { //Message sent to server here; } } Thanks DieSieben07!
  13. Thanks, I will try that. I was editing my question while you were replying. Could you comment on my edit? I am not sure which side I'm on. If I'm already on the server side, I don't have to send the message, and I can do the work in my method serverStopping().
  14. Is there a 1.8 event I can subscribe to which is fired after button "Save and Quit to Title" is pressed, and before receiving a FMLServerStoppingEvent? I need to send a message to the server at this point. edit: If I subscribe to a FMLServerStoppingEvent, do I get the event on the server or client side? I am confused by what I see in the console. The println is on the server thread but the side is CLIENT: @EventHandler public void serverStopping(FMLServerStoppingEvent event) { System.out.println("side="+event.getSide()); } [13:01:50] [server thread/INFO] [sTDOUT]: [ds.plato.Plato:serverStopping:177]: side=CLIENT
  15. Solved my problem. Added one line to InventoryStaff.setInventoryContents to set the tag on the parent stack: @Override public void setInventorySlotContents(int slot, ItemStack stack) { tag.setStack(slot, stack); //Fix for issue #42: Staff loses spells when reopening gui parentStack.setTagCompound(tagStaff.getTag()); parentInventory.setInventorySlotContents(parentSlot, parentStack); } parentInventory is the inventory containing this inventory parentSlot the slot this inventory is in. parentStack is that slot's stack Thanks for the help CoolAlias!
  16. I pointed this out on my first post, maybe not clearly enough: I do not have a field of type ItemStack[] in my inventory which needs to be written to the tag after every change to the stack array. Instead, I am writing directly to the stack's tag on every call to setInventorySlotContents and decrStackSize. To illustrate the point I have added a println to my previously empty markDirty which prints the current state of the tag to the console: [10:46:00] [Client thread/INFO] [sTDOUT]: [ds.plato.item.staff.InventoryStaff:markDirty:64]: tag=TagStaff [tag={index:0,0:"SpellBox",}] [10:46:00] [Client thread/INFO] [sTDOUT]: [ds.plato.item.staff.InventoryStaff:markDirty:64]: tag=TagStaff [tag={index:0,0:"SpellBox",}] [10:46:00] [server thread/INFO] [sTDOUT]: [ds.plato.item.staff.InventoryStaff:markDirty:64]: tag=TagStaff [tag={index:0,0:"SpellBox",}] [10:46:00] [server thread/INFO] [sTDOUT]: [ds.plato.item.staff.InventoryStaff:markDirty:64]: tag=TagStaff [tag={index:0,0:"SpellBox",}] Not sure why I am getting two calls from each thread, but you can see that, after adding a box spell to the staff, the tags on both server and client side are up to date. When I close and reopen the gui I get this in the console from the println in Staff.onItemUse: [10:59:27] [Client thread/INFO] [sTDOUT]: [ds.plato.item.staff.Staff:onItemUse:55]: tag={index:0,0:"SpellBox",} [10:59:27] [server thread/INFO] [sTDOUT]: [ds.plato.item.staff.Staff:onItemUse:55]: tag={index:0,} Clearly, somewhere along the line, the server side tag has lost the box spell.
  17. In your tutorial the opening of your gui on the server is effectively modified by a keypress: @Override public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player) { if (!world.isRemote) { if (!player.isSneaking()) { player.openGui(TutorialMain.instance, TutorialMain.GUI_ITEM_INV, player.worldObj, (int) player.posX, (int) player.posY, (int) player.posZ); I understand now that player.isSneaking() is not the same as testing for a keypress. Thankyou for pointing that out. I have changed to isSneaking now. I am bothering with the keypress because with no modifier the call is passed on to the current spell on the staff. I need onItemUse because I want the side parameter for use in the spell. Anyway, non of this solves my original problem. When I move new spells from the player's inventory to the staff and reopen the gui, the spells are no longer there.
  18. Thanks coolAlias for the reply I had a look at your tutorial, specifically class ItemStore. I think my code is the equivalent of yours. I am returning when the world is remote and opening the gui on the server thread. Here is my Staff.onItemUse() cleaned up a little and commented. I have also overriden getMaxItemUseDuration as you suggest: @Override public int getMaxItemUseDuration(ItemStack stack) { return 1; // return any value greater than zero } @Override public boolean onItemUse(ItemStack stack, EntityPlayer player, World world, BlockPos pos, EnumFacing side, float sx, float sy, float sz) { // To compare item stacks and their tags on both sides System.out.println("tag=" + stack.getTagCompound()); // Return if called from the client thread. We only want the the call from the server side. if (world.isRemote) { return true; } // We are on the server side. Open staff gui if space bar is down if (Keyboard.isKeyDown(Keyboard.KEY_SPACE)) { player.openGui(Plato.instance, GuiHandler.GUI_STAFF, world, (int) player.posX, (int) player.posY, (int) player.posZ); return true; } // Get the current spell on the staff and use it if (!isEmpty(stack)) { Spell s = getSpell(stack); s.onItemUse(stack, player, world, pos, side, sx, sy, sz); return true; } return false; } I am overriding onItemUse() instead of onItemRightClick(), but my guess is that it shouldn't make a difference.
  19. Maybe a gradle dependency is pointing to the wrong version of Blood Magic. The compiler seems to have recognized symbol IBloodAlter.
  20. Hi A while back I posted for help with setting up an item with an inventory. I got everything working after much help from diesieben07 (many thanks again). Here is the thread: http://www.minecraftforge.net/forum/index.php/topic,22360.msg114869.html#msg114869 After porting to 1.8, things are not working any more. When I reopen the gui, all items placed in the inventory have disappeared. Here are my relevent classes (I am putting spells on a staff, both of which are items): InventoryStaff (setInventorySlotContents() passes the call on to the inventory passed to the constructor. I am writing immediately to the NBTTag) public class InventoryStaff implements IInventory { private TagStaff tag; //These three fields to be used in method setInventorySlotContents private IInventory parentInventory; private ItemStack parentStack; private int parentSlot; public InventoryStaff(IInventory parentInventory, int parentSlot) { this.parentInventory = parentInventory; this.parentSlot = parentSlot; parentStack = parentInventory.getStackInSlot(parentSlot); tag = new TagStaff(parentStack); } //IInventory-------------------------------------------------------- @Override public ItemStack getStackInSlot(int i) { return tag.getItemStack(i); } @Override public ItemStack decrStackSize(int i, int amount) { ItemStack stack = tag.getItemStack(i); tag.setItemStack(i, null); return stack; } @Override public ItemStack getStackInSlotOnClosing(int i) { //Seems like this is not being called System.out.println(); if (tag.getItemStack(i) != null) { ItemStack itemstack = tag.getItemStack(i); //Why this? tag.setItemStack(i, null); return itemstack; } else { return null; } } @Override public void setInventorySlotContents(int i, ItemStack stack) { tag.setItemStack(i, stack); //These three fields were passed to constructor parentInventory.setInventorySlotContents(parentSlot, this.parentStack); } @Override public int getSizeInventory() { return Staff.maxNumSpells; } @Override public int getInventoryStackLimit() { return 1; } @Override public void markDirty() { } @Override public boolean isUseableByPlayer(EntityPlayer player) { return true; } @Override public boolean isItemValidForSlot(int i, ItemStack stack) { return true; } // ---------------------------------- New 1.8 methods in IInventory @Override public String getName() { return null; } @Override public boolean hasCustomName() { return false; } @Override public IChatComponent getDisplayName() { return new ChatComponentText(getName()); } @Override public void openInventory(EntityPlayer player) { System.out.println(); } @Override public void closeInventory(EntityPlayer player) { } @Override public int getField(int id) { // TODO Auto-generated method stub return 0; } @Override public void setField(int id, int value) { // TODO Auto-generated method stub } @Override public int getFieldCount() { // TODO Auto-generated method stub return 0; } @Override public void clear() { //Seems this is not being called System.out.println(); for (int i = 0; i < parentInventory.getSizeInventory(); ++i) { setInventorySlotContents(i, null); } } } TagStaff encapsulates all the complexitity of writing to the tag and it working fine, so I won't post it. GuiStaffContainer: public class GuiStaffContainer extends Container { private IInventory inventoryStaff; public GuiStaffContainer(InventoryPlayer inventoryPlayer, IInventory inventoryStaff) { this.inventoryStaff = inventoryStaff; for (int i = 0; i < inventoryStaff.getSizeInventory(); i++) { //Overrides isItemValid to permit only spells in these slots addSlotToContainer(new GuiStaffSlot(inventoryStaff, i, 8 + i * 18, 18)); } for (int i = 0; i < 3; i++) { for (int j = 0; j < 9; j++) { addSlotToContainer(new Slot(inventoryPlayer, j + i * 9 + 9, 8 + j * 18, 49 + i * 18)); } } for (int i = 0; i < 9; i++) { addSlotToContainer(new Slot(inventoryPlayer, i, 8 + i * 18, 107)); } } @Override public boolean canInteractWith(EntityPlayer player) { return inventoryStaff.isUseableByPlayer(player); } } GuiHandler public class GuiHandler implements IGuiHandler { @Override public Object getServerGuiElement(int id, EntityPlayer player, World w, int x, int y, int z) { switch (id) { case 3: return new GuiStaffContainer(player.inventory, new InventoryStaff(player.inventory, player.inventory.currentItem)); default: throw new IllegalArgumentException("GUI id " + id + " is undefined"); } } @Override public Object getClientGuiElement(int id, EntityPlayer player, World world, int x, int y, int z) { switch (id) { case 3: return new GuiStaff(new GuiStaffContainer(player.inventory, new InventoryStaff(player.inventory, player.inventory.currentItem))); default: throw new IllegalArgumentException("GUI id " + id + " is undefined"); } } } The tag seems to be in sync on both side after placing a spell on the staff: [16:22:18] [Client thread/INFO] [sTDOUT]: [ds.plato.item.staff.TagStaff:setItemStack:65]: tag={index:0,0:"SpellBox",} [16:22:18] [server thread/INFO] [sTDOUT]: [ds.plato.item.staff.TagStaff:setItemStack:65]: tag={index:0,0:"SpellBox",} When I close and reopen the gui, the staff has no spells and the tag on the server side is empty: [16:24:30] [Client thread/INFO] [sTDOUT]: [ds.plato.item.staff.Staff:onItemUse:49]: tag={index:0,0:"SpellBox",} [16:24:30] [server thread/INFO] [sTDOUT]: [ds.plato.item.staff.Staff:onItemUse:49]: tag={index:0,} I am opening the gui in Staff.onItemUse: @Override public boolean onItemUse(ItemStack stack, EntityPlayer player, World world, BlockPos pos, EnumFacing side, float sx, float sy, float sz) { // To compare item stacks on both sides System.out.println("tag=" + stack.getTagCompound()); if (world.isRemote) { return true; } // Open staff gui if space is down if (Keyboard.isKeyDown(Keyboard.KEY_SPACE)) { player.openGui(Plato.instance, 3, world, 0, 0, 0); return true; } } Logic tells me I am doing something elsewhere on the server side which is clearing the tag, but I can't find it. Any help appreciated
  21. Hi , I am trying to make the player orbit around a block. I would like to have the player always face the block at the center of the orbit. I've been messing around with: player.setPosition(x, y, z); player.setPositionAndRotation(x, y, z, yaw, pitch); player.setPositionAndUpdate(x, y, z); player.setLocationAndAngles(x, y, z, yaw, pitch); Setting the position seems to be working ok. I am having problems setting the direction the player is looking. Ideally I would be looking for something like: player.setLocation(double x, double y, double z); player.facePosition(BlockPos pos); When you hit f5, minecraft behaves like this, except I want a block at the center of the view. Any help is appreciated
  22. Funny, this code was running on 1.7 with no messaging. Not sure how (probably badly). Anyway, works fine now with messaging. Thanks Draco.
  23. Hi. In my mod I have spells on a staff. Classes Staff and Spell are both items. To select the next spell on the staff, I am modifying the tag on the staff's stack on a key press in the client thread: @SideOnly(Side.CLIENT) @SubscribeEvent public void onKeyInput(KeyInputEvent event) { if (keyBindings.get("nextSpell").isPressed()) { EntityPlayerSP p = Minecraft.getMinecraft().thePlayer; ItemStack stack = p.getCurrentEquippedItem(); if (stack != null) { Item i = stack.getItem(); if (i instanceof Staff) { ((Staff) i).nextSpell(stack); } } } } Staff.nextSpell() modifies the tag on the stack, setting the index to the current spell. Later when I use the spell, I want to run on the server thread because it is going to modify the world: @Override public boolean onItemUse(ItemStack stack, EntityPlayer playerIn, World world, BlockPos pos, EnumFacing side, float hitX, float hitY, float hitZ) { System.out.println("tag="+stack.getTagCompound()); //Only on server side. if (world.isRemote) { return true; } The println shows that index has been updated only the tag in the client thread: [14:31:09] [Client thread/INFO] [sTDOUT]: [ds.plato.item.spell.Spell:onItemUse:97]: tag={3:"SpellLine",2:"SpellCone",index:6,1:"SpellCircle",0:"SpellBox",7:"SpellSeaShell",6:"SpellRectangle",5:"SpellPyramid",4:"SpellMeasure",8:"SpellSphere",} [14:31:09] [server thread/INFO] [sTDOUT]: [ds.plato.item.spell.Spell:onItemUse:97]: tag={3:"SpellLine",2:"SpellCone",index:0,1:"SpellCircle",0:"SpellBox",7:"SpellSeaShell",6:"SpellRectangle",5:"SpellPyramid",4:"SpellMeasure",8:"SpellSphere",} How can I set the stack in the server thread so that it is in sync with the client thread?
  24. Solved my problems. Works great. package ds.plato.block; import java.awt.Color; import java.util.ArrayList; import java.util.List; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.ItemCameraTransforms; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.model.IBakedModel; import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.util.EnumFacing; import net.minecraftforge.client.model.ISmartBlockModel; import net.minecraftforge.common.property.IExtendedBlockState; import net.minecraftforge.common.property.IUnlistedProperty; public class BlockSelectedModel implements ISmartBlockModel { public static ModelResourceLocation modelResourceLocation = new ModelResourceLocation("plato:blockSelected"); private IBakedModel defaultModel, model; private IUnlistedProperty selectedBlockProperty; private final int tint = Color.RED.getRed(); public BlockSelectedModel(IBakedModel defaultModel, PropertySelectedBlock selectedBlockProperty) { this.defaultModel = defaultModel; this.selectedBlockProperty = selectedBlockProperty; } @Override public IBakedModel handleBlockState(IBlockState state) { assert IExtendedBlockState.class.isAssignableFrom(state.getClass()); IBlockState s = ((IExtendedBlockState) state).getValue(selectedBlockProperty); model = Minecraft.getMinecraft().getBlockRendererDispatcher().getBlockModelShapes().getModelForState(s); return this; } @Override public List getFaceQuads(EnumFacing face) { List<BakedQuad> quads = new ArrayList<>(); List<BakedQuad> faceQuads = model.getFaceQuads(face); for (BakedQuad q : faceQuads) { quads.add(new BakedQuad(tint(q.getVertexData()), 0, face)); } return quads; } @Override public List getGeneralQuads() { List<BakedQuad> quads = new ArrayList<>(); List<BakedQuad> generalQuads = model.getGeneralQuads(); for (BakedQuad q : generalQuads) { quads.add( new BakedQuad(tint(q.getVertexData()), 0, q.getFace())); } return quads; } @Override public boolean isAmbientOcclusion() { return model.isAmbientOcclusion(); } @Override public boolean isGui3d() { return model.isGui3d(); } @Override public boolean isBuiltInRenderer() { return model.isBuiltInRenderer(); } @Override public TextureAtlasSprite getTexture() { return model.getTexture(); } @Override public ItemCameraTransforms getItemCameraTransforms() { return model.getItemCameraTransforms(); } // Private----------------------------------------------------- private int[] tint(int[] vertexData) { int[] vd = new int[vertexData.length]; System.arraycopy(vertexData, 0, vd, 0, vertexData.length); vd[3] = tint; vd[10] = tint; vd[17] = tint; vd[24] = tint; return vd; } } Thanks TGG!
  25. Ok, after looking closely at your excelent example, I've come up with this really simple solution which works ...almost. package ds.plato.block; import java.awt.Color; import java.util.List; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.ItemCameraTransforms; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.resources.model.IBakedModel; import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.util.EnumFacing; import net.minecraftforge.client.model.ISmartBlockModel; import net.minecraftforge.common.property.IExtendedBlockState; import net.minecraftforge.common.property.IUnlistedProperty; public class BlockSelectedModel implements ISmartBlockModel { public static ModelResourceLocation modelResourceLocation = new ModelResourceLocation("plato:blockSelected"); private IBakedModel defaultModel, model; private IUnlistedProperty selectedBlockProperty; private final int tint = Color.RED.getRed(); public BlockSelectedModel(IBakedModel defaultModel, PropertySelectedBlock selectedBlockProperty) { this.defaultModel = defaultModel; this.selectedBlockProperty = selectedBlockProperty; } @Override public IBakedModel handleBlockState(IBlockState state) { assert IExtendedBlockState.class.isAssignableFrom(state.getClass()); IBlockState s = ((IExtendedBlockState) state).getValue(selectedBlockProperty); model = Minecraft.getMinecraft().getBlockRendererDispatcher().getBlockModelShapes().getModelForState(s); return this; } @Override public List getFaceQuads(EnumFacing face) { List<BakedQuad> faceQuads = model.getFaceQuads(face); for (BakedQuad q : faceQuads) { q = new BakedQuad(tint(q.getVertexData()), 0, face); } return faceQuads; } @Override public List getGeneralQuads() { List<BakedQuad> generalQuads = model.getGeneralQuads(); for (BakedQuad q : generalQuads) { q = new BakedQuad(tint(q.getVertexData()), 0, q.getFace()); } return generalQuads; } @Override public boolean isAmbientOcclusion() { return model.isAmbientOcclusion(); } @Override public boolean isGui3d() { return model.isGui3d(); } @Override public boolean isBuiltInRenderer() { return model.isBuiltInRenderer(); } @Override public TextureAtlasSprite getTexture() { return model.getTexture(); } @Override public ItemCameraTransforms getItemCameraTransforms() { return model.getItemCameraTransforms(); } // Private----------------------------------------------------- private int[] tint(int[] vertexData) { //int[] vd = new int[vertexData.length]; //System.arraycopy(vertexData, 0, vd, 0, vertexData.length); int[] vd = vertexData; vd[3] = tint; vd[10] = tint; vd[17] = tint; vd[24] = tint; return vd; } } Just to recap, I am replacing the selected block with a new block that uses the model of the selected block. I modify to vertex data to tint the texture red. This works fine! The only problem is that some, but not all, of the blocks of the same type as the selected block are also tinted red: (How do I insert an image? file://~/image.png?, i'm on ubuntu.) If I return a copy of the new vertex data arrray as commented out in private method tint(), the problem goes away, but my block is not tinted. Any help appreciated.
×
×
  • Create New...

Important Information

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