Jump to content

[1.8] Two-way scroll bars


Earthcomputer

Recommended Posts

I'm making a client-side mod which requires a GUI which may be bigger that the physical screen size. I've looked at the vanilla code for scroll bars (in GuiSlot), but I don't understand it. How would I implement a two-way scroll bar such that:

  • Everything drawn on the screen is translated (shifted) in the opposite direction to the scroll bars
  • The mouse co-ordinates are translated in the same direction as the scroll bars, so it is easy to determine what was clicked

Are there examples of code that can already to this? If not, how would I go about implementing it?

 

Thanks in advance for help.

 

Edit: okay, I've made a start. It's just the commented parts of drawScreen I need help with now.

 

 

 


public class GuiTwoWayScroll extends GuiScreen {

private static final Method keyTypedMethod = ReflectionHelper.findMethod(GuiScreen.class, null,
		new String[] { "func_73869_a", "keyTyped" }, char.class, int.class);
private static final Method mouseClickedMethod = ReflectionHelper.findMethod(GuiScreen.class, null,
		new String[] { "func_73864_a", "mouseClicked" }, int.class, int.class, int.class);
private static final Method mouseReleasedMethod = ReflectionHelper.findMethod(GuiScreen.class, null,
		new String[] { "func_146286_b", "mouseReleased" }, int.class, int.class, int.class);
private static final Method mouseClickMoveMethod = ReflectionHelper.findMethod(GuiScreen.class, null,
		new String[] { "func_146273_a", "mouseClickMove" }, int.class, int.class, int.class, long.class);
private static final Method actionPerformedMethod = ReflectionHelper.findMethod(GuiScreen.class, null,
		new String[] { "func_146284_a", "actionPerformed" }, GuiButton.class);

protected float amtScrolledX;
protected float amtScrolledY;
protected GuiScreen child;

public GuiTwoWayScroll(GuiScreen child) {
	this.child = child;
}

@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
	// translate(amtScrolledX, amtScrolledY);
	child.drawScreen(mouseX + (int) amtScrolledX, mouseY + (int) amtScrolledY, partialTicks);
	// translate(-amtScrolledX, -amtScrolledY);
	// drawScrollBars();
}

@Override
protected void keyTyped(char typedChar, int keyCode) throws IOException {
	invokeSilently(keyTypedMethod, child, typedChar, keyCode);
}

@Override
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException {
	invokeSilently(mouseClickedMethod, child, mouseX + (int) amtScrolledX, mouseY + (int) amtScrolledY,
			mouseButton);
}

@Override
protected void mouseReleased(int mouseX, int mouseY, int state) {
	invokeSilently(mouseReleasedMethod, child, mouseX + (int) amtScrolledX, mouseY + (int) amtScrolledY, state);
}

@Override
protected void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) {
	invokeSilently(mouseClickMoveMethod, child, mouseX + (int) amtScrolledX, mouseY + (int) amtScrolledY,
			clickedMouseButton, timeSinceLastClick);
}

@Override
protected void actionPerformed(GuiButton button) throws IOException {
	invokeSilently(actionPerformedMethod, child, button);
}

@Override
public void initGui() {
	child.initGui();
}

@Override
public void updateScreen() {
	child.updateScreen();
}

@Override
public void onGuiClosed() {
	child.onGuiClosed();
}

@Override
public void drawDefaultBackground() {
	child.drawDefaultBackground();
}

@Override
public void drawWorldBackground(int tint) {
	child.drawWorldBackground(tint);
}

@Override
public void drawBackground(int tint) {
	child.drawBackground(tint);
}

@Override
public boolean doesGuiPauseGame() {
	return child.doesGuiPauseGame();
}

@Override
public void confirmClicked(boolean result, int id) {
	child.confirmClicked(result, id);
}

private static Object invokeSilently(Method method, Object instance, Object... args) {
	Exception exception = null;
	try {
		return method.invoke(instance, args);
	} catch (IllegalAccessException e) {
		exception = e;
	} catch (IllegalArgumentException e) {
		exception = e;
	} catch (InvocationTargetException e) {
		exception = e;
	}
	throw new ReportedException(CrashReport.makeCrashReport(exception, "Doing reflection"));
}

}

 

 

catch(Exception e)

{

 

}

