Jump to content

SOLVED - Quick Question - 1.8 - saving client side data


BioAndy

Recommended Posts

Good Afternoon Everyone!

 

So I have been writing my first mod for Minecraft using Forge 1.8.

My son comes up with the idea and I help him implement it. He's only 9, but really interested in software development and I think this is a good way for him to experience it and gives us some fun times together.

 

I have worked with c++ for the past 15 years in a professional setting, so if you can help with my problem, pointing me in the right direction should be good enough. I highly doubt I would need full code examples, and really dislike going through other peoples code anyway! ;D

 

The problem - Saving state information between sessions.

Example - Lets say I have a simple GUI (no inventory attached and client side only) that when opened lets the user re-position an overlay onto the screen. Now this overlay needs to have it's X and Y coords. saved between sessions.

 

I'm just not really sure where I would be saving this information ->

Config File - From what I've read it's mostly for Block and Item ID's.

NBTData - Seems like overkill to save what little information I need saving in here.

 

Now I have only been digging through the Forge API for a couple days now, so I'm sure that I am missing something.

 

Questions?

Am I missing some other way Forge easily enables the saving of client side data?

With something as generic as x and y coords. that is not an item, block, or entity do I just need to write my own input / output handler that does what i need and saves it in my mod folder?

 

I guess before writing my own, I'm just wondering if Forge already implements an easy to use Interface for this.

 

Thanks everyone and I'm looking forward to being a part of the Minecraft modding community!

Link to comment
Share on other sites

I'd say big NOPE to NBT here.

 

