Jump to content

Recommended Posts

Posted

I'm trying to understand how minimap mods actually work. I found the open source MapWriter and have browsed the source code for other popular minimaps, but what I can't seem to figure out is how they actually get the colors for the map. I've tried something like how the map item works (getting the color of the block I'm standing on and drawing a rectangle on screen with that integer), but nothing happened. Could someone explain this to me please?

 

This is what I've got so far, and you can see where I'd like to add the minimap. The zone your in (top left) will change based on a text file stored locally or on the server. When your armor's durability is less than 75%, a manikin type thing will appear in the bottom right.

 

width=800 height=450

ggm12Qc.jpg?1 [/img]

Posted

At first I tried this:

 

IBlockState state = MC.theWorld.getBlockState(MC.thePlayer.getPosition());
int color = state.getBlock().getMapColor(state).colorValue;
drawRect(0, 0, 100, 100, color);

 

That didn't work. So then I found a thread talking about getting the textures from the jar like so:

 

MC.getTextureManager().bindTexture(new ResourceLocation("textures/blocks/grass_side.png"));
drawModalRectWithCustomSizedTexture(0, 0, 0, 0, 64, 64, 64, 64);

 

This does work, and I could get the RGB values like this:

 

static int[] loadTexture(String block)
{
try
{
	final ResourceLocation resourceLocation = new ResourceLocation("textures/blocks/" + block + ".png");
	final IResourceManager resourceManager = Minecraft.getMinecraft().getResourceManager();
	final IResource resource = resourceManager.getResource(resourceLocation);
	final InputStream in = resource.getInputStream();
	final BufferedImage image = ImageIO.read(in);
	return image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());
}
catch (Exception e)
{

}
return null;
}

 

My thought now is to cache all the block colors found in Minecraft, but what do I do about custom blocks added by a mod? Is it possible to iterate through the textures in their jar?

Posted

Cool, that works too.

 

IBlockState state = MC.theWorld.getBlockState(MC.thePlayer.getPosition());
int color = 0xFF000000 | state.getBlock().getMapColor(state).colorValue;
drawRect(50, 50, 100, 100, color);

 

Although, it's not the level of detail I was looking for. It swaps between black and the actual color for whatever reason. Xaero's map mod doesn't work like this, right?

Posted

Well, no, but what I'm saying is that Xaero's map, for instance, looks nothing like the item you can craft in game. It is much more detailed. I don't think the getMapColor() function has the ability to yield that level of detail. So, how do they do it?

 

I'll play around with the getMapColor() function and see what I get. I'll post that when I'm done.

Posted

GetMapColor() returns a predefined color (one of 16, IIRC although there are some duplicates).  The more advanced maps go "this color is useless" and examine the block's actual texture and make a "best guess" average color for the block.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Posted

Right, my question is how do they get that texture? The default Minecraft blocks are easy enough, but how do you grab the textures of custom blocks?

 

Edit:

Let me clarify, I can use the loadTexture() function I mentioned before only if I know where the resource location is, correct? I will assume I don't know that. Some modder may decide not to follow convention. I can't seem to find a way to pull a texture from an arbitrary block.

Posted

This works, but I get tons of errors. What is the right way to do this? I really don't even want to use the getMapColor() function because it flickers between black and green. I don't know why.

 

