PageRenderTime 55ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java

https://gitlab.com/Skull3x/WorldEdit
Java | 442 lines | 349 code | 49 blank | 44 comment | 60 complexity | 0ce704fbed9713316c1c7550ced43513 MD5 | raw file
  1. /*
  2. * WorldEdit, a Minecraft world manipulation toolkit
  3. * Copyright (C) sk89q <http://www.sk89q.com>
  4. * Copyright (C) WorldEdit team and contributors
  5. *
  6. * This program is free software: you can redistribute it and/or modify it
  7. * under the terms of the GNU Lesser General Public License as published by the
  8. * Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful, but WITHOUT
  12. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  14. * for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package com.sk89q.worldedit.forge;
  20. import com.sk89q.jnbt.CompoundTag;
  21. import com.sk89q.worldedit.EditSession;
  22. import com.sk89q.worldedit.MaxChangedBlocksException;
  23. import com.sk89q.worldedit.Vector;
  24. import com.sk89q.worldedit.Vector2D;
  25. import com.sk89q.worldedit.WorldEditException;
  26. import com.sk89q.worldedit.blocks.BaseBlock;
  27. import com.sk89q.worldedit.blocks.BaseItemStack;
  28. import com.sk89q.worldedit.blocks.LazyBlock;
  29. import com.sk89q.worldedit.entity.BaseEntity;
  30. import com.sk89q.worldedit.entity.Entity;
  31. import com.sk89q.worldedit.internal.Constants;
  32. import com.sk89q.worldedit.regions.Region;
  33. import com.sk89q.worldedit.util.Location;
  34. import com.sk89q.worldedit.util.TreeGenerator.TreeType;
  35. import com.sk89q.worldedit.world.AbstractWorld;
  36. import com.sk89q.worldedit.world.biome.BaseBiome;
  37. import com.sk89q.worldedit.world.registry.WorldData;
  38. import java.lang.ref.WeakReference;
  39. import java.util.ArrayList;
  40. import java.util.List;
  41. import java.util.Random;
  42. import java.util.Set;
  43. import java.util.logging.Level;
  44. import java.util.logging.Logger;
  45. import javax.annotation.Nullable;
  46. import net.minecraft.block.Block;
  47. import net.minecraft.block.BlockLeaves;
  48. import net.minecraft.block.BlockOldLeaf;
  49. import net.minecraft.block.BlockOldLog;
  50. import net.minecraft.block.BlockPlanks;
  51. import net.minecraft.block.state.IBlockState;
  52. import net.minecraft.entity.EntityList;
  53. import net.minecraft.entity.item.EntityItem;
  54. import net.minecraft.init.Blocks;
  55. import net.minecraft.inventory.IInventory;
  56. import net.minecraft.nbt.NBTTagCompound;
  57. import net.minecraft.tileentity.TileEntity;
  58. import net.minecraft.util.math.BlockPos;
  59. import net.minecraft.world.ChunkCoordIntPair;
  60. import net.minecraft.world.World;
  61. import net.minecraft.world.biome.BiomeGenBase;
  62. import net.minecraft.world.chunk.Chunk;
  63. import net.minecraft.world.chunk.IChunkProvider;
  64. import net.minecraft.world.gen.ChunkProviderServer;
  65. import net.minecraft.world.gen.feature.WorldGenBigMushroom;
  66. import net.minecraft.world.gen.feature.WorldGenBigTree;
  67. import net.minecraft.world.gen.feature.WorldGenBirchTree;
  68. import net.minecraft.world.gen.feature.WorldGenCanopyTree;
  69. import net.minecraft.world.gen.feature.WorldGenMegaJungle;
  70. import net.minecraft.world.gen.feature.WorldGenMegaPineTree;
  71. import net.minecraft.world.gen.feature.WorldGenSavannaTree;
  72. import net.minecraft.world.gen.feature.WorldGenShrub;
  73. import net.minecraft.world.gen.feature.WorldGenSwamp;
  74. import net.minecraft.world.gen.feature.WorldGenTaiga1;
  75. import net.minecraft.world.gen.feature.WorldGenTaiga2;
  76. import net.minecraft.world.gen.feature.WorldGenTrees;
  77. import net.minecraft.world.gen.feature.WorldGenerator;
  78. import static com.google.common.base.Preconditions.checkNotNull;
  79. /**
  80. * An adapter to Minecraft worlds for WorldEdit.
  81. */
  82. public class ForgeWorld extends AbstractWorld {
  83. private static final Random random = new Random();
  84. private static final int UPDATE = 1, NOTIFY = 2, NOTIFY_CLIENT = 4;
  85. private static final Logger logger = Logger.getLogger(ForgeWorld.class.getCanonicalName());
  86. private static final IBlockState JUNGLE_LOG = Blocks.LOG.getDefaultState().withProperty(BlockOldLog.VARIANT, BlockPlanks.EnumType.JUNGLE);
  87. private static final IBlockState JUNGLE_LEAF = Blocks.LEAVES.getDefaultState().withProperty(BlockOldLeaf.VARIANT, BlockPlanks.EnumType.JUNGLE).withProperty(BlockLeaves.CHECK_DECAY, Boolean.valueOf(false));
  88. private static final IBlockState JUNGLE_SHRUB = Blocks.LEAVES.getDefaultState().withProperty(BlockOldLeaf.VARIANT, BlockPlanks.EnumType.OAK).withProperty(BlockLeaves.CHECK_DECAY, Boolean.valueOf(false));
  89. private final WeakReference<World> worldRef;
  90. /**
  91. * Construct a new world.
  92. *
  93. * @param world the world
  94. */
  95. ForgeWorld(World world) {
  96. checkNotNull(world);
  97. this.worldRef = new WeakReference<World>(world);
  98. }
  99. /**
  100. * Get the underlying handle to the world.
  101. *
  102. * @return the world
  103. * @throws WorldEditException thrown if a reference to the world was lost (i.e. world was unloaded)
  104. */
  105. public World getWorldChecked() throws WorldEditException {
  106. World world = worldRef.get();
  107. if (world != null) {
  108. return world;
  109. } else {
  110. throw new WorldReferenceLostException("The reference to the world was lost (i.e. the world may have been unloaded)");
  111. }
  112. }
  113. /**
  114. * Get the underlying handle to the world.
  115. *
  116. * @return the world
  117. * @throws RuntimeException thrown if a reference to the world was lost (i.e. world was unloaded)
  118. */
  119. public World getWorld() {
  120. World world = worldRef.get();
  121. if (world != null) {
  122. return world;
  123. } else {
  124. throw new RuntimeException("The reference to the world was lost (i.e. the world may have been unloaded)");
  125. }
  126. }
  127. @Override
  128. public String getName() {
  129. return getWorld().getWorldInfo().getWorldName();
  130. }
  131. @Override
  132. public boolean setBlock(Vector position, BaseBlock block, boolean notifyAndLight) throws WorldEditException {
  133. checkNotNull(position);
  134. checkNotNull(block);
  135. World world = getWorldChecked();
  136. int x = position.getBlockX();
  137. int y = position.getBlockY();
  138. int z = position.getBlockZ();
  139. // First set the block
  140. Chunk chunk = world.getChunkFromChunkCoords(x >> 4, z >> 4);
  141. BlockPos pos = new BlockPos(x, y, z);
  142. IBlockState old = chunk.getBlockState(pos);
  143. IBlockState newState = Block.getBlockById(block.getId()).getStateFromMeta(block.getData());
  144. IBlockState successState = chunk.setBlockState(pos, newState);
  145. boolean successful = successState != null;
  146. // Create the TileEntity
  147. if (successful) {
  148. if (block.hasNbtData()) {
  149. // Kill the old TileEntity
  150. world.removeTileEntity(pos);
  151. NBTTagCompound nativeTag = NBTConverter.toNative(block.getNbtData());
  152. nativeTag.setString("id", block.getNbtId());
  153. TileEntityUtils.setTileEntity(world, position, nativeTag);
  154. }
  155. }
  156. if (notifyAndLight) {
  157. if (!successful) {
  158. newState = old;
  159. }
  160. world.checkLight(pos);
  161. world.markAndNotifyBlock(pos, chunk, old, newState, UPDATE | NOTIFY);
  162. }
  163. return successful;
  164. }
  165. @Override
  166. public int getBlockLightLevel(Vector position) {
  167. checkNotNull(position);
  168. return getWorld().getLight(new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ()));
  169. }
  170. @Override
  171. public boolean clearContainerBlockContents(Vector position) {
  172. checkNotNull(position);
  173. TileEntity tile = getWorld().getTileEntity(new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ()));
  174. if ((tile instanceof IInventory)) {
  175. IInventory inv = (IInventory) tile;
  176. int size = inv.getSizeInventory();
  177. for (int i = 0; i < size; i++) {
  178. inv.setInventorySlotContents(i, null);
  179. }
  180. return true;
  181. }
  182. return false;
  183. }
  184. @Override
  185. public BaseBiome getBiome(Vector2D position) {
  186. checkNotNull(position);
  187. return new BaseBiome(BiomeGenBase.getIdForBiome(getWorld().getBiomeGenForCoords(new BlockPos(position.getBlockX(), 0, position.getBlockZ()))));
  188. }
  189. @Override
  190. public boolean setBiome(Vector2D position, BaseBiome biome) {
  191. checkNotNull(position);
  192. checkNotNull(biome);
  193. Chunk chunk = getWorld().getChunkFromBlockCoords(new BlockPos(position.getBlockX(), 0, position.getBlockZ()));
  194. if ((chunk != null) && (chunk.isLoaded())) {
  195. chunk.getBiomeArray()[((position.getBlockZ() & 0xF) << 4 | position.getBlockX() & 0xF)] = (byte) biome.getId();
  196. return true;
  197. }
  198. return false;
  199. }
  200. @Override
  201. public void dropItem(Vector position, BaseItemStack item) {
  202. checkNotNull(position);
  203. checkNotNull(item);
  204. if (item.getType() == 0) {
  205. return;
  206. }
  207. EntityItem entity = new EntityItem(getWorld(), position.getX(), position.getY(), position.getZ(), ForgeWorldEdit.toForgeItemStack(item));
  208. entity.setPickupDelay(10);
  209. getWorld().spawnEntityInWorld(entity);
  210. }
  211. @Override
  212. public boolean regenerate(Region region, EditSession editSession) {
  213. BaseBlock[] history = new BaseBlock[256 * (getMaxY() + 1)];
  214. for (Vector2D chunk : region.getChunks()) {
  215. Vector min = new Vector(chunk.getBlockX() * 16, 0, chunk.getBlockZ() * 16);
  216. for (int x = 0; x < 16; x++) {
  217. for (int y = 0; y < getMaxY() + 1; y++) {
  218. for (int z = 0; z < 16; z++) {
  219. Vector pt = min.add(x, y, z);
  220. int index = y * 16 * 16 + z * 16 + x;
  221. history[index] = editSession.getBlock(pt);
  222. }
  223. }
  224. }
  225. try {
  226. Set<Vector2D> chunks = region.getChunks();
  227. IChunkProvider provider = getWorld().getChunkProvider();
  228. if (!(provider instanceof ChunkProviderServer)) {
  229. return false;
  230. }
  231. ChunkProviderServer chunkServer = (ChunkProviderServer) provider;
  232. for (Vector2D coord : chunks) {
  233. long pos = ChunkCoordIntPair.chunkXZ2Int(coord.getBlockX(), coord.getBlockZ());
  234. Chunk mcChunk;
  235. if (chunkServer.chunkExists(coord.getBlockX(), coord.getBlockZ())) {
  236. mcChunk = chunkServer.loadChunk(coord.getBlockX(), coord.getBlockZ());
  237. mcChunk.onChunkUnload();
  238. }
  239. chunkServer.droppedChunksSet.remove(pos);
  240. chunkServer.id2ChunkMap.remove(pos);
  241. mcChunk = chunkServer.provideChunk(coord.getBlockX(), coord.getBlockZ());
  242. chunkServer.id2ChunkMap.add(pos, mcChunk);
  243. chunkServer.loadedChunks.add(mcChunk);
  244. if (mcChunk != null) {
  245. mcChunk.onChunkLoad();
  246. mcChunk.populateChunk(chunkServer, chunkServer.chunkGenerator);
  247. }
  248. }
  249. } catch (Throwable t) {
  250. logger.log(Level.WARNING, "Failed to generate chunk", t);
  251. return false;
  252. }
  253. for (int x = 0; x < 16; x++) {
  254. for (int y = 0; y < getMaxY() + 1; y++) {
  255. for (int z = 0; z < 16; z++) {
  256. Vector pt = min.add(x, y, z);
  257. int index = y * 16 * 16 + z * 16 + x;
  258. if (!region.contains(pt))
  259. editSession.smartSetBlock(pt, history[index]);
  260. else {
  261. editSession.rememberChange(pt, history[index], editSession.rawGetBlock(pt));
  262. }
  263. }
  264. }
  265. }
  266. }
  267. return false;
  268. }
  269. @Nullable
  270. private static WorldGenerator createWorldGenerator(TreeType type) {
  271. switch (type) {
  272. case TREE: return new WorldGenTrees(true);
  273. case BIG_TREE: return new WorldGenBigTree(true);
  274. case REDWOOD: return new WorldGenTaiga2(true);
  275. case TALL_REDWOOD: return new WorldGenTaiga1();
  276. case BIRCH: return new WorldGenBirchTree(true, false);
  277. case JUNGLE: return new WorldGenMegaJungle(true, 10, 20, JUNGLE_LOG, JUNGLE_LEAF);
  278. case SMALL_JUNGLE: return new WorldGenTrees(true, 4 + random.nextInt(7), JUNGLE_LOG, JUNGLE_LEAF, false);
  279. case SHORT_JUNGLE: return new WorldGenTrees(true, 4 + random.nextInt(7), JUNGLE_LOG, JUNGLE_LEAF, true);
  280. case JUNGLE_BUSH: return new WorldGenShrub(JUNGLE_LOG, JUNGLE_SHRUB);
  281. case RED_MUSHROOM: return new WorldGenBigMushroom(Blocks.BROWN_MUSHROOM_BLOCK);
  282. case BROWN_MUSHROOM: return new WorldGenBigMushroom(Blocks.RED_MUSHROOM_BLOCK);
  283. case SWAMP: return new WorldGenSwamp();
  284. case ACACIA: return new WorldGenSavannaTree(true);
  285. case DARK_OAK: return new WorldGenCanopyTree(true);
  286. case MEGA_REDWOOD: return new WorldGenMegaPineTree(false, random.nextBoolean());
  287. case TALL_BIRCH: return new WorldGenBirchTree(true, true);
  288. case RANDOM:
  289. case PINE:
  290. case RANDOM_REDWOOD:
  291. default:
  292. return null;
  293. }
  294. }
  295. @Override
  296. public boolean generateTree(TreeType type, EditSession editSession, Vector position) throws MaxChangedBlocksException {
  297. WorldGenerator generator = createWorldGenerator(type);
  298. return generator != null ? generator.generate(getWorld(), random, ForgeAdapter.toBlockPos(position)) : false;
  299. }
  300. @Override
  301. public WorldData getWorldData() {
  302. return ForgeWorldData.getInstance();
  303. }
  304. @Override
  305. public boolean isValidBlockType(int id) {
  306. return (id == 0) || (net.minecraft.block.Block.getBlockById(id) != null);
  307. }
  308. @Override
  309. public BaseBlock getBlock(Vector position) {
  310. World world = getWorld();
  311. BlockPos pos = new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ());
  312. IBlockState state = world.getBlockState(pos);
  313. TileEntity tile = getWorld().getTileEntity(pos);
  314. if (tile != null) {
  315. return new TileEntityBaseBlock(Block.getIdFromBlock(state.getBlock()), state.getBlock().getMetaFromState(state), tile);
  316. } else {
  317. return new BaseBlock(Block.getIdFromBlock(state.getBlock()), state.getBlock().getMetaFromState(state));
  318. }
  319. }
  320. @Override
  321. public BaseBlock getLazyBlock(Vector position) {
  322. World world = getWorld();
  323. BlockPos pos = new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ());
  324. IBlockState state = world.getBlockState(pos);
  325. return new LazyBlock(Block.getIdFromBlock(state.getBlock()), state.getBlock().getMetaFromState(state), this, position);
  326. }
  327. @Override
  328. public int hashCode() {
  329. return getWorld().hashCode();
  330. }
  331. @Override
  332. public boolean equals(Object o) {
  333. if (o == null) {
  334. return false;
  335. } else if ((o instanceof ForgeWorld)) {
  336. ForgeWorld other = ((ForgeWorld) o);
  337. World otherWorld = other.worldRef.get();
  338. World thisWorld = worldRef.get();
  339. return otherWorld != null && thisWorld != null && otherWorld.equals(thisWorld);
  340. } else if (o instanceof com.sk89q.worldedit.world.World) {
  341. return ((com.sk89q.worldedit.world.World) o).getName().equals(getName());
  342. } else {
  343. return false;
  344. }
  345. }
  346. @Override
  347. public List<? extends Entity> getEntities(Region region) {
  348. List<Entity> entities = new ArrayList<Entity>();
  349. for (net.minecraft.entity.Entity entity : getWorld().loadedEntityList) {
  350. if (region.contains(new Vector(entity.posX, entity.posY, entity.posZ))) {
  351. entities.add(new ForgeEntity(entity));
  352. }
  353. }
  354. return entities;
  355. }
  356. @Override
  357. public List<? extends Entity> getEntities() {
  358. List<Entity> entities = new ArrayList<Entity>();
  359. for (net.minecraft.entity.Entity entity : getWorld().loadedEntityList) {
  360. entities.add(new ForgeEntity(entity));
  361. }
  362. return entities;
  363. }
  364. @Nullable
  365. @Override
  366. public Entity createEntity(Location location, BaseEntity entity) {
  367. World world = getWorld();
  368. net.minecraft.entity.Entity createdEntity = EntityList.createEntityByName(entity.getTypeId(), world);
  369. if (createdEntity != null) {
  370. CompoundTag nativeTag = entity.getNbtData();
  371. if (nativeTag != null) {
  372. NBTTagCompound tag = NBTConverter.toNative(entity.getNbtData());
  373. for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
  374. tag.removeTag(name);
  375. }
  376. createdEntity.readFromNBT(tag);
  377. }
  378. createdEntity.setLocationAndAngles(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
  379. world.spawnEntityInWorld(createdEntity);
  380. return new ForgeEntity(createdEntity);
  381. } else {
  382. return null;
  383. }
  384. }
  385. /**
  386. * Thrown when the reference to the world is lost.
  387. */
  388. private static class WorldReferenceLostException extends WorldEditException {
  389. private WorldReferenceLostException(String message) {
  390. super(message);
  391. }
  392. }
  393. }