Yay, Pokémon exception handling, gotta catch 'em all (and then do nothing with 'em).

Link to comment
Share on other sites

Well, after a week of playing around with this, I think I've got a working solution which works for me (with just one small bug):

 

 

package net.earthcomputer.easyeditors.gui;

import java.io.IOException;

import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.GL11;

import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.util.MathHelper;

public abstract class GuiTwoWayScroll extends GuiScreen {

private int headerHeight;
private int footerHeight;
private int virtualWidth;
private int virtualHeight;
private int scrollX;
private int scrollY;

private int shownWidth;
private int shownHeight;
private int maxScrollX;
private int maxScrollY;
private int xScrollBarWidth;
private int yScrollBarHeight;
private int xScrollBarLeft;
private int yScrollBarTop;
private int xScrollBarRight;
private int yScrollBarBottom;
private boolean xScrollBarVisible;
private boolean yScrollBarVisible;

private int firstXScrollBarPos = -1;
private int firstYScrollBarPos = -1;
private int firstMouseX = -1;
private int firstMouseY = -1;
private boolean leftButtonDown;

private int leftKey;
private int rightKey;
private int upKey;
private int downKey;
private boolean useMouseWheel = true;
private int xScrollBarPolicy;
private int yScrollBarPolicy;

public static final int SHOWN_NEVER = 0;
public static final int SHOWN_WHEN_NEEDED = 1;
public static final int SHOWN_ALWAYS = 2;

public GuiTwoWayScroll(int headerHeight, int footerHeight, int virtualWidth, int virtualHeight) {
	this.headerHeight = headerHeight;
	this.footerHeight = footerHeight;
	this.virtualWidth = virtualWidth;
	this.virtualHeight = virtualHeight;
	scrollX = scrollY = 0;
	xScrollBarPolicy = yScrollBarPolicy = SHOWN_WHEN_NEEDED;
}

private boolean hasInitialized = false;

@Override
public void initGui() {
	hasInitialized = true;
	refreshScrollBars();
}

private void refreshScrollBars() {
	if (!hasInitialized)
		return;
	switch (xScrollBarPolicy) {
	case SHOWN_NEVER:
		xScrollBarVisible = false;
		break;
	case SHOWN_WHEN_NEEDED:
		xScrollBarVisible = virtualWidth > width;
		break;
	case SHOWN_ALWAYS:
		xScrollBarVisible = true;
		break;
	default:
		throw new IllegalStateException("Illegal value for xScrollBarPolicy: " + xScrollBarPolicy);
	}
	shownHeight = height - headerHeight - footerHeight - (xScrollBarVisible ? 6 : 0);
	switch (yScrollBarPolicy) {
	case SHOWN_NEVER:
		yScrollBarVisible = false;
		break;
	case SHOWN_WHEN_NEEDED:
		yScrollBarVisible = virtualHeight > shownHeight;
		break;
	case SHOWN_ALWAYS:
		yScrollBarVisible = true;
		break;
	default:
		throw new IllegalStateException("Illegal value for yScrollBarPolicy: " + yScrollBarPolicy);
	}
	shownWidth = width - (yScrollBarVisible ? 6 : 0);
	if (yScrollBarVisible && !xScrollBarVisible && xScrollBarPolicy == SHOWN_WHEN_NEEDED
			&& virtualWidth > shownWidth) {
		xScrollBarVisible = true;
		shownHeight -= 6;
	}
	maxScrollX = virtualWidth - shownWidth;
	if (maxScrollX < 0)
		maxScrollX = 0;
	maxScrollY = virtualHeight - shownHeight;
	if (maxScrollY < 0)
		maxScrollY = 0;
	xScrollBarWidth = shownWidth * shownWidth / virtualWidth;
	if (xScrollBarWidth > shownWidth)
		xScrollBarWidth = shownWidth;
	yScrollBarHeight = shownHeight * shownHeight / virtualHeight;
	if (yScrollBarHeight > shownHeight)
		yScrollBarHeight = shownHeight;
	xScrollBarLeft = (maxScrollX == 0 ? 0 : (shownWidth - xScrollBarWidth) * scrollX / maxScrollX);
	yScrollBarTop = (maxScrollY == 0 ? 0 : (shownHeight - yScrollBarHeight) * scrollY / maxScrollY) + headerHeight;
	xScrollBarRight = xScrollBarLeft + xScrollBarWidth;
	yScrollBarBottom = yScrollBarTop + yScrollBarHeight;
	return;
}

public int getHeaderHeight() {
	return headerHeight;
}

public void setHeaderHeight(int headerHeight) {
	this.headerHeight = headerHeight;
	refreshScrollBars();
}

public int getFooterHeight() {
	return footerHeight;
}

public void setFooterHeight(int footerHeight) {
	this.footerHeight = footerHeight;
	refreshScrollBars();
}

public int getVirtualWidth() {
	return virtualWidth;
}

public void setVirtualWidth(int virtualWidth) {
	this.virtualWidth = virtualWidth;
	refreshScrollBars();
}

public int getVirtualHeight() {
	return virtualHeight;
}

public void setVirtualHeight(int virtualHeight) {
	this.virtualHeight = virtualHeight;
	refreshScrollBars();
}

public int getScrollX() {
	return scrollX;
}

public void setScrollX(int scrollX) {
	this.scrollX = MathHelper.clamp_int(scrollX, 0, maxScrollX);
	refreshScrollBars();
}

public void addScrollX(int scrollX) {
	setScrollX(this.scrollX + scrollX);
}

public int getScrollY() {
	return scrollY;
}

public void setScrollY(int scrollY) {
	this.scrollY = MathHelper.clamp_int(scrollY, 0, maxScrollY);
	refreshScrollBars();
}

public void addScrollY(int scrollY) {
	setScrollY(this.scrollY + scrollY);
}

public int getMaxScrollX() {
	return maxScrollX;
}

public int getMaxScrollY() {
	return maxScrollY;
}

public void scrollTo(int x, int y) {
	scrollX = MathHelper.clamp_int(x, 0, maxScrollX);
	scrollY = MathHelper.clamp_int(y, 0, maxScrollY);
	refreshScrollBars();
}

public int getShownWidth() {
	return shownWidth;
}

public int getShownHeight() {
	return shownHeight;
}

protected int getXScrollBarWidth() {
	return xScrollBarWidth;
}

protected int getYScrollBarHeight() {
	return yScrollBarHeight;
}

protected int getXScrollBarLeft() {
	return xScrollBarLeft;
}

protected int getYScrollBarTop() {
	return yScrollBarTop;
}

protected int getXScrollBarRight() {
	return xScrollBarRight;
}

protected int getYScrollBarBottom() {
	return yScrollBarBottom;
}

public int getXScrollBarPolicy() {
	return xScrollBarPolicy;
}

public void setXScrollBarPolicy(int xScrollBarPolicy) {
	this.xScrollBarPolicy = xScrollBarPolicy;
	refreshScrollBars();
}

public int getYScrollBarPolicy() {
	return yScrollBarPolicy;
}

public void setYScrollBarPolicy(int yScrollBarPolicy) {
	this.yScrollBarPolicy = yScrollBarPolicy;
	refreshScrollBars();
}

public int getLeftKey() {
	return leftKey;
}

public void setLeftKey(int leftKey) {
	this.leftKey = leftKey;
}

public int getRightKey() {
	return rightKey;
}

public void setRightKey(int rightKey) {
	this.rightKey = rightKey;
}

public int getUpKey() {
	return upKey;
}

public void setUpKey(int upKey) {
	this.upKey = upKey;
}

public int getDownKey() {
	return downKey;
}

public void setDownKey(int downKey) {
	this.downKey = downKey;
}

public boolean usesMouseWheel() {
	return useMouseWheel;
}

public void setUsesMouseWheel(boolean usesMouseWheel) {
	useMouseWheel = usesMouseWheel;
}

public void drawScreen(int mouseX, int mouseY, float partialTicks) {
	drawVirtualScreen(mouseX + scrollX, mouseY + scrollY - headerHeight, scrollX, scrollY, headerHeight);
	GlStateManager.disableLighting();
	GlStateManager.disableFog();
	Tessellator tessellator = Tessellator.getInstance();
	WorldRenderer worldRenderer = tessellator.getWorldRenderer();
	GlStateManager.disableDepth();

	overlayBackground(0, headerHeight);
	overlayBackground(height - footerHeight, footerHeight);
	drawForeground(mouseX, mouseY, partialTicks);

	GlStateManager.enableBlend();
	GlStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 0, 1);
	GlStateManager.disableAlpha();
	GlStateManager.shadeModel(GL11.GL_SMOOTH);
	GlStateManager.disableTexture2D();