public class MiniMapHandler
{
private static final Minecraft MC = Minecraft.getMinecraft();
private static final BufferedImage MAP = new BufferedImage(75, 75, BufferedImage.TYPE_INT_RGB);
private static DynamicTexture TEXTURE = new DynamicTexture(MAP);

@SubscribeEvent
public void onTick(PlayerTickEvent event)
{
	// (Y, X) They're swapped. Don't feel like retyping.
	BlockPos zero_zero = MC.thePlayer.getPosition();
	BlockPos one_zero = MC.thePlayer.getPosition().north();
	BlockPos n_one_zero = MC.thePlayer.getPosition().south();
	BlockPos zero_one = MC.thePlayer.getPosition().east();
	BlockPos zero_n_one = MC.thePlayer.getPosition().west();

	IBlockState state_zero_zero = MC.theWorld.getBlockState(zero_zero);
	IBlockState state_one_zero = MC.theWorld.getBlockState(one_zero);
	IBlockState state_n_one_zero = MC.theWorld.getBlockState(n_one_zero);
	IBlockState state_zero_one = MC.theWorld.getBlockState(zero_one);
	IBlockState state_zero_n_one = MC.theWorld.getBlockState(zero_n_one);

	int color_zero_zero = state_zero_zero.getBlock().getMapColor(state_zero_zero).colorValue;
	int color_one_zero = state_one_zero.getBlock().getMapColor(state_one_zero).colorValue;
	int color_n_one_zero = state_n_one_zero.getBlock().getMapColor(state_n_one_zero).colorValue;
	int color_zero_one = state_zero_one.getBlock().getMapColor(state_zero_one).colorValue;
	int color_zero_n_one = state_zero_n_one.getBlock().getMapColor(state_zero_n_one).colorValue;

	MAP.setRGB(37, 37, color_zero_zero);
	MAP.setRGB(37, 38, color_one_zero);
	MAP.setRGB(37, 36, color_n_one_zero);
	MAP.setRGB(38, 37, color_zero_one);
	MAP.setRGB(36, 37, color_zero_n_one);

	TEXTURE = new DynamicTexture(MAP);
}

public static ResourceLocation getTexture()
{
	return MC.getTextureManager().getDynamicTextureLocation("minimap", TEXTURE);
}
}

 

Inside onRenderGui(RenderGameOverlayEvent event):

MC.getTextureManager().bindTexture(MiniMapHandler.getTexture());
drawModalRectWithCustomSizedTexture(MC.displayWidth / 2 - 78, 13, 0, 0, 75, 75, 75, 75);

Posted

I knew that creating this wasn't going to be easy, but this is a lot of work! With The Division launching soon, I honestly don't think I'll be working on this much longer. I've opted to use getMapColor() (despite what I said) in lieu of starting a huge project. I have created a much better way to draw the map, although is there a way to update the DynamicTexture without making a new one every time? My game starts to lag after a while and updateDynamicTexture() doesn't seem to do the trick.

 

MiniMapHandler:

public class MiniMapHandler
{
   private static final Minecraft MC = Minecraft.getMinecraft();
   public static final BufferedImage MAP = new BufferedImage(75, 75, BufferedImage.TYPE_INT_RGB);

   @SubscribeEvent
   public void onTick(PlayerTickEvent event)
   {
      BlockPos center = MC.thePlayer.getPosition();

      for (int z = center.getZ() - 37; z < center.getZ() + 38; z++)
      {
         for (int x = center.getX() - 37; x < center.getX() + 38; x++)
         {
            IBlockState state = MC.theWorld.getBlockState(new BlockPos(x, center.getY(), z));
            int color = state.getBlock().getMapColor(state).colorValue;

            MAP.setRGB(x - center.getX() + 37, z - center.getZ() + 37, color);
         }
      }
   }
}

 

Inside onRenderGui(RenderGameOverlayEvent event):

TEXTURE = new DynamicTexture(MiniMapHandler.MAP);
MC.getTextureManager().bindTexture(MC.getTextureManager().getDynamicTextureLocation("minimap", TEXTURE));
drawModalRectWithCustomSizedTexture(MC.displayWidth / 2 - 78, 13, 0, 0, 75, 75, 75, 75);

 

The map reminds me of NetHack...

 

8rNdJ8G.jpg

Posted

While using the player's current Y position seems like a logical choice, that map is utterly worthless to me, as a player.  In looking at it and looking at what's in front of the player in that screenshot, I have literally no idea where I am or what direction I'm facing.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Posted

That's true. Haha! Could you suggest a way to get the topmost block or perhaps a better way to draw the map without crashing my client? I'm most concerned about the latter.

 

Edit:

I made the map look better. It checks blocks above and below the player.

 

public class MiniMapHandler
{
private static final Minecraft MC = Minecraft.getMinecraft();
public static final BufferedImage MAP = new BufferedImage(75, 75, BufferedImage.TYPE_INT_RGB);

@SubscribeEvent
public void onTick(PlayerTickEvent event)
{
	BlockPos center = MC.thePlayer.getPosition();
	MAP.flush();

	for (int z = center.getZ() - 37; z < center.getZ() + 38; z++)
	{
		for (int x = center.getX() - 37; x < center.getX() + 38; x++)
		{
			for (int y = center.getY() + 10; y > center.getY() - 10; y--)
			{
				BlockPos pos = new BlockPos(x, y, z);
				IBlockState state = MC.theWorld.getBlockState(pos);

				if (!state.getBlock().isAir(MC.theWorld.init(), pos))
				{
					int color = state.getBlock().getMapColor(state).colorValue;

					MAP.setRGB(x - center.getX() + 37, z - center.getZ() + 37, color);

					break;
				}
			}
		}
	}

	MAP.setRGB(37, 37, 0xFFFFFF);
}
}

 

