Jump to content

Bektor

Forge Modder
  • Posts

    852
  • Joined

  • Last visited

Posts posted by Bektor

  1. Hi,

     

    I've got a little problem. My UI rendered on top of the block using the RenderGameOverlayEvent.Post event doesn't show my changed values.

     

    When my block received energy it normally displays the new values for how much energy it stores now, but in some rare condition it doesn't show the new value.

    For example you have a solar panel and place under the solar panel the new block. The new block shows always 0 until you relog. After a relog it shows the actual value.

    If you would place the solar panel ontop of the new block it will work as it should be, but having the solar panel already placed, the UI doesn't show the changed values.

     

    in update(): {
      [...]
            if(this.flag) {
            	this.getWorld().notifyBlockUpdate(this.getPos(), this.getWorld().getBlockState(this.getPos()), this.getWorld().getBlockState(this.getPos()), 3);
                this.markDirty();
            }
    }
    
        @Override
        public SPacketUpdateTileEntity getUpdatePacket() {
            return new SPacketUpdateTileEntity(this.getPos(), 3, this.getUpdateTag());
        }
        
        @Override
        public NBTTagCompound getUpdateTag() {
            return this.writeToNBT(new NBTTagCompound());
        }
        
        @Override
        public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) {
            super.onDataPacket(net, pkt);
            this.handleUpdateTag(pkt.getNbtCompound());
        }

    Note: flag will change when the block itself sends energy.

     

    Thx in advance.

    Bektor

  2. 6 hours ago, Animefan8888 said:

    If I am not mistaken Ender IO and Thermal Expansion send energy through there pipes by just filling there pipes with energy, which allows them to get past all of that path finding that if I am not mistaken IC2 does. Though if you go with my suggestion from the other thread you could bypass all of that and just store it in the 'network' instead of the pipes.

    I might look into your idea at some point, thought not yet as I don't have enough time to do the required testing and implementation before the Server of a few friends of mine goes online.

    Hm, I'm wondering, how does this pathfinding from IC2 actually work?

  3. Hi,

     

    I'm currently working on an energy network and I am wondering how I can send energy into a specific direction.

     

    Imagine the following situation:

    • You've got about 100 solar panels which produce energy
    • You've also got one machine which should consume the energy
    • Everything is connected with cables

     

    Point beeing: How can I and how do other mods find out in which direction the energy should be send so that it ends up beeing send to the consumer and not beeing send to producer 51 or the like.

     

    Thx in advance.

    Bektor

  4. 9 hours ago, Animefan8888 said:

    And instead of storing the network in the pipes/energy tileentities. You could store it in a WorldSavedData and simply reference it when ever you have a World instance.

    The problem I see with this is when multiply players build up their networks and these networks aren't connected to each other.

     

    9 hours ago, Animefan8888 said:

    Adding things to the network could simply be done in the Block.onBlockPlaced and Block.breakBlock, or Block.onNeighborChanged

    Here we have the problem that when adding blocks simply to the network in those methods: How do you know they are really connected to the network? Who tells you that they are connected to other cables? How do you know they are connected to other cables and those cables are part of the network and not some random cables in the world connected to no network??

     

    9 hours ago, Animefan8888 said:

    Your FPS shouldn't be consistently low unless you are handling all of this on the client side as well as the server side which is entirely unnecessary.

     

    Well, looking at the code, I guess this one if-check got lost in the endless code updates. ^^

     

  5. 7 hours ago, Socratic_Phoenix said:

    Don't save it. Unless you're storing large amounts of data, caching will likely be more trouble than it's worth..

     

    Why would caching be more trouble than it's worth?

     

    7 hours ago, Socratic_Phoenix said:

    How much data do you plan on sending?

    Well, currently I plan just on sending a few hundred values to the client. This values might either be of integer type of boolean or long.

  6. 15 hours ago, mysticalherobine said:

    I would highly recommend learning to use the RF Api by TeamCOFH

    I would rather start with FE and not use RF, because the FE API is build into Forge itself and thus it is always available. You would also not run into problems when the RF API doesn't update anymore. Like I remember a time where the mods by TeamCOFH got the 1.7.10 version 2014 and three years later followed 1.10.2 skipping 1.8.9 etc.

    Also most mods which support RF also support FE. More explanation on wether to use FE or RF or both might be found here:

    https://minecraft.curseforge.com/projects/redstone-flux

     

    16 hours ago, Sommanker said:

    mainly because most mods have complex systems and implementations

    Most mods have this complex implementations because they are supporting multiply energy APIs while converting the energy internally to the one which is requested as an output or they got their own energy API for internal uses and convert to RF or FE or whatever is required by the connected machines.

    When searching for mods which support energy and don't have complex systems I would start searching for mods which just support FE or maybe even support FE and Tesla, but I would also suggest looking into mods which got a fresh rewrite as otherwhise it might be that at one point they added support for FE into their existing systems and later on removed all the other APIs which aren't needed and such code is harder to read when you've got no idea how it works.

     

    15 hours ago, Sommanker said:

    Admittedly, there are more tutorials so it would be far easier for me to get started with RF, but equally I think I would rather, if at all possible, go straight to what appears to be becoming the future popular API to use, and if not FE, then Tesla.

     

    WARNING: I might have missed some things in the following description as my own energy system also supports Tesla and thus is a bit more complicated.

     

    As a starting point for FE, I would highly suggest to learn about the Capability System by Forge, if you haven't already done this.

    https://mcforge.readthedocs.io/en/latest/datastorage/capabilities/

    This is required as the Forge Energy system is based around capabilities.

     

    The next point would be create a new class extended from EnergyStorage or just implement all the code yourself using the IEnergyStorage interface which I would suggest if you plan on overwritting all methods provided in EnergyStorage anyway. This class should also implement the interface INBTSerializable<NBTTagCompound> if you want to store your energy data (I assume here that you are already familiar with Java Generics).

    In this class, save your data and read it in using normal NBT read and write methods (serializeNBT and deserializeNBT). This methods will later be called in for example our TileEntity's NBT read and write methods. In a more advanced system you might also use the capability methods to call wrapper classes around FE and Tesla and other capability based energy systems in this class, but this is out of scope of this basic overview.

     

    At next, you should create a TileEntity.

    This TileEntity needs your EnergyManager class you created before (the class which extends EnergyStorage or implements IEnergyStorage ). 

    In here you add the read and write methods for NBT data and call the serializeNBT and deserializeNBT methods of your EnergyManager.

    For example:

    this.container.deserializeNBT(compound.getCompoundTag("Energy"));

    (container is here the instance of EnergyManager.)

     

    Then you also need the hasCapability and getCapability methods. I think you figure yourself out what to put in there.

    In the update method of your TileEntity you might want to put your logic about energy production or extraction in.

     

    this.container.receiveEnergy(int maxReceive, boolean simulate);
    this.container.extractEnergy(int maxExtract, boolean simulate);

     

    If you want to extract or receive energy from another TileEntity, you first need to get it at the specific location and than get the capability at the specific side. (Note: If a machine only sends energy in other machines underneath, then it might only return a capability for EnumFacing.DOWN.) An EnumFacing with null is also possible and might be used for internal purposes if I recall correctly.

     

    Do note that it is recommended for the most compatibility between mods to let your machines send the energy into the cables instead of letting the cables extract the energy out of your machines.

    https://github.com/SleepyTrousers/EnderIO/issues/4081#issuecomment-284911416

     

    I hope this gives you a basic overview of how energy systems actually work. For items, I currently having figured it out myself as I've not worked with items and capabilities so far and transmitting energy wirelessly shouldn't be a challange after reading this. The basics are always the same, just how much more you put around those basic is different.

    • Like 2
    • Thanks 1
  7. 15 hours ago, jabelar said:

    use a real profiler to time each part of your code

    As I've never used an profiler before, which one would you suggest to use?

    15 hours ago, jabelar said:

    printout actual timestamps

    I don't think this would work as I've got 66 blocks and even with less than those blocks, it would just be a mess to read the data out.

     

    15 hours ago, jabelar said:

    For example, you could randomly update each tile entity.

    This would not work for my part as my system is build upon the idea that a machine gets every tick its energy, like in other energy mods itself.

    But I have a system in place which might work a bit like this and this is the process to build up the energy network as it doesn't run every tick, it just checks if it needs to run every tick and if it

    doesn't then it stops there, otherwhise the whole data will be updated, like when a new block is placed and thus the network changed.

    15 hours ago, jabelar said:

    Overall though, pretty much every network will be hit a limit where performance doesn't scale well after a certain size.

    Yeah, that clear, but it shouldn't hit the limit with only 66 blocks.

  8. 15 minutes ago, OreCruncher said:

    Then your mod would have to send packets to clients when a player logs in.  Up to your mod to do that, assuming the client really needs your updated server side values.

    And here we've got a few problems:

    • How to send the data to the client in a way the performance doesn't drop when it's a lot of data?
    • How should the client or the server know if the data is different?
    • How should I stop the client from using the data that already comes with the mod instead of using the new server data?
    • Where to save the data on the client side? I mean the data is server specific like a resource pack can be server specific.
  9. Hi,

     

    I've got a problem with the code for my energy network. As soon as I build the system to be larger than a some really small value my FPS

    drops significantly from over 100 to 15-24fps.

    The size of the network I tested it currently was 11x6.

     

    Layer 1: 11x6 solar panels

    Layer 2: 11x6 transfer pipes

    Layer 3: 11x6 cables

    Layer 4: 1x1 transfer pipe

    Layer 5: 1x1 machine

     

    I don't know what exactly causes the problem, but it's not the machine block, as the energy doesn't even reach this block. 

    And from my 66 solar panels, the energy storage of 56 of them is full while the energy storage of the last 10 is empty and stays empty.

     

    public class TileEntityPipeTransferEnergy extends TileEntityEnergy {
        
        private boolean flag = false;
        private HashMap<BlockPos, EnumFacing> connected = new HashMap<>(); // without pre-calculated weight
        
        public boolean shouldRecalculate = false;
        
        public TileEntityPipeTransferEnergy() {
            super(10, 10);
            this.container.setTransferMode(EnergyTransfer.PRODUCER);
        }
        
        @Override
        public void update() {
            this.flag = false;
            if(this.shouldRecalculate || this.connected.isEmpty()) {
                this.shouldRecalculate = false;
                this.findTransferPipes();
                this.flag = true;
            }
            
            this.connected.forEach((pos, side) -> {
                final TileEntity tile = this.getWorld().getTileEntity(pos);
                if(tile == null) return;
                
                IEnergyStorage storage = tile.getCapability(CapabilityEnergy.ENERGY, side);
                if(storage == null)
                    storage = tile.getCapability(CapabilityEnergy.ENERGY, null);
                
                if(storage != null) {
                    storage.receiveEnergy(this.container.extractEnergy((int)(10 / this.connected.size()), false), false);
                    this.flag = true; // flag has to be a class member because of inner classes
                }
            });
            
            if(this.flag)
                this.markDirty();
        }
        
        private void findTransferPipes() {
            if(this.connected.size() > 0)
                this.connected.clear();
            
            // we want to store the facing and the position
            Queue<Pair<BlockPos, EnumFacing>> toSearch = new ArrayDeque<>();
            HashSet<BlockPos> scanned = new HashSet<BlockPos>();
            scanned.add(this.getPos());
            
            if(toSearch.isEmpty() || toSearch.peek() == null)
                this.getBlocksToScan(toSearch, scanned, this.getPos());
            
            // temp object because we poll stuff out of the list
            Pair<BlockPos, EnumFacing> pair = null;
            BlockPos current = null;
            EnumFacing face = null;
            
            while(toSearch.peek() != null) {
                pair = toSearch.poll();
                current = pair.getLeft();
                face = pair.getRight();
                scanned.add(current);
                
                if(this.getWorld().getTileEntity(current) instanceof TileEntityPipeEnergy) // check cable connection
                    this.getBlocksToScan(toSearch, scanned, current);
                else if(this.getWorld().getTileEntity(current) instanceof TileEntityPipeTransferEnergy &&
                        !this.getPos().equals(current) && !this.connected.containsKey(current))
                    this.connected.put(current, face); // found end of line
            }
        }
        
        private void getBlocksToScan(Queue<Pair<BlockPos, EnumFacing>> toSearch, HashSet<BlockPos> scanned, BlockPos pos) {
            // EnumSet.allOf(EnumFacing.class) does not include the null, so machines which don't require a specific
            // site will be ignored
            for(EnumFacing face : EnumFacing.VALUES) {
                // same as pos.north(), just for all directions
                BlockPos offset = pos.offset(face);
                if(!scanned.contains(offset))
                    // create a new pair and add it to the list
                    // a pair is just some class which holds two fields
                    toSearch.add(Pair.of(offset, face.getOpposite()));
            }
        }
        
        @Override
        public void readFromNBT(NBTTagCompound compound) {
            super.readFromNBT(compound);
        	if(compound.hasKey("connection")) {
                NBTTagList list = compound.getTagList("connection", Constants.NBT.TAG_COMPOUND);
                for(int i = 0; i <= list.tagCount(); i++) {
                    NBTTagCompound com = list.getCompoundTagAt(i);
                    if(com.hasKey("posX") && com.hasKey("posY") && com.hasKey("posZ")
                            && com.hasKey("facing")) {
                        
                        this.connected.put(
                                new BlockPos(
                                        com.getInteger("posX"),
                                        com.getInteger("posY"),
                                        com.getInteger("posZ")), 
                                EnumFacing.byName(com.getString("facing")));
                    }
                }
            }
        }
        
        @Override
        public NBTTagCompound writeToNBT(NBTTagCompound compound) {
        	NBTTagList list = new NBTTagList();
            this.connected.forEach((pos, side) -> {
                NBTTagCompound com = new NBTTagCompound();
                com.setInteger("posX", pos.getX());
                com.setInteger("posY", pos.getY());
                com.setInteger("posZ", pos.getZ());
                com.setString("facing", side.getName());
                list.appendTag(com);
            });
            compound.setTag("connection", list);
            
            return super.writeToNBT(compound);
        }
        
        @Override
        public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) {
            super.onDataPacket(net, pkt);
            this.readFromNBT(pkt.getNbtCompound());
        }
    }

     

    The code of the cable:

    public class TileEntityPipeEnergy extends TileEntity {
    	
        private HashSet<BlockPos> connected_machines = new HashSet<>();
        
        public void searchNetwork() {
            if(this.connected_machines.size() > 0)
                this.connected_machines.clear();
            
            ArrayDeque<BlockPos> toSearch = new ArrayDeque<>();
            HashSet<BlockPos> scanned = new HashSet<>();
            
            if(toSearch.isEmpty() || toSearch.peek() == null)
                this.getBlocksToScan(toSearch, scanned, this.getPos());
            
            BlockPos current = null;
            
            while(toSearch.peek() != null) {
                current = toSearch.pop();
                scanned.add(current);
                
                TileEntity tile = this.getWorld().getTileEntity(current);
                if(tile != null) {
                    if(tile instanceof TileEntityPipeEnergy)
                        this.getBlocksToScan(toSearch, scanned, current);
                    else if(tile instanceof TileEntityPipeTransferEnergy)
                        this.connected_machines.add(current);
                }
            }
        }
        
        private void getBlocksToScan(ArrayDeque<BlockPos> toSearch, HashSet<BlockPos> scanned, BlockPos pos) {
            for(EnumFacing face : EnumSet.allOf(EnumFacing.class)) {
                // same as pos.north(), just for all directions
                BlockPos offset = pos.offset(face);
                if(!scanned.contains(offset))
                    toSearch.add(offset);
            }
        }
        
        public void informNetwork() {
            this.connected_machines.forEach(pos -> {
                TileEntity tile = this.getWorld().getTileEntity(pos);
                // make sure the tile entity at 'pos' is really the one we think it is
                if(tile != null && tile instanceof TileEntityPipeTransferEnergy) {
                    TileEntityPipeTransferEnergy transfer = (TileEntityPipeTransferEnergy) tile;
                    transfer.shouldRecalculate = true;
                }
            });
        }
        
        @Override
        public NBTTagCompound writeToNBT(NBTTagCompound compound) {
            NBTTagList list = new NBTTagList();
            for(BlockPos pos : this.connected_machines) {
                NBTTagCompound com = new NBTTagCompound();
                com.setInteger("posX", pos.getX());
                com.setInteger("posY", pos.getY());
                com.setInteger("posZ", pos.getZ());
                list.appendTag(com);
            }
            compound.setTag("machines", list);
            
            return super.writeToNBT(compound);
        }
        
        @Override
        public void readFromNBT(NBTTagCompound compound) {
            super.readFromNBT(compound);
            
            if(compound.hasKey("machines")) {
                NBTTagList list = compound.getTagList("machines", Constants.NBT.TAG_COMPOUND);
                for(int i = 0; i <= list.tagCount(); i++) {
                    NBTTagCompound com = list.getCompoundTagAt(i);
                    if(com.hasKey("posX") && com.hasKey("posY") && com.hasKey("posZ")) {
                        
                        this.connected_machines.add(
                                new BlockPos(
                                        com.getInteger("posX"),
                                        com.getInteger("posY"),
                                        com.getInteger("posZ"))
                                );
                    }
                }
            }
        }
    }

     

    The code of my solar panel:

    public class TileEntitySolarPanel extends TileEntityEnergy {
        
    [...]
        
        @Override
        public void update() {
            super.update();
          
          [...]
            
            this.container.receiveEnergy(this.inputRate, false);
            
            if(this.world.getTileEntity(this.getPos().down()) != null) {
                int received = sendEnergy(this, this.world.getTileEntity(this.getPos().down()), EnumFacing.DOWN, 10);
                if(received > 0) flag = true;
            }
            
            if(flag) {
                this.getWorld().notifyBlockUpdate(this.getPos(), this.getWorld().getBlockState(this.getPos()), this.getWorld().getBlockState(this.getPos()), 3);
                this.markDirty();
            }
        }
      
        public static int sendEnergy(@Nullable TileEntityEnergy from, @Nullable TileEntity sendTo, EnumFacing orientation, int transferAmount) {
            if(from == null || sendTo == null)
                return 0;
            
            EnumFacing side = orientation.getOpposite();
    
            if(sendTo.hasCapability(CapabilityEnergy.ENERGY, side)) { // Forge Energy Support
                IEnergyStorage storage = sendTo.getCapability(CapabilityEnergy.ENERGY, side);
                if(storage != null && storage.canReceive() && from.container.canExtract())
                    return storage.receiveEnergy(from.container.extractEnergy(transferAmount, false), false);
            }
            return 0;
        }
        
        @Override
        public SPacketUpdateTileEntity getUpdatePacket() {
            return new SPacketUpdateTileEntity(this.getPos(), 3, this.getUpdateTag());
        }
        
        @Override
        public NBTTagCompound getUpdateTag() {
            return this.writeToNBT(new NBTTagCompound());
        }
        
        @Override
        public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) {
            super.onDataPacket(net, pkt);
            this.handleUpdateTag(pkt.getNbtCompound());
        }
    }

     

    The debug mode of eclipse also showed me the information that in TileEntityPipeTransferEnergy the shouldRecalculate value is set to false and the connected list also contains data.

     

    So it must have something to do with the energy transfer from one block to anther, thought I'm not sure what could cause the problem there. 

    I just suspect that my solar panels are transfering the energy between themselves instead of putting it into the machine, but I don't see why this would cause

    a significantly fps drop.

     

    Also to note: for rendering I'm just using a default json file with a texture. So nothing really special about it. The RAM stays with about 1.5GB of 3GB available and the chunk updates are above 200.

     

    Thx in advance.

    Bektor

     

    EDIT: further note: I had no problems before having so many solar panels, so I think its a problem with the cable network

  10. 8 minutes ago, OreCruncher said:

    Uh, this isn't related to writing mods.  I suggest you read the documentation for the various mods you use in your pack.  A lot of them provide configuration settings to do specifically what you want, or provide integration with systems such as Minetweaker/Crafttweaker.

    This is related to writing mods as I want my mod to have this feature, as I want for balancing and thus stuff my mod to sync data between the server and the client so that when I have for example a config file or a json file or whatever file I might want to use, I can change on the server just this file and everyone will be able to play with the new balanced variables instead of the old ones and the old ones are only used on the client when the client is not conntected to my server.

  11. If I would guess, I would say that there is a problem finding OpenGL drivers which could be caused by out-of date drivers or

    something is wrong with the drivers. I would recommend uninstall the driver with something like DDU to make sure everything is removed

    uppon uninstallation and then install it again.

    Downgrading Java should definitly not solve the problem. Also since Mojang enforces Java 8 with Minecraft 1.12, you shouldn't be

    able to start Minecraft with Java 7/6 or lower.

  12. Hi,

     

    if it is possible using Forge to change values server side only, for example to allow a server owner to change how much energy a machine produces or consumes

    without the need of a update of the mod itself on the client side.

     

    Basically the server owner should be able to change specific values and the client uses the values from the server instead of it's own values.

     

    My problem there is:

    1. I don't know if this is possible with the current available systems in Forge/Minecraft.
    2. I don't know how to do it, except for that at some point the server has to send all the data to the client.

     

    Thx in advance.

    Bektor

  13. Hi,

     

    I am currently working on implementing a chunk loader to my mod, thought I've got little to none idea how to do this.

     

    How the chunk loader should work:

    • if placed by player, create chunk loader for player as long as the limit per player wasn't reached
    • otherwhise let the mod manage the chunk loader until the limit is reached (which should be equal to the limit of chunk loaders per player)
    • if the limit is reached, send a message to the placer informing him about the reached limit

     

    This is the code I've got so far:

    public class TileEntityChunkLoader extends TileEntityEnergy {
        
        private final String UUID_TAG = "UUID";
        
        private final int consume;
        private boolean isActive = true;
        
        private ForgeChunkManager.Ticket ticket;
        
        private UUID ownerId = null;
        
        public TileEntityChunkLoader(int capacity, int maxTransfer) {
            super(capacity, maxTransfer, false);
            
            this.consume = maxTransfer;
            this.getEnergyManager().setTransferMode(EnergyTransfer.CONSUMER);
        }
        
        @Override
        public void update() {
            super.update();
            
            if(!this.hasWorld() || this.getWorld().isRemote)
                return;
            
            this.isActive = EnergyUtils.consumeEnergy(this, this.consume);
            
            if(isActive) {
                startChunkLoading();
                
                this.getWorld().notifyBlockUpdate(this.getPos(), this.getWorld().getBlockState(this.getPos()), this.getWorld().getBlockState(this.getPos()), 3);
                this.markDirty();
            }
        }
        
        private void startChunkLoading() {
            if(this.ticket == null) {
                if(this.ownerId != null)
                    this.ticket = ForgeChunkManager.requestPlayerTicket(Constants.MOD_ID, this.ownerId.toString(), this.getWorld(), Type.NORMAL);
                else
                    this.ticket = ForgeChunkManager.requestTicket(Constants.MOD_ID, this.getWorld(), Type.NORMAL);
            }
            
            if(this.ticket == null) {
                JustAnotherEnergy.LOGGER.error("Ticket creation for the chunk loader at: [" + this.getPos().getX() + ", " + this.getPos().getY() + ", " + this.getPos().getZ() + "] failed!");
                return;
            }
        }
    
        @Override
        public SPacketUpdateTileEntity getUpdatePacket() {
            return new SPacketUpdateTileEntity(this.getPos(), 3, this.getUpdateTag());
        }
        
        @Override
        public NBTTagCompound getUpdateTag() {
            return this.writeToNBT(new NBTTagCompound());
        }
        
        @Override
        public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) {
            super.onDataPacket(net, pkt);
            this.handleUpdateTag(pkt.getNbtCompound());
        }
        
        @Override
        public void readFromNBT(NBTTagCompound compound) {
            this.ownerId = compound.getUniqueId(this.UUID_TAG);
            
            super.readFromNBT(compound);
        }
        
        @Override
        public NBTTagCompound writeToNBT(NBTTagCompound compound) {
            compound.setUniqueId(this.UUID_TAG, this.ownerId);
            
            return super.writeToNBT(compound);
        }
    public class BlockChunkLoader extends Block implements ITileEntityProvider {
        
        public static PropertyEnum<ChunkLoaderTypes> META_PROPERTY = PropertyEnum.create("loader", ChunkLoaderTypes.class);
        
        public BlockChunkLoader() {
            super(Material.IRON);
            this.setDefaultState(this.blockState.getBaseState().withProperty(META_PROPERTY, ChunkLoaderTypes.LOAD_01));
        }
        
        @Override
        public void onBlockPlacedBy(World worldIn, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack) {
            if(worldIn.isRemote)
                return;
            
            TileEntity tile = worldIn.getTileEntity(pos);
            if(tile instanceof TileEntityChunkLoader) {
                TileEntityChunkLoader tileCL = (TileEntityChunkLoader) tile;
                
                if(placer instanceof EntityPlayer) {
                    EntityPlayer player = (EntityPlayer) placer;
                    tileCL.setOwnerId(player.getGameProfile().getId());
                }
            }
        }
        
        @Override
        public void breakBlock(World worldIn, BlockPos pos, IBlockState state) {
            TileEntity tile = worldIn.getTileEntity(pos);
            if(tile instanceof TileEntityChunkLoader) {
                TileEntityChunkLoader tileCL = (TileEntityChunkLoader) tile;
                
            }
            
            super.breakBlock(worldIn, pos, state);
        }
        
        @Override
        public TileEntity createNewTileEntity(World worldIn, int meta) {
            switch(meta) {
                case 0: return new TileEntityChunkLoader(500, 100); 
                case 1: return new TileEntityChunkLoader(4500, 900);
                case 2: return new TileEntityChunkLoader(125000, 25000);
                default: return new TileEntityChunkLoader(500, 100);   
            }
        }
        
        @Override
        public void getSubBlocks(Item itemIn, CreativeTabs tab, List<ItemStack> list) {
            for(final ChunkLoaderTypes type : ChunkLoaderTypes.values())
                list.add(new ItemStack(this, 1, type.getID()));
        }
        
        @Override
        public int damageDropped(IBlockState state) {
            return this.getMetaFromState(state);
        }
        
        @Override
        protected BlockStateContainer createBlockState() {
            return new BlockStateContainer(this, META_PROPERTY);
        }
        
        @Override
        public int getMetaFromState(IBlockState state) {
            return state.getValue(META_PROPERTY).getID();
        }
        
        @Override
        public IBlockState getStateFromMeta(int meta) {
            return this.getDefaultState().withProperty(META_PROPERTY, ChunkLoaderTypes.byMetadata(meta));
        }
    }

     

    As can be seen in the block class, the amount of energy consumption is higher when more chunks should be loaded (radius around the chunk loader including it's chunk, can also be just the chunk of the chunk loader)

     

    My problem now is that I am stuck at achieving this. I am also wondering what the difference between an Entity and a Normal ChunkLoader as seen in requestTicket is.

     

    Thx in advance.

    Bektor

  14. 3 minutes ago, V0idWa1k3r said:
    11 minutes ago, Bektor said:

    int guiStartX = (sRes.getScaledWidth() - 16) / 2;

    int guiStartY = (sRes.getScaledHeight() - 51) / 2;

     

    Fixed with changing the first 51 parameter from my method to 0. The ScaledResolution thing also solved problems with disappering textures when changing the screen size, which I didn't even noticed before. :) 

     

    3 minutes ago, V0idWa1k3r said:

    EDIT: even though you've fixed it I am still curious as to what the issue was exactly. Was it a texture issue? 

    I suppose it was an issue with missing ScaledResolution and me having the reading direction for OpenGL texture coordinates wrong in my head.

  15. 19 minutes ago, V0idWa1k3r said:

    Hm, interesting. Can you please elaborate on the word 'gone' in your scenario? I have debugged your code and had no issues with it - apart from the fact that it is rendered across the entire screen and the UVs point at a relatively small region of the image.

     

    Ok, fixed it with changing the first 51 parameter from my method to 0.

     

    19 minutes ago, V0idWa1k3r said:

    Here is my test sample:

    Hm, I'm wondering what this invoke method is.

  16. 17 minutes ago, V0idWa1k3r said:

    Your textureX/Y are of an integer type. So is 1. And so is 256. Dividing an integer 1 by an integer 256 prouces you 0 and well, anything *0 is 0.

     

    Always forget this one thing, but it didn't solve the problem. The texture is still gone:

    	    glTexCoord2f(textureX * (1.f / 256.f), textureY * (1.f / 256.f));
    	    glVertex3i(0, 0, 0);
    	    
    	    glTexCoord2f(textureX * (1.f / 256.f), (textureY + height) * (1.f / 256.f));
    	    glVertex3i(0, height + 400, 0);
    	    
    	    glTexCoord2f((textureX + width) * (1.f / 256.f), (textureY + height) * (1.f / 256.f));
    	    glVertex3i(width + 800, height + 400, 0);
    	    
    	    glTexCoord2f((textureX + width) * (1.f / 256.f), textureY * (1.f / 256.f));
    	    glVertex3i(width + 800, 0, 0);

    Hope I didn't forgot a small thing again, thought somehow I hope I did as it would solve the problem faster. ^^

     

    17 minutes ago, V0idWa1k3r said:

    Why do people create abstractions to begin with? Let's all write our code in assembly! That is sure going to be fun! :P

    Assembly makes fun. You can even enforce a PC configuration with this as it won't run anywhere else. xD But why using Assembly, binary with its 01010101 is even better. :P 

     

    17 minutes ago, V0idWa1k3r said:

    And yet a lot of people had issues with MC starting to require OpenGL 3.3. I remember having one myself on a linux machine at work due to the way mesa drivers are done.

    I don't know much about Mesa drivers, except that they got a somewhat stable OpenGL 4.5 support, but why not using official drivers?

    And I don't think Minecraft uses OpenGL 3.3, the last version I know for sure they are using is OpenGL 2.1.

  17. 51 minutes ago, V0idWa1k3r said:

    Let's look at Gui::drawTexturedModalRect. Specifically at it signature:

    public void drawTexturedModalRect(int x, int y, int textureX, int textureY, int width, int height)

    The parameters I've marked as bold are to be processed into UVs in the method. textureX/Y are the start, and textureX/Y + width/height are the end. How are they processed? Well, let's look into that method. You will quickly notice that the results of manipulations with these values are multiplied by 0.00390625F. What is this magical number? It is 1/256. MC assumes the height/width of a GUI icon to be 256x256 and does calculations based on that assumption. If the texture is bigger in size(say via a resourcepack) it actually does not matter as the range of [0-1] is effectively a percentage and if the code is based around a 256x256 assumption(the variables passed to this method, that is) the end-result percentages are still going to be correct regardless of the texture width/height. 

    Changing my code to multiply the texture values with 1/256 just let's the complete image disappear.

            glTexCoord2f(textureX * (1/256), textureY * (1/256));
    	    glVertex3i(0, 0, 0);
    	    
    	    glTexCoord2f(textureX * (1/256), (textureY + height) * (1/256));
    	    glVertex3i(0, height + 400, 0);
    	    
    	    glTexCoord2f((textureX + width) * (1/256), (textureY + height) * (1/256));
    	    glVertex3i(width + 800, height + 400, 0);
    	    
    	    glTexCoord2f((textureX + width) * (1/256), textureY * (1/256));
    	    glVertex3i(width + 800, 0, 0);

     

    51 minutes ago, V0idWa1k3r said:

    It is somewhat of a complex yet very flexible wrapper around GL 3.0+ rendering.

    Hm, why the hell creates Mojang wrappers around GL 3.0+ rendering instead of using it direclty. I mean, I don't know any graphics card today which doesn't support OpenGL 3.0+, even NVIDIAs GTX 400 series supports OpenGL 4.6 with the newest beta drivers and 4.5 with the newest stable drivers.

  18. 8 hours ago, V0idWa1k3r said:

    Well, where are you expecting to see your quad? We can't tell what is wrong with the UI positioning just by looking at the code - although I can take a guess: you should subtract your position offsets after you've found the center of the screen.

     

    Well, I want to place it in the middle of the screen.

    8 hours ago, Abastro said:

    Also, why don't use make use of x and y?

    Well, I used them, thought it didn't went that good. Couldn't see the image anymore, so I started fixing the image first to get it properly drawn as one big image is easier to see then thousands of small images.

     

    9 hours ago, V0idWa1k3r said:

    I still suggest using BufferBuilder/VertexBuffer rather than OpenGL directly. There is a reason MC switched to it entirely. And well, some people might assume that all drawing is indeed done using methods from that class and abuse that.

    I don't see any reason why BufferBuilder/VertexBuffer is the prefered way, especially when I do not know how it is connected to the hole rendering system and how those two classes work internally.

     

    9 hours ago, V0idWa1k3r said:

    UVs must be within a range of [0-1] and you are passing a range of [0-16] x [0-51]. That won't work well. By default all MC textures have their wrap S/T set to repeat, and that's why you see "thousands of little textures" - it is actually your texture repeated 16 times on the x axis and 51 times on the y axis.

     

    Hm, now I am wondering, where does MC the conversion? I mean, when calling drawTexturedModalRect I don't think I ever passed the width of the complete image to draw, only the position and width and height of the part of the image to be drawn nor is the texture passed.

  19. 1 minute ago, Draco18s said:

    Where are you doing this, then?

     

    RenderGameOverlayEvent.Post calls a custom method which would then execute the code for specific blocks.

     

    2 minutes ago, Draco18s said:

    , you're not binding a texture.

    I'm binding the texture directly above the first line of code I posted. ^^

  20. Hi,

     

    I've got a little problem with my OpenGL code while drawing a rectangular texture:

    • thousands of little textures will be drawn instead of one texture
    • the texture to display is part of a larger texture, but the wrong part of this huge "spritesheet" is drawn
    • those thousands of little textures are drawn on the wrong place of the screen

     

    • `guiStartX` and `guiStartY`: the x and y positions where the GUI should start
    • `textureX` and `textureY`: the position in the "spritesheet" where the texture can be found
    • `width` and `height`: the width and height of the texture to draw

     

     

    mc.getTextureManager().bindTexture(UI_PROGRESS_TEXTURE);
    int guiStartX = (mc.displayWidth - 16) / 2;
    	int guiStartY = (mc.displayHeight - 51) / 2;
    	renderTexturedRect(guiStartX, guiStartY, 176, 51, 16, 51);
    
    renderTexturedRect(int x, int y, int textureX, int textureY, int width, int height) {
    	glBegin(GL_QUADS);
    	    glTexCoord2f(textureX, textureY);
    	    glVertex3i(0, 0, 0);
    	    
    	    glTexCoord2f(textureX, textureY + height);
    	    glVertex3i(0, height + 400, 0);
    	    
    	    glTexCoord2f(textureX + width, textureY + height);
    	    glVertex3i(width + 800, height + 400, 0);
    	    
    	    glTexCoord2f(textureX + width, textureY);
    	    glVertex3i(width + 800, 0, 0);
    	    glEnd();

     

    1. One may ask, why not using drawTexturedModalRect from Minecraft itself. The Reason for this is that it is not available in the place where I am drawing.

    2. And why am I using OpenGL directly instead of for example looking into how drawTexturedModalRect renders a rectangle and doing it the same way?:

      The answer to this is that I am rather using OpenGL directly instead of using undocumented code where I do not even know what it is doing and how the stuff is getting onto the screen with using this (like where and which OpenGL calls are being called etc.)

     

    Note: I am not familiar with OpenGL 2.1 nor with LWJGL 2 as I've only worked with OpenGL 4.5 (in C++) and it's shaders so far (and very little with LWJGL 3).

     

    Thx in advance.

    Bektor

×
×
  • Create New...

Important Information

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