First of all - NBT is a compression tool designed to save "data" not configuration, ESPECIALLY not Client configs (World saving, player saving, it's all server-side).

 

If you are planning on doing this all on Client I'd suggest using Forge Configuration. It's not just for blocks and items as you read. You can have there pretty much everything. Calling "save" can be done pretty much everywhere. Just keep in mind that is Client config - you should load it from client proxy, what use would it be on server. ;p

 

Anyway, if you would use e.g GuiScreen you will probably want to call config.save() from ActionPerformed (just don't do it per-render, that would be as much as your FPS) of some kind, that I'll leave to you.

 

Here's example of not-block-or-item config:

public class ConfigGeneral
{
public static Configuration config;

public static void init(File file)
{
	config = new Configuration(file, true);
	try
	{
		config.load();
		config.setCategoryComment("General", ServerReference.GENERAL_CONFIG_COMMENT);

		Registry.setPointsPerLevel(config.get("General", "PointsPerLevel", 10).getInt());

		for (String attribute : config.get("General", "BaseAttributes", new String[] {"B10.0", "A10.0", "I10.0", "V10.0", "T10.0"}, "Shared player basic attributes.").getStringList())
		{
			Registry.appendBaseAttribute(Attribute.decode(attribute));
		}
	}
	catch (Exception e)
	{
		Main.log.log(Level.ERROR, "Could not properly load General config file!");
	}
	finally
	{
		if (config.hasChanged()) config.save();
	}
}
}

This one is actually server-side, loads some values which are then used in construction of player or leveling.

 

Whole Configuration is pretty well documented.

 

Second idea would to totally leave forge for a while and use custom system, or as suggested NBT, but on client side. Your choice.

1.7.10 is no longer supported by forge, you are on your own.

Link to comment
Share on other sites

Hi

 

>Config File - From what I've read it's mostly for Block and Item ID's.

 

Not anymore :)  Block and Item IDs went out the window around 1.6

 

I think the key is - if you want the storage to be local on the client's machine, completely independent of the server, and to carry over from one world to another, then use a config file.

 

Otherwise - I'd suggest to store it on the server using per-player storage, with NBT.  Never had to do that, but I have heard talk of IExtendedEntityProperties.

http://www.minecraftforge.net/forum/index.php?topic=6999.0

 

Much easier than trying to roll your own.

 

-TGG

Link to comment
Share on other sites

Thanks for all the replies everyone. I will edit my post and mark it solved.

 

I ended up using config files. I was just worried about storing information in them that should not be stored in them.

NBT just seemed like overkill for my purposes.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • KILAT77 : Waspada Situs Scam dengan Withdraw Yang Tidak Dibayar Di era digital saat ini, banyak orang yang mencari keberuntungan melalui situs perjudian online. Namun, dibalik gemerlap janji-janji manis kemenangan, ada bahaya yang mengintai. Salah satu situs yang patut diwaspadai adalah KILAT77. Situs ini mendapat reputasi buruk karena banyak laporan dari pengguna yang mengklaim bahwa mereka tidak bisa menarik dana kemenangan mereka. Dalam artikel ini, kita akan membahas mengapa ROTER88 dianggap sebagai situs scam dan bagaimana Anda bisa melindungi diri dari penipuan serupa. Pengalaman Pengguna: Penarikan Tidak Dibayar Beberapa pengguna telah melaporkan pengalaman buruk mereka dengan KILAT77. Mereka mengaku bahwa setelah memenangkan sejumlah uang dan mencoba menariknya, proses penarikan mereka ditolak tanpa alasan yang jelas. Bahkan, beberapa pengguna melaporkan bahwa akun mereka tiba-tiba diblokir setelah mencoba melakukan penarikan, sehingga mereka kehilangan akses ke dana mereka sama sekali.
    • It is a dupe mod issue Remove Rubidium - you are already using Embeddium which is a fork of Rubidium
    • I made a block entity in forge 1.20.1, I want to prevent hopper from taking input slot item, i tried to override the extractItem method, it prevented hopper from taking input slot item, but the player also unable to take/change the item in input slot unless the slot is empty. public class FluidSeparatorBlockEntity extends BlockEntity implements MenuProvider { private static final int INPUT_SLOT = 0; private final CustomItemHandler itemHandler = new CustomItemHandler(3){ @Override protected void onContentsChanged(int slot) { setChanged(); } @Override public boolean isItemValid(int slot, @NotNull ItemStack stack) { return slot == INPUT_SLOT; } @Override public @NotNull ItemStack extractItem(int slot, int amount, boolean simulate) { if (slot == INPUT_SLOT) { return ItemStack.EMPTY; } return super.extractItem(slot, amount, simulate); } }; private LazyOptional<IItemHandler> lazyItemHandler = LazyOptional.empty(); protected final ContainerData data; private int progress = 0; private int maxProgress = 78; public FluidSeparatorBlockEntity(BlockPos pPos, BlockState pBlockState) { super(ModBlockEntities.FLUID_SEPARATOR_BE.get(), pPos, pBlockState); this.data = new ContainerData() { @Override public int get(int pIndex) { return switch (pIndex) { case 0 -> FluidSeparatorBlockEntity.this.progress; case 1, 2 -> FluidSeparatorBlockEntity.this.maxProgress; default -> 0; }; } @Override public void set(int pIndex, int pValue) { switch (pIndex) { case 0 -> FluidSeparatorBlockEntity.this.progress = pValue; case 1, 2 -> FluidSeparatorBlockEntity.this.maxProgress = pValue; } } @Override public int getCount() { return 3; } }; } @Override public @NotNull <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) { if(cap == ForgeCapabilities.ITEM_HANDLER) { return lazyItemHandler.cast(); } return super.getCapability(cap, side); } @Override public void onLoad() { super.onLoad(); lazyItemHandler = LazyOptional.of(() -> itemHandler); } @Override public void invalidateCaps() { super.invalidateCaps(); lazyItemHandler.invalidate(); } public void drops() { SimpleContainer inventory = new SimpleContainer(itemHandler.getSlots()); for(int i = 0; i < itemHandler.getSlots(); i++) { inventory.setItem(i, itemHandler.getStackInSlot(i)); } Containers.dropContents(this.level, this.worldPosition, inventory); } @Override public Component getDisplayName() { return Component.translatable("block.chemmaster.fluid_separator"); } @Nullable @Override public AbstractContainerMenu createMenu(int pContainerId, Inventory pPlayerInventory, Player pPlayer) { return new FluidSeparatorMenu(pContainerId, pPlayerInventory, this, this.data); } @Override protected void saveAdditional(CompoundTag pTag) { pTag.put("inventory", itemHandler.serializeNBT()); pTag.putInt("fluid_separator.progress", progress); super.saveAdditional(pTag); } @Override public void load(CompoundTag pTag) { super.load(pTag); itemHandler.deserializeNBT(pTag.getCompound("inventory")); progress = pTag.getInt("fluid_separator.progress"); } public void tick(Level pLevel, BlockPos pPos, BlockState pState) { ItemStack inputStack = this.itemHandler.getStackInSlot(INPUT_SLOT); if (inputStack.getCount() < 2) { resetProgress(); return; } if(hasRecipe()) { increaseCraftingProgress(); setChanged(pLevel, pPos, pState); if(hasProgressFinished()) { craftItem(); resetProgress(); } } else { resetProgress(); } } private void resetProgress() { progress = 0; } private void craftItem() { Optional<FluidSeparatingRecipe> recipe = getCurrentRecipe(); if (recipe.isPresent()) { List<ItemStack> results = recipe.get().getOutputs(); ItemStack inputStack = this.itemHandler.getStackInSlot(INPUT_SLOT); if (inputStack.getCount() < 2) { // If there are not enough items, do not proceed with crafting return; } // Extract the input item from the input slot this.itemHandler.internalExtractItem(INPUT_SLOT, 2, false); // Loop through each result item and find suitable output slots for (ItemStack result : results) { int outputSlot = findSuitableOutputSlot(result); if (outputSlot != -1) { this.itemHandler.setStackInSlot(outputSlot, new ItemStack(result.getItem(), this.itemHandler.getStackInSlot(outputSlot).getCount() + result.getCount())); } else { // Handle the case where no suitable output slot is found // This can be logging an error, throwing an exception, or any other handling logic System.err.println("No suitable output slot found for item: " + result); } } } } private int findSuitableOutputSlot(ItemStack result) { // Implement logic to find a suitable output slot for the given result // Return the slot index or -1 if no suitable slot is found for (int i = 0; i < this.itemHandler.getSlots(); i++) { // Ensure we do not place the output item in the input slot if (i == INPUT_SLOT) { continue; } ItemStack stackInSlot = this.itemHandler.getStackInSlot(i); if (stackInSlot.isEmpty() || (stackInSlot.getItem() == result.getItem() && stackInSlot.getCount() + result.getCount() <= stackInSlot.getMaxStackSize())) { return i; } } return -1; } private boolean hasRecipe() { Optional<FluidSeparatingRecipe> recipe = getCurrentRecipe(); if (recipe.isEmpty()) { return false; } List<ItemStack> results = recipe.get().getOutputs(); for (ItemStack result : results) { if (!canInsertAmountIntoOutputSlot(result) || !canInsertItemIntoOutputSlot(result.getItem())) { return false; } } return true; } private Optional<FluidSeparatingRecipe> getCurrentRecipe(){ SimpleContainer inventory = new SimpleContainer(this.itemHandler.getSlots()); for (int i = 0; i < itemHandler.getSlots(); i++) { inventory.setItem(i, this.itemHandler.getStackInSlot(i)); } return this.level.getRecipeManager().getRecipeFor(FluidSeparatingRecipe.Type.INSTANCE, inventory, level); } private boolean canInsertAmountIntoOutputSlot(ItemStack result) { for (int i = 1; i < this.itemHandler.getSlots(); i++) { ItemStack stackInSlot = this.itemHandler.getStackInSlot(i); if (stackInSlot.isEmpty() || (stackInSlot.getItem() == result.getItem() && stackInSlot.getCount() + result.getCount() <= stackInSlot.getMaxStackSize())) { return true; } } return false; } private boolean canInsertItemIntoOutputSlot(Item item) { for (int i = 1; i < this.itemHandler.getSlots(); i++) { ItemStack stackInSlot = this.itemHandler.getStackInSlot(i); if (stackInSlot.isEmpty() || stackInSlot.getItem() == item) { return true; } } return false; } private boolean hasProgressFinished() { return progress >= maxProgress; } private void increaseCraftingProgress() { progress++; } }  
    • No dice. Unfortunately this fix didn't work, thank you though.
  • Topics

×
×
  • Create New...

Important Information

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