Jump to content

McGrizzle

Members
  • Posts

    5
  • Joined

  • Last visited

Everything posted by McGrizzle

  1. Hello again! I recently posted here on the forum needing assistance and got some great help, so I'm back, this time with a little less confidence that I'm doing something correctly. Basically, I'm attempting to read MapItem in ForgeSrc and extend it to render data from an arbitrary location, whether it's from a database, metadata, or something else entirely. By doing some research, I've found out several things about the way maps work: Colors are indexed and color variants are calculated from a base byte value. These colors are fetched statically by id from MapColor , based on block material. The way colors are fetched can likely be changed before it's added to the map, allowing what I'd like to do. I'm not looking to add custom colors, but use existing colors for annotations based on other data besides block material. The only issue is that the file is largely obfuscated, with much of the information needed to implement my goal concealed in a 7-deep nested monster branching statement inside the updateMapData method. The MapItem class in question (I've added comments to certain sections that I believe explain what the code is doing, but I could very wrong and I haven't made much progress): package net.minecraft.item; import com.google.common.collect.HashMultiset; import com.google.common.collect.Iterables; import com.google.common.collect.Multisets; import java.util.List; import net.minecraft.block.Block; import net.minecraft.block.BlockDirt; import net.minecraft.block.BlockStone; import net.minecraft.block.material.MapColor; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.init.Items; import net.minecraft.network.Packet; import net.minecraft.util.BlockPos; import net.minecraft.util.MathHelper; import net.minecraft.world.World; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.storage.MapData; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; public class ItemMap extends ItemMapBase { private static final String __OBFID = "CL_00000047"; protected ItemMap() { this.setHasSubtypes(true); } @SideOnly(Side.CLIENT) public static MapData loadMapData(int mapId, World worldIn) { String s = "map_" + mapId; MapData mapdata = (MapData)worldIn.loadItemData(MapData.class, s); if (mapdata == null) { mapdata = new MapData(s); worldIn.setItemData(s, mapdata); } return mapdata; } public MapData getMapData(ItemStack stack, World worldIn) { String s = "map_" + stack.getMetadata(); MapData mapdata = (MapData)worldIn.loadItemData(MapData.class, s); if (mapdata == null && !worldIn.isRemote) { stack.setItemDamage(worldIn.getUniqueDataId("map")); s = "map_" + stack.getMetadata(); mapdata = new MapData(s); mapdata.scale = 3; mapdata.func_176054_a((double)worldIn.getWorldInfo().getSpawnX(), (double)worldIn.getWorldInfo().getSpawnZ(), mapdata.scale); mapdata.dimension = worldIn.provider.getDimensionId(); mapdata.markDirty(); worldIn.setItemData(s, mapdata); } return mapdata; } public void updateMapData(World worldIn, Entity viewer, MapData data) { if (worldIn.provider.getDimensionId() == data.dimension && viewer instanceof EntityPlayer) { int i = 1 << data.scale; int j = data.xCenter; int k = data.zCenter; int l = MathHelper.floor_double(viewer.posX - (double)j) / i + 64; int i1 = MathHelper.floor_double(viewer.posZ - (double)k) / i + 64; int j1 = 128 / i; if (worldIn.provider.getHasNoSky()) { j1 /= 2; } MapData.MapInfo mapinfo = data.getMapInfo((EntityPlayer)viewer); ++mapinfo.field_82569_d; boolean flag = false; for (int k1 = l - j1 + 1; k1 < l + j1; ++k1) { if ((k1 & 15) == (mapinfo.field_82569_d & 15) || flag) { flag = false; double d0 = 0.0D; for (int l1 = i1 - j1 - 1; l1 < i1 + j1; ++l1) { if (k1 >= 0 && l1 >= -1 && k1 < 128 && l1 < 128) { int i2 = k1 - l; int j2 = l1 - i1; boolean flag1 = i2 * i2 + j2 * j2 > (j1 - 2) * (j1 - 2); int k2 = (j / i + k1 - 64) * i; int l2 = (k / i + l1 - 64) * i; HashMultiset hashmultiset = HashMultiset.create(); Chunk chunk = worldIn.getChunkFromBlockCoords(new BlockPos(k2, 0, l2)); if (!chunk.isEmpty()) { int i3 = k2 & 15; int j3 = l2 & 15; int k3 = 0; double d1 = 0.0D; int l3; // Noise is generated in place of meaningful data when in the Nether or the End. The if statement below does this. // if the current dimension has no sky (ie, is the Nether or the End)... if (worldIn.provider.getHasNoSky()) { l3 = k2 + l2 * 231871; l3 = l3 * l3 * 31287121 + l3 * 11; if ((l3 >> 20 & 1) == 0) { hashmultiset.add(Blocks.dirt.getMapColor(Blocks.dirt.getDefaultState().withProperty(BlockDirt.VARIANT, BlockDirt.DirtType.DIRT)), 10); } else { hashmultiset.add(Blocks.stone.getMapColor(Blocks.stone.getDefaultState().withProperty(BlockStone.VARIANT, BlockStone.EnumType.STONE)), 100); } d1 = 100.0D; } // The good stuff... else { for (l3 = 0; l3 < i; ++l3) { for (int i4 = 0; i4 < i; ++i4) { int j4 = chunk.getHeight(l3 + i3, i4 + j3) + 1; IBlockState iblockstate = Blocks.air.getDefaultState(); if (j4 > 1) { do { --j4; iblockstate = chunk.getBlockState(new BlockPos(l3 + i3, j4, i4 + j3)); } while (iblockstate.getBlock().getMapColor(iblockstate) == MapColor.airColor && j4 > 0); if (j4 > 0 && iblockstate.getBlock().getMaterial().isLiquid()) { int k4 = j4 - 1; Block block; do { block = chunk.getBlock(l3 + i3, k4--, i4 + j3); ++k3; } while (k4 > 0 && block.getMaterial().isLiquid()); } } d1 += (double)j4 / (double)(i * i); hashmultiset.add(iblockstate.getBlock().getMapColor(iblockstate)); } } } // Below - generic depth test w/ shadows (probably) k3 /= i * i; double d2 = (d1 - d0) * 4.0D / (double)(i + 4) + ((double)(k1 + l1 & 1) - 0.5D) * 0.4D; byte b0 = 1; if (d2 > 0.6D) { b0 = 2; } if (d2 < -0.6D) { b0 = 0; } MapColor mapcolor = (MapColor)Iterables.getFirst(Multisets.copyHighestCountFirst(hashmultiset), MapColor.airColor); // Below - most likely a water depth test for coloring deeper water darker shades of blue if (mapcolor == MapColor.waterColor) { d2 = (double)k3 * 0.1D + (double)(k1 + l1 & 1) * 0.2D; b0 = 1; if (d2 < 0.5D) { b0 = 2; } if (d2 > 0.9D) { b0 = 0; } } d0 = d1; // According to Minecraft wiki: Below - data being accessed via data[xOffset + yOffset *128] and color data is added based on color index if (l1 >= 0 && i2 * i2 + j2 * j2 < j1 * j1 && (!flag1 || (k1 + l1 & 1) != 0)) { byte b1 = data.colors[k1 + l1 * 128]; byte b2 = (byte)(mapcolor.colorIndex * 4 + b0); if (b1 != b2) { data.colors[k1 + l1 * 128] = b2; data.updateMapData(k1, l1); flag = true; } } } } } } } } } /** * Called each tick as long the item is on a player inventory. Uses by maps to check if is on a player hand and * update it's contents. */ public void onUpdate(ItemStack stack, World worldIn, Entity entityIn, int itemSlot, boolean isSelected) { if (!worldIn.isRemote) { MapData mapdata = this.getMapData(stack, worldIn); if (entityIn instanceof EntityPlayer) { EntityPlayer entityplayer = (EntityPlayer)entityIn; mapdata.updateVisiblePlayers(entityplayer, stack); } if (isSelected) { this.updateMapData(worldIn, entityIn, mapdata); } } } public Packet createMapDataPacket(ItemStack stack, World worldIn, EntityPlayer player) { return this.getMapData(stack, worldIn).getMapPacket(stack, worldIn, player); } /** * Called when item is crafted/smelted. Used only by maps so far. */ public void onCreated(ItemStack stack, World worldIn, EntityPlayer playerIn) { if (stack.hasTagCompound() && stack.getTagCompound().getBoolean("map_is_scaling")) { MapData mapdata = Items.filled_map.getMapData(stack, worldIn); stack.setItemDamage(worldIn.getUniqueDataId("map")); MapData mapdata1 = new MapData("map_" + stack.getMetadata()); mapdata1.scale = (byte)(mapdata.scale + 1); if (mapdata1.scale > 4) { mapdata1.scale = 4; } mapdata1.func_176054_a((double)mapdata.xCenter, (double)mapdata.zCenter, mapdata1.scale); mapdata1.dimension = mapdata.dimension; mapdata1.markDirty(); worldIn.setItemData("map_" + stack.getMetadata(), mapdata1); } } /** * allows items to add custom lines of information to the mouseover description * * @param tooltip All lines to display in the Item's tooltip. This is a List of Strings. * @param advanced Whether the setting "Advanced tooltips" is enabled */ @SideOnly(Side.CLIENT) public void addInformation(ItemStack stack, EntityPlayer playerIn, List tooltip, boolean advanced) { MapData mapdata = this.getMapData(stack, playerIn.worldObj); if (advanced) { if (mapdata == null) { tooltip.add("Unknown map"); } else { tooltip.add("Scaling at 1:" + (1 << mapdata.scale)); tooltip.add("(Level " + mapdata.scale + "/" + 4 + ")"); } } } } Now, the thing is I could be going about this completely wrong and there could be a more effective way of making an item that does what I described, or there may not even be a good way to do it. My questions: [*]Does anyone have experience in extending maps or even working with the code, and if so, do you know if I'm doing this in the best way? [*]Is there anyone that can provide details about the variables and what they may be doing? I've identified several blocks (such as the block that tests for water depth and the nether/end noise block, among others) but would anyone have more? I'm obviously doing my best to look though it myself, but it's a daunting task. Sorry if I look like an idiot. Admittedly, this is my first time ever analyzing obfuscated code. Thanks for the help if anyone offers!
  2. Thanks for popping in! If that's the case, I'll probably update it when I migrate everything to 1.8. I began this mod with the intention of making it compatible with another mod, but the mod in question is no longer maintained, and at this point, I'm mainly making a proof-of-concept. I'll add this to the list though, since so far, testing has gone smoothly, and everything is starting to get a little messy. But hey, it was awesome to me. It got the results I wanted, and premature optimization is the devil's right hand.
  3. Thanks guys for all the help. I was able to get something up and running! This was the first thing I tried, but I kept getting invalid spawn point warnings in chat about a lack of a bed within the chunk, even though I was passing the forced flag. I gave up after moving it between different events still without luck. This did it! It's not particularly elegant, but it works as expected! Can definitely use this for prototyping, plus the visual shenanigans are negligible, even when moving great distances. Here's my code, in case you were curious. My tick handler: public class PlayerTickEventHandler { static boolean isFirstTick; public PlayerTickEventHandler() { this.isFirstTick = true; } @SubscribeEvent public void onPlayerTick(PlayerTickEvent event) { if (isFirstTick) { if (SupplyPlayer.get(event.player).hasSpawn){ ChatUtils.sendServerMsg("Tick hit!"); event.player.setPositionAndUpdate(SupplyPlayer.get(event.player).supplyX, SupplyPlayer.get(event.player).supplyY, SupplyPlayer.get(event.player).supplyZ); } this.isFirstTick = false; } } } My spawn event handler (I rolled the two events into a single class, but I'm not sure if this is best practice): public class PlayerSpawnEventsHandler { PlayerSpawnEventsHandler(){}; @SubscribeEvent public void onEntityJoinWorldEvent(EntityJoinWorldEvent event) { if (!event.entity.worldObj.isRemote && event.entity instanceof EntityPlayer) { PlayerTickEventHandler.isFirstTick = true; NBTTagCompound playerData = CommonProxy.getPlayerNBT(((EntityPlayer) event.entity).getDisplayName()); if (playerData != null) { SupplyPlayer.getFromEvent(event).loadNBTData(playerData); } } } @SubscribeEvent public void onLivingDeathEvent(LivingDeathEvent event) { if (!event.entity.worldObj.isRemote && event.entity instanceof EntityPlayer) { ChatUtils.sendServerMsg("Player successfully detected on death!"); NBTTagCompound playerData = new NBTTagCompound(); SupplyPlayer.getFromEvent(event).saveNBTData(playerData); CommonProxy.storePlayerNBT(((EntityPlayer) event.entity).getDisplayName(), playerData); } } } And finally, my IEEP class (pretty much entirely lifted from coolAlias's awesome IEEP tutorial): public class SupplyPlayer implements IExtendedEntityProperties { public final static String EXT_PROP_NAME = "SupplyPlayer"; private EntityPlayer player; public double supplyX, supplyY, supplyZ; public boolean hasSpawn; public SupplyPlayer(EntityPlayer player) { this.player = player; this.supplyX = 0; this.supplyY = 0; this.supplyZ = 0; this.hasSpawn = false; } public static void register(EntityPlayer player) { player.registerExtendedProperties(EXT_PROP_NAME, new SupplyPlayer(player)); } public static SupplyPlayer get(EntityPlayer player) { return (SupplyPlayer) player.getExtendedProperties(EXT_PROP_NAME); } public static SupplyPlayer getFromEvent(EntityEvent event) { return SupplyPlayer.get((EntityPlayer) event.entity); } private static String getSaveKey(EntityPlayer player) { return player.getDisplayName() + ":" + EXT_PROP_NAME; } public static void saveProxyData(EntityPlayer player) { SupplyPlayer playerData = SupplyPlayer.get(player); NBTTagCompound savedData = new NBTTagCompound(); playerData.saveNBTData(savedData); CommonProxy.storePlayerNBT(getSaveKey(player), savedData); } public static void loadProxyData(EntityPlayer player) { SupplyPlayer playerData = SupplyPlayer.get(player); NBTTagCompound savedData = CommonProxy.getPlayerNBT(getSaveKey(player)); if(savedData != null) { playerData.loadNBTData(savedData); } } public void updateCoords(int supplyX, int supplyY, int supplyZ) { this.supplyX = supplyX; this.supplyY = supplyY; this.supplyZ = supplyZ; this.hasSpawn = true; } @Override public void saveNBTData(NBTTagCompound compound) { NBTTagCompound props = new NBTTagCompound(); props.setDouble("supplyX", this.supplyX); props.setDouble("supplyY", this.supplyY); props.setDouble("supplyZ", this.supplyZ); props.setBoolean("hasSpawn", this.hasSpawn); compound.setTag(EXT_PROP_NAME, props); } // Loads NBT data @Override public void loadNBTData(NBTTagCompound compound) { NBTTagCompound props = (NBTTagCompound) compound.getTag(EXT_PROP_NAME); this.supplyX = props.getDouble("supplyX"); this.supplyY = props.getDouble("supplyY"); this.supplyZ = props.getDouble("supplyZ"); this.hasSpawn = props.getBoolean("hasSpawn"); } // Basically useless for now @Override public void init(Entity entity, World world) { } } Thanks again for the help. Hugely appreciated! Btw, if either of you have suggestions regarding my code, let me know. It's unfinished and I'm prone to making mistakes, but I'm also not the best programmer in the world, so I'm bound to overlook things.
  4. Thanks for the reply! A lot of good stuff. +1 I spent some time with the stack trace the warning was giving me which gave me some insight. [*] World.addEntityToWorld(Entity entity) is called after the entity is finished constructing. [*]Vars i and j are calculated using (entity.posX / 16.0D) and (entity.posZ / 16.0D) respectively, to be used later as chunk coordinates. [*] EntityJoinWorldEvent is fired, at this point allowing entity modification (I don't remember the method sig, but I know the entity gets passed) [*]World.getChunkFromChunkCoords(i, j).addEntity(entity) is called, meaning chunk coordinates are calculated before any position editing can be done in EntityJoinWorldEvent , making it is impossible to modify the chunk coordinates from the event. This is what made me conclude that somehow I was going about this all wrong. I was actually reviewing the Perfect Spawn mod to see how the author handled multiple spawn points. There was some mention of WorldProvider, but I otherwise didn't spend enough time with it to fully grasp it. I'll look into beds, though. Thanks! This might be viable, from what I can tell, since my main goal is high compatibility with other mods. Any visual side-effects aren't much of a concern to me. At this point, I'm just trying to prototype, and if it's really ugly, I'll change it. Thanks for this! If anyone has any other suggestions, I'd love to hear them. Otherwise, I'll post code snippets as soon as I get home so I can hopefully paint a better picture of what I'm trying to do.
  5. Hello! New to Forge. I know my way around Java, so I hope I'll be able to provide enough info for anyone willing to help me. Been pulling my hair out over this for the past 2 days. So, I've extended player properties via IExtendedEntityProperties and I've been attempting to control player spawning based on their properties that they have stored. My mod has tile entity blocks that give players its coords on activation. These tile entities store two ints: 1 that tracks the number of spawns it has facilitated since its instantiation, and how many "resource points" it has left. When the player attempts to spawn at a tile entity they are subscribed, a resource point is deducted. If the tile entity is out of points, the player defaults to world spawn. I've ran into several issues: [*]When using the EntityJoinWorldEvent , I discovered that chunk coords are calculated before the event fires in World.addEntityToWorld() , making it (seemingly) impossible to accomplish my goal this way, as I can only modify the EntityPlayer 's position, not it's chunk. Modifying the position will throw a "Wrong location!" warning [*]The strange thing is it does in fact work, but only on first spawn. When a player first joins the world, it spawns on the tile entity block initially just fine, as its pulling the data from NBT that I set in IExtendedEntityProperties , but any subsequent respawn makes the player just spawn at respawn, even though I've written working persistent properties, so I know my properties are reattaching to the player just fine at respawn. I'm sure the way I'm going about this is BEYOND hacky, so if anyone has any suggestion, by all means, let me know. I can provide code when I get home, but I was hoping I wouldn't have to do that, as some one might have an infinitely more elegant way of doing this.
×
×
  • Create New...

Important Information

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