	if (yScrollBarVisible) {
		worldRenderer.startDrawingQuads();
		worldRenderer.setColorRGBA_I(0, 255);
		worldRenderer.addVertexWithUV(shownWidth, height - footerHeight, 0, 0, 1);
		worldRenderer.addVertexWithUV(width, height - footerHeight, 0, 1, 1);
		worldRenderer.addVertexWithUV(width, headerHeight, 0, 1, 0);
		worldRenderer.addVertexWithUV(shownWidth, headerHeight, 0, 0, 0);
		tessellator.draw();
	}
	if (xScrollBarVisible) {
		worldRenderer.startDrawingQuads();
		worldRenderer.setColorRGBA_I(0, 255);
		worldRenderer.addVertexWithUV(0, height - footerHeight, 0, 0, 1);
		worldRenderer.addVertexWithUV(width, height - footerHeight, 0, 1, 1);
		worldRenderer.addVertexWithUV(width, shownHeight + headerHeight, 0, 1, 0);
		worldRenderer.addVertexWithUV(0, shownHeight + headerHeight, 0, 0, 0);
		tessellator.draw();
	}
	if (xScrollBarVisible && yScrollBarVisible) {
		worldRenderer.startDrawingQuads();
		worldRenderer.setColorRGBA_I(0x808080, 255);
		worldRenderer.addVertexWithUV(shownWidth, height - footerHeight, 0, 0, 1);
		worldRenderer.addVertexWithUV(width, height - footerHeight, 0, 1, 1);
		worldRenderer.addVertexWithUV(width, shownHeight + headerHeight, 0, 1, 0);
		worldRenderer.addVertexWithUV(shownWidth, shownHeight + headerHeight, 0, 0, 0);
		tessellator.draw();
	}
	if (yScrollBarVisible) {
		worldRenderer.startDrawingQuads();
		worldRenderer.setColorRGBA_I(0x808080, 255);
		worldRenderer.addVertexWithUV(shownWidth, yScrollBarBottom, 0, 0, 1);
		worldRenderer.addVertexWithUV(width, yScrollBarBottom, 0, 1, 1);
		worldRenderer.addVertexWithUV(width, yScrollBarTop, 0, 1, 0);
		worldRenderer.addVertexWithUV(shownWidth, yScrollBarTop, 0, 0, 0);
		tessellator.draw();
	}
	if (xScrollBarVisible) {
		worldRenderer.startDrawingQuads();
		worldRenderer.setColorRGBA_I(0x808080, 255);
		worldRenderer.addVertexWithUV(xScrollBarLeft, height - footerHeight, 0, 0, 1);
		worldRenderer.addVertexWithUV(xScrollBarRight, height - footerHeight, 0, 1, 1);
		worldRenderer.addVertexWithUV(xScrollBarRight, shownHeight + headerHeight, 0, 1, 0);
		worldRenderer.addVertexWithUV(xScrollBarLeft, shownHeight + headerHeight, 0, 0, 0);
		tessellator.draw();
	}
	if (yScrollBarVisible) {
		worldRenderer.startDrawingQuads();
		worldRenderer.setColorRGBA_I(0xc0c0c0, 255);
		worldRenderer.addVertexWithUV(shownWidth, yScrollBarBottom - 1, 0, 0, 1);
		worldRenderer.addVertexWithUV(width - 1, yScrollBarBottom - 1, 0, 1, 1);
		worldRenderer.addVertexWithUV(width - 1, yScrollBarTop, 0, 1, 0);
		worldRenderer.addVertexWithUV(shownWidth, yScrollBarTop, 0, 0, 0);
		tessellator.draw();
	}
	if (xScrollBarVisible) {
		worldRenderer.startDrawingQuads();
		worldRenderer.setColorRGBA_I(0xc0c0c0, 255);
		worldRenderer.addVertexWithUV(xScrollBarLeft, height - footerHeight - 1, 0, 0, 1);
		worldRenderer.addVertexWithUV(xScrollBarRight - 1, height - footerHeight - 1, 0, 1, 1);
		worldRenderer.addVertexWithUV(xScrollBarRight - 1, shownHeight + headerHeight, 0, 1, 0);
		worldRenderer.addVertexWithUV(xScrollBarLeft, shownHeight + headerHeight, 0, 0, 0);
		tessellator.draw();
	}