855QFDc.png

Posted

If it crashes, include the line that it crashes at.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Posted

No, it "crashes" because Java's memory fills up and the server shuts down. I get messages saying the server jumped ahead X number of ticks. I'm not handling the dynamic textures properly. I don't exactly know how other than to create a new one and load it.

Posted

That's because you're creating 118125 BlockPos objects and 118125 IBlockState references every tick, all of which need to be garbage collected.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Posted

Even when I take out the for loops and render a black box, I still lag and break the game. I have no doubt that all that cleanup is a problem, but there has to be a way to reuse the dynamic texture. Unless, I'm approaching this in entirely the wrong way.

Posted

Would probably help if...

 

1) Didn't use MAP.flush();

2) Didn't check for changes every tick (seriously, scanning 118125 blocks every tick is dumb)

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Posted

I have the minimap updating at 5 FPS. This seems to work a lot better, but only appears to be delaying the inevitable. This was mainly a proof of concept for me. I may decide to work on this more later, but for now I am finished.

Posted

You still don't need MAP.flush()

 

You may as well think of it as MAP.garbageCollect(), the method exists to mark the memory it uses as free, but you're using it to reset the colors.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

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

    • https://mclo.gs/9y5ciD2 anyone ever had this issue?  Internal exception illegal argument exception: unable to fit 3194354 into 3
    • Hoffman Law Recovery helped me recovered my lost funds
    • Hi! I'm trying to add my custom models/textures renderer like this: public class PonyPlayerWrapperRenderer extends EntityRenderer<Player> { // wrapper class under my LivingEntityRenderer class implementation private final PonyPlayerRenderer innerRenderer; private final PonyPlayerRenderer innerSlimRenderer; public PonyPlayerWrapperRenderer(final EntityRendererProvider.Context context) { super(context); System.out.println("creating new PonyPlayerWrapperRenderer"); this.innerRenderer = new PonyPlayerRenderer(context, false); this.innerSlimRenderer = new PonyPlayerRenderer(context, true); } @Override public void render(final Player entity, final float yaw, final float partialTicks, final PoseStack poseStack, final MultiBufferSource bufferSource, final int packedLight) { System.out.println("PonyPlayerWrapperRenderer render: " + entity.toString()); if (entity instanceof AbstractClientPlayer clientPlayer) { if (clientPlayer.getModelName().contains("slim")) { innerSlimRenderer.render(clientPlayer, yaw, partialTicks, poseStack, bufferSource, packedLight); } else { innerRenderer.render(clientPlayer, yaw, partialTicks, poseStack, bufferSource, packedLight); } } } @Override public ResourceLocation getTextureLocation(final Player player) { System.out.println("PonyPlayerWrapperRenderer getTextureLocation"); if (player instanceof AbstractClientPlayer clientPlayer) { return clientPlayer.getSkinTextureLocation(); } System.out.println("player instanceof AbstractClientPlayer is false"); return getDefaultSkin(player.getUUID()); } } public class PonyPlayerRenderer extends LivingEntityRenderer<AbstractClientPlayer, PlayerModel<AbstractClientPlayer>> { private final PlayerModel<AbstractClientPlayer> earthModel; private final PlayerModel<AbstractClientPlayer> pegasusModel; private final PlayerModel<AbstractClientPlayer> unicornModel; public PonyPlayerRenderer(final EntityRendererProvider.Context context, final boolean slim) { super( context, slim ? new PonyModelSlim(context.bakeLayer(PonyModelSlim.LAYER_LOCATION)) : new PonyModel(context.bakeLayer(PonyModel.LAYER_LOCATION)), 0.5f ); System.out.println("creating new PonyPlayerRenderer"); this.earthModel = slim ? new PonyModelSlim(context.bakeLayer(PonyModelSlim.LAYER_LOCATION)) : new PonyModel(context.bakeLayer(PonyModel.LAYER_LOCATION)); this.pegasusModel = new PegasusModel(context.bakeLayer(PegasusModel.LAYER_LOCATION)); this.unicornModel = new UnicornModel(context.bakeLayer(UnicornModel.LAYER_LOCATION)); } @Override public void render(final AbstractClientPlayer player, final float entityYaw, final float partialTicks, final PoseStack poseStack, final MultiBufferSource buffer, final int packedLight) { final PonyRace race = player.getCapability(PONY_DATA) .map(data -> ofNullable(data.getRace()).orElse(PonyRace.EARTH)) .orElse(PonyRace.EARTH); this.model = switch (race) { case PEGASUS -> pegasusModel; case UNICORN -> unicornModel; case EARTH -> earthModel; }; super.render(player, entityYaw, partialTicks, poseStack, buffer, packedLight); } @Override public ResourceLocation getTextureLocation(final AbstractClientPlayer player) { final PonyRace race = player.getCapability(PONY_DATA) .map(data -> ofNullable(data.getRace()).orElse(PonyRace.EARTH)) .orElse(PonyRace.EARTH); return switch (race) { case EARTH -> fromNamespaceAndPath(MODID, "textures/entity/earth_pony.png"); case PEGASUS -> fromNamespaceAndPath(MODID, "textures/entity/pegasus.png"); case UNICORN -> fromNamespaceAndPath(MODID, "textures/entity/unicorn.png"); }; } } @Mod.EventBusSubscriber(modid = MODID, bus = MOD, value = CLIENT) public class ClientRenderers { // mod bus render registration config @SubscribeEvent public static void onRegisterLayerDefinitions(final EntityRenderersEvent.RegisterLayerDefinitions event) { event.registerLayerDefinition(PonyModel.LAYER_LOCATION, PonyModel::createBodyLayer); event.registerLayerDefinition(PonyModelSlim.LAYER_LOCATION, PonyModelSlim::createBodyLayer); event.registerLayerDefinition(PegasusModel.LAYER_LOCATION, PegasusModel::createBodyLayer); event.registerLayerDefinition(UnicornModel.LAYER_LOCATION, UnicornModel::createBodyLayer); event.registerLayerDefinition(InnerPonyArmorModel.LAYER_LOCATION, InnerPonyArmorModel::createBodyLayer); event.registerLayerDefinition(OuterPonyArmorModel.LAYER_LOCATION, OuterPonyArmorModel::createBodyLayer); } @SubscribeEvent public static void onRegisterRenderers(final EntityRenderersEvent.RegisterRenderers event) { event.registerEntityRenderer(EntityType.PLAYER, PonyPlayerWrapperRenderer::new); System.out.println("onRegisterRenderers end"); } } Method onRegisterRenderers() is called and I can see it being logged. But when I enter the world, my PonyWrapperRenderer render() method doesn't ever seem to be called. I also tried to put my renderer to EntityRenderDispatcher's playerRenderers via reflection: @Mod.EventBusSubscriber(modid = MODID, bus = MOD, value = CLIENT) public class ClientRenderers { @SubscribeEvent public static void onRegisterLayerDefinitions(final EntityRenderersEvent.RegisterLayerDefinitions event) { event.registerLayerDefinition(PonyModel.LAYER_LOCATION, PonyModel::createBodyLayer); event.registerLayerDefinition(PonyModelSlim.LAYER_LOCATION, PonyModelSlim::createBodyLayer); event.registerLayerDefinition(PegasusModel.LAYER_LOCATION, PegasusModel::createBodyLayer); event.registerLayerDefinition(UnicornModel.LAYER_LOCATION, UnicornModel::createBodyLayer); event.registerLayerDefinition(InnerPonyArmorModel.LAYER_LOCATION, InnerPonyArmorModel::createBodyLayer); event.registerLayerDefinition(OuterPonyArmorModel.LAYER_LOCATION, OuterPonyArmorModel::createBodyLayer); } @SubscribeEvent public static void onClientSetup(final FMLClientSetupEvent event) { event.enqueueWork(() -> { try { final EntityRenderDispatcher dispatcher = Minecraft.getInstance().getEntityRenderDispatcher(); final Field renderersField = getEntityRenderDispatcherField("playerRenderers"); final Field itemInHandRenderer = getEntityRenderDispatcherField("itemInHandRenderer"); @SuppressWarnings("unchecked") final Map<String, EntityRenderer<? extends Player>> playerRenderers = (Map<String, EntityRenderer<? extends Player>>)renderersField.get(dispatcher); final PonyPlayerWrapperRenderer renderer = new PonyPlayerWrapperRenderer( new EntityRendererProvider.Context( dispatcher, Minecraft.getInstance().getItemRenderer(), Minecraft.getInstance().getBlockRenderer(), (ItemInHandRenderer)itemInHandRenderer.get(dispatcher), Minecraft.getInstance().getResourceManager(), Minecraft.getInstance().getEntityModels(), Minecraft.getInstance().font ) ); playerRenderers.put("default", renderer); playerRenderers.put("slim", renderer); System.out.println("Player renderers replaced"); } catch (final Exception e) { throw new RuntimeException("Failed to replace player renderers", e); } }); } private static Field getEntityRenderDispatcherField(final String fieldName) throws NoSuchFieldException { final Field field = EntityRenderDispatcher.class.getDeclaredField(fieldName); field.setAccessible(true); return field; } } But I receive the error before Minecraft Client appears (RuntimeException: Failed to replace player renderers - from ClientRenderers onClientSetup() method - and its cause below): java.lang.IllegalArgumentException: No model for layer anotherlittlepony:earth_pony#main at net.minecraft.client.model.geom.EntityModelSet.bakeLayer(EntityModelSet.java:18) ~[forge-1.20.1-47.4.0_mapped_official_1.20.1-recomp.jar:?] {re:classloading,pl:runtimedistcleaner:A} at net.minecraft.client.renderer.entity.EntityRendererProvider$Context.bakeLayer(EntityRendererProvider.java:69) ~[forge-1.20.1-47.4.0_mapped_official_1.20.1-recomp.jar:?] {re:classloading,pl:runtimedistcleaner:A} at com.thuggeelya.anotherlittlepony.client.renderer.pony.PonyPlayerRenderer.<init>(PonyPlayerRenderer.java:32) ~[main/:?] {re:classloading} at com.thuggeelya.anotherlittlepony.client.renderer.pony.PonyPlayerWrapperRenderer.<init>(PonyPlayerWrapperRenderer.java:24) ~[main/:?] {re:classloading} at com.thuggeelya.anotherlittlepony.client.renderer.ClientRenderers.lambda$onClientSetup$0(ClientRenderers.java:79) ~[main/:?] {re:classloading} ... 33 more Problem appears when EntityRendererProvider context tries to bakeLayer with my model layer location: new PonyModel(context.bakeLayer(PonyModel.LAYER_LOCATION)); // PonyPlayerRenderer.java:32 public class PonyModel extends PlayerModel<AbstractClientPlayer> { // the model class itself public static final ModelLayerLocation LAYER_LOCATION = new ModelLayerLocation( ResourceLocation.fromNamespaceAndPath(MODID, "earth_pony"), "main" ); public PonyModel(final ModelPart root) { super(root, false); } public static LayerDefinition createBodyLayer() { // some CubeListBuilder stuff for model appearance } } Textures PNGs are placed at: resources/assets/[my mod id]/textures/entity. My forge version is 1.20.1. Would appreciate any help.
    • Well, a bit more information about what you're trying to do would be helpful. e.g. why you're trying to use "INVOKE_ASSIGN" instead of "INVOKE". "INVOKE_ASSIGN" calls your code after the "target" is called and its value is stored, if applicable. "INVOKE" calls your code before the target is called. "target" expects a fully qualified name, as per the SpongePowered docs, if that name is going to be remapped (which it will be if your injecting into Minecraft itself and not another mod). For more information on fully qualified names versus canonical names, see the Java specifications. Here's an example of a working "@At" from my own code that targets the "getClosestsVulnerablePlayerToEntity" call inside a mob's logic: @At(value = "INVOKE_ASSIGN", target = "net.minecraft.world.World.getClosestVulnerablePlayerToEntity(Lnet/minecraft/entity/Entity;D)Lnet/minecraft/entity/player/EntityPlayer;") Hope this helps!
    • Ran it one more time just to check, and there's no errors this time on the log??? Log : https://mclo.gs/LnuaAiu I tried allocating more memory to the modpack, around 8000MB and it's still the same; stopping at "LOAD_REGISTRIES". Are some of the mods clashing, maybe? I have no clue what to do LOL
  • Topics

  • Who's Online (See full list)

×
×
  • Create New...

Important Information

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