package com.rwtema.funkylocomotion.fakes; import com.mojang.authlib.GameProfile; import com.rwtema.funkylocomotion.blocks.TileMovingClient; import com.rwtema.funkylocomotion.helper.BlockStates; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ChunkProviderClient; import net.minecraft.client.multiplayer.WorldClient; import net.minecraft.client.network.NetHandlerPlayClient; import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReportCategory; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.EnumPacketDirection; import net.minecraft.network.NetworkManager; import net.minecraft.network.Packet; import net.minecraft.server.MinecraftServer; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.SoundCategory; import net.minecraft.util.SoundEvent; import net.minecraft.util.math.BlockPos; import net.minecraft.world.EnumSkyBlock; import net.minecraft.world.World; import net.minecraft.world.WorldSettings; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.IChunkProvider; import net.minecraftforge.common.DimensionManager; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.HashMap; import java.util.Random; import java.util.UUID; @SideOnly(Side.CLIENT) public class FakeWorldClient extends WorldClient { private static final HashMap<World, FakeWorldClient> cache = new HashMap<>(); private static GameProfile MINECRAFT = new GameProfile(UUID.fromString("41C82C87-7AfB-4024-BA57-13D2C99CAE77"), "[Minecraft]"); final World world; final WorldClient worldClient; public double offset = 0; public int dir_id = -1; @Nullable public EnumFacing dir = null; public HashMap<BlockPos, IBlockState> blockstateOverides = new HashMap<>(); public HashMap<BlockPos, TileEntity> tileOverides = new HashMap<>(); public HashMap<BlockPos, TileMovingClient> tileClientOverides = new HashMap<>(); private FakeWorldClient(World world) { super(new NetHandlerPlayClient(Minecraft.getMinecraft(), null, new NetworkManager(EnumPacketDirection.SERVERBOUND), MINECRAFT ), new WorldSettings(world.getWorldInfo()), world.provider.getDimension(), world.getDifficulty(), world.profiler); this.world = world; this.worldClient = world instanceof WorldClient ? ((WorldClient) world) : null; } public static boolean isValid(World world) { return world != null && world.provider != null && DimensionManager.isDimensionRegistered(world.provider.getDimension()); } public static FakeWorldClient getFakeWorldWrapper(World world) { FakeWorldClient fakeWorldClient = cache.get(world); if (fakeWorldClient == null) { if (world instanceof FakeWorldClient) fakeWorldClient = (FakeWorldClient) world; else fakeWorldClient = new FakeWorldClient(world); cache.put(world, fakeWorldClient); } return fakeWorldClient; } public static void register() { MinecraftForge.EVENT_BUS.register(new FakeWorldManager()); } @Override public boolean isBlockNormalCube(BlockPos pos, boolean _default) { IBlockState state = getBlockState(pos); return state.getBlock().isNormalCube(state, this, pos); } @Nonnull @Override public Chunk getChunkFromChunkCoords(int x, int z) { return world.getChunkFromChunkCoords(x, z); } @Nonnull @Override protected IChunkProvider createChunkProvider() { return null; } @Override public Entity getEntityByID(int id) { return world.getEntityByID(id); } public TileMovingClient getTile(BlockPos pos) { TileMovingClient overrideTile = tileClientOverides.get(pos); if (overrideTile != null) return overrideTile; TileEntity tile = world.getTileEntity(pos); return (tile != null && tile.getClass() == TileMovingClient.class) && ((TileMovingClient) tile).dir == dir_id ? (TileMovingClient) tile : null; } @Nonnull @Override public IBlockState getBlockState(BlockPos pos) { IBlockState state = blockstateOverides.get(pos); if (state != null) return state; TileMovingClient tile = getTile(pos); if (tile != null) return tile.getState(); return BlockStates.AIR; } public Block getBlock(BlockPos pos) { return getBlockState(pos).getBlock(); } @Override public TileEntity getTileEntity(BlockPos pos) { TileEntity tileEntity = tileOverides.get(pos); if (tileEntity != null) return tileEntity; TileMovingClient tileMovingClient = getTile(pos); return tileMovingClient == null ? null : tileMovingClient.tile; } @Override public boolean setBlockState(BlockPos pos, @Nonnull IBlockState newState, int flags) { return false; } @Override public void notifyLightSet(@Nonnull BlockPos pos) { } @Override public int getLight(BlockPos pos) { return super.getLight(pos); } @Override public int getLightFor(@Nonnull EnumSkyBlock type, BlockPos pos) { return world.getLightFor(type, pos); } @Override public void sendPacketToServer(@Nonnull Packet<?> packetIn) { } @Nonnull @Override public ChunkProviderClient getChunkProvider() { return worldClient.getChunkProvider(); } @Override public void invalidateBlockReceiveRegion(int x1, int y1, int z1, int x2, int y2, int z2) { worldClient.invalidateBlockReceiveRegion(x1, y1, z1, x2, y2, z2); } @Override public void showBarrierParticles(int p_184153_1_, int p_184153_2_, int p_184153_3_, int p_184153_4_, @Nonnull Random random, boolean p_184153_6_, BlockPos.MutableBlockPos pos) { worldClient.showBarrierParticles(p_184153_1_, p_184153_2_, p_184153_3_, p_184153_4_, random, p_184153_6_, pos); } @Override public void playSound(@Nullable EntityPlayer player, double x, double y, double z, @Nonnull SoundEvent soundIn, @Nonnull SoundCategory category, float volume, float pitch) { worldClient.playSound(player, x, y, z, soundIn, category, volume, pitch); } @Override public void playSound(BlockPos pos, @Nonnull SoundEvent soundIn, @Nonnull SoundCategory category, float volume, float pitch, boolean distanceDelay) { worldClient.playSound(pos, soundIn, category, volume, pitch, distanceDelay); } @Override public boolean isAirBlock(@Nonnull BlockPos pos) { IBlockState state = getBlockState(pos); return state.getMaterial() == Material.AIR || state.getBlock().isAir(state, this, pos); } @Override @SideOnly(Side.CLIENT) public int getHeight() { return world.getHeight(); } @Override public boolean isSideSolid(BlockPos pos, @Nonnull EnumFacing side, boolean _default) { IBlockState state = getBlockState(pos); return state.getBlock().isSideSolid(state, this, pos, side); } @Override public boolean spawnEntity(@Nonnull Entity entity) { return false; } @Override public void spawnParticle(EnumParticleTypes particleType, boolean ignoreRange, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed, @Nonnull int... parameters) { world.spawnParticle(particleType, ignoreRange, x + getXOffset(), y + getYOffset(), z + getZOffset(), xSpeed, ySpeed, zSpeed, parameters); } @Override public void spawnParticle(EnumParticleTypes particleType, double xCoord, double yCoord, double zCoord, double xSpeed, double ySpeed, double zSpeed, @Nonnull int... parameters) { world.spawnParticle(particleType, xCoord + getXOffset(), yCoord + getYOffset(), zCoord + getZOffset(), xSpeed, ySpeed, zSpeed, parameters); } private double getZOffset() { if (dir == null) return 0; return offset * this.dir.getFrontOffsetZ(); } private double getYOffset() { if (dir == null) return 0; return offset * this.dir.getFrontOffsetY(); } private double getXOffset() { if (dir == null) return 0; return offset * this.dir.getFrontOffsetX(); } @Override public void tick() { } @Nonnull @Override public CrashReportCategory addWorldInfoToCrashReport(CrashReport crash) { CrashReportCategory crashReportCategory = world.addWorldInfoToCrashReport(crash); crashReportCategory.addCrashSection("Fake World", "This world is a fake wrapper used by Funky Locomotion"); return crashReportCategory; } @Override public void doVoidFogParticles(int posX, int posY, int posZ) { if (worldClient != null) worldClient.doVoidFogParticles(posX, posY, posZ); } @Override public void makeFireworks(double x, double y, double z, double vx, double vy, double vz, NBTTagCompound tag) { if (worldClient != null) worldClient.makeFireworks(x, y, z, vx, vy, vz, tag); } @Override public void removeEntity(Entity entity) { } @Override public void addEntityToWorld(int id, Entity entity) { } @Nonnull @Override public Entity removeEntityFromWorld(int entity) { return null; } @Override public void playSound(double x, double y, double z, SoundEvent soundIn, @Nonnull SoundCategory category, float volume, float pitch, boolean distanceDelay) { if (worldClient != null) worldClient.playSound(x, y, z, soundIn, category, volume, pitch, distanceDelay); } @Override public void removeAllEntities() { } @Override public void doPreChunk(int x, int z, boolean load) { } @Override public void sendQuittingDisconnectingPacket() { world.sendQuittingDisconnectingPacket(); } @SideOnly(Side.CLIENT) public static class FakeWorldManager { @SubscribeEvent public void onDimensionUnload(WorldEvent.Unload event) { cache.remove(event.getWorld()); MinecraftServer server = FMLCommonHandler.instance().getMinecraftServerInstance(); if (server != null && !server.isServerRunning()) { cache.clear(); } } } }