	GlStateManager.enableTexture2D();
	GlStateManager.shadeModel(GL11.GL_FLAT);
	GlStateManager.enableAlpha();
	GlStateManager.disableBlend();

	super.drawScreen(mouseX, mouseY, partialTicks);
}

protected void overlayBackground(int top, int bottom) {
	Tessellator tessellator = Tessellator.getInstance();
	WorldRenderer worldRenderer = tessellator.getWorldRenderer();
	this.mc.getTextureManager().bindTexture(Gui.optionsBackground);
	GlStateManager.color(1, 1, 1, 1);
	worldRenderer.startDrawingQuads();
	worldRenderer.setColorRGBA_I(0x404040, 255);
	worldRenderer.addVertexWithUV(0, bottom, 0, 0, (float) bottom / 32);
	worldRenderer.addVertexWithUV(width, bottom, 0, (float) width / 32, (float) bottom / 32);
	worldRenderer.addVertexWithUV(width, top, 0, (float) this.width / 32, (float) top / 32);
	worldRenderer.addVertexWithUV(0, top, 0, 0, (float) top / 32);
	tessellator.draw();
}

protected abstract void drawVirtualScreen(int virtualMouseX, int virtualMouseY, int scrollX, int scrollY,
		int headerHeight);

protected abstract void drawForeground(int mouseX, int mouseY, float partialTicks);

@Override
public void keyTyped(char typedChar, int keyCode) throws IOException {
	if (keyCode == leftKey) {
		addScrollX(-20);
	} else if (keyCode == rightKey) {
		addScrollX(20);
	} else if (keyCode == upKey) {
		addScrollY(-20);
	} else if (keyCode == downKey) {
		addScrollY(20);
	} else {
		super.keyTyped(typedChar, keyCode);
	}
}

@Override
public void handleMouseInput() throws IOException {
	super.handleMouseInput();

	if (useMouseWheel) {
		int amtToScroll = Mouse.getEventDWheel();
		if (amtToScroll < 0)
			amtToScroll = 20;
		else if (amtToScroll > 0)
			amtToScroll = -20;
		addScrollY(amtToScroll);
	}
}

@Override
public void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException {
	if (mouseButton == 0) {
		if (yScrollBarVisible && mouseX >= width - 6 && mouseY >= getYScrollBarTop() && mouseX < width
				&& mouseY < getYScrollBarBottom()) {
			firstYScrollBarPos = scrollY;
			firstMouseY = mouseY;
		} else if (xScrollBarVisible && mouseX >= getXScrollBarLeft() && mouseY >= headerHeight + shownHeight
				&& mouseX < getXScrollBarRight() && mouseY < height - footerHeight) {
			firstXScrollBarPos = scrollX;
			firstMouseX = mouseX;
		} else {
			mouseClickedVirtual(mouseX + scrollX, mouseY + scrollY - headerHeight, mouseButton);
		}
	} else if (mouseY > headerHeight && mouseY < height - footerHeight) {
		mouseClickedVirtual(mouseX + scrollX, mouseY + scrollY - headerHeight, mouseButton);
	}
	super.mouseClicked(mouseX, mouseY, mouseButton);
}

protected void mouseClickedVirtual(int virtualMouseX, int virtualMouseY, int mouseButton) {
}

@Override
public void mouseReleased(int mouseX, int mouseY, int state) {
	if (state == 0) { // This test does not work
		firstXScrollBarPos = firstYScrollBarPos = firstMouseX = firstMouseY = -1;
	}
	if (mouseY > headerHeight && mouseY < height - footerHeight)
		mouseReleasedVirtual(mouseX + scrollX, mouseY + scrollY - headerHeight, state);
	super.mouseReleased(mouseX, mouseY, state);
}

protected void mouseReleasedVirtual(int virtualMouseX, int virtualMouseY, int releasedButton) {
}

@Override
public void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) {
	if (firstXScrollBarPos != -1) {
		int dx = mouseX - firstMouseX;
		float multiplier = (float) getShownWidth() / getXScrollBarWidth();
		setScrollX(firstXScrollBarPos + MathHelper.ceiling_float_int(dx * multiplier));
	} else if (firstYScrollBarPos != -1) {
		int dy = mouseY - firstMouseY;
		float multiplier = (float) getShownHeight() / getYScrollBarHeight();
		setScrollY(firstYScrollBarPos + MathHelper.ceiling_float_int(dy * multiplier));
	} else if (mouseY > headerHeight && mouseY < height - footerHeight) {
		mouseClickMoveVirtual(mouseX + scrollX, mouseY + scrollY - headerHeight, clickedMouseButton,
				timeSinceLastClick);
	}
}

protected void mouseClickMoveVirtual(int virtualMouseX, int virtualMouseY, int clickedMouseButton,
		long timeSinceLastClick) {
}

}

 

 

 

The small bug is in the method mouseReleased. Anyone know how to differentiate between different mouse buttons in that method?

catch(Exception e)

{

 

}

Yay, Pokémon exception handling, gotta catch 'em all (and then do nothing with 'em).

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.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • Realizing I was a victim of a scam was a devastating blow. My initial investment of $89,000, driven by dreams of financial success and the buzz surrounding a new cryptocurrency project, turned into a nightmare. The project promised high returns and rapid gains, attracting many eager investors like myself. However, as time passed and inconsistencies began to surface, it became evident that I had made a grave mistake by not thoroughly vetting the brokerage company handling the investment. Feeling anxious and betrayed, I desperately searched for a way to recover my funds. It was during this frantic search that I stumbled upon the Lee Ultimate Hacker tool through a Facebook post. With little left to lose, I decided to reach out to their team for help. To my relief, they were quick to respond and immediately started recovering my compromised email and regaining access to my cryptocurrency wallets. The team at Lee Ultimate Hacker was incredibly professional and transparent throughout the process. They meticulously traced the digital footprints left by the scammers, employing advanced technological methods to unravel the complex network that had ensnared my funds. Their expertise in cybersecurity and recovery strategies gradually began to turn the tide in my favor. Although the scammers had already siphoned off $30,000 worth of Bitcoin, Lee Ultimate Hacker was relentless in their pursuit. They managed to expose the fraudulent activities of the scam operators, revealing their identities and the mechanisms they used to lure investors. This exposure was crucial not only for my case but also as a warning to the wider community about the perils of unverified investment schemes. As we progressed, it became a race against time to retrieve the remaining $59,000 before the scammers could vanish completely. Each step forward was met with new challenges, as these criminals constantly shifted tactics and moved their digital assets to evade capture. Nonetheless, the determination and skill of the recovery team kept us hopeful. Throughout this ordeal, I learned the hard value of caution and due diligence in investment, especially within the volatile world of cryptocurrency. The experience has been incredibly taxing, both emotionally and financially, but the support and results provided by Lee Ultimate Hacker have been indispensable. The recovery process is ongoing, and while the final outcome remains uncertain, the progress made so far gives me hope. The battle to recover the full amount of my investment continues, and with the expertise of Lee Ultimate Hacker, I remain optimistic about the eventual recovery of my funds. Their commitment to their clients and proficiency in handling such complex cases truly sets them apart in the field of cyber recovery. LEEULTIMATEHACKER@ AOL. COM   Support @ leeultimatehacker . com.  telegram:LEEULTIMATE   wh@tsapp +1  (715) 314  -  9248     
    • Hi everyone. I’m excited to share my experience with CrackerWizard Recovery Firm. They helped me recover a substantial amount of crypto after falling victim to online scams disguised as Bitcoin investments. CrackerWizard’s exceptional service enabled me to retrieve my lost funds, despite the complex circumstances surrounding the case. With their dedicated team and advanced technology, they swiftly traced and recovered my assets. CrackerWizard is a reliable partner in the crypto world, highly recommended for anyone facing similar challenges. Contact them.
    • So I saw that mixin is shipped as a library with forge, but is it available for 1.7.10 ?  
    • So I've read the EULA, and lets be straight...     If I split my modpack(of my mods, yeah I'm nuts) into several(many) individual mods(like just one boss) with minor additions(plus not working together), then have a complete/modpack version on patreon/onlyfans having each addon work together... Would people buy my idea?
  • Topics

×
×
  • Create New...

Important Information

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