PageRenderTime 42ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

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

http://github.com/sk89q/worldedit
Java | 417 lines | 298 code | 52 blank | 67 comment | 56 complexity | 6938acf34aeab14b35347eb5bbc8d1a2 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
  7. * it under the terms of the GNU General Public License as published by
  8. * the 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,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. package com.sk89q.worldedit.forge;
  20. import com.mojang.brigadier.ParseResults;
  21. import com.mojang.brigadier.exceptions.CommandSyntaxException;
  22. import com.sk89q.worldedit.LocalSession;
  23. import com.sk89q.worldedit.WorldEdit;
  24. import com.sk89q.worldedit.command.util.PermissionCondition;
  25. import com.sk89q.worldedit.event.platform.PlatformReadyEvent;
  26. import com.sk89q.worldedit.event.platform.PlatformUnreadyEvent;
  27. import com.sk89q.worldedit.event.platform.PlatformsRegisteredEvent;
  28. import com.sk89q.worldedit.event.platform.SessionIdleEvent;
  29. import com.sk89q.worldedit.extension.platform.Capability;
  30. import com.sk89q.worldedit.extension.platform.Platform;
  31. import com.sk89q.worldedit.extension.platform.PlatformManager;
  32. import com.sk89q.worldedit.forge.net.handler.InternalPacketHandler;
  33. import com.sk89q.worldedit.forge.net.handler.WECUIPacketHandler;
  34. import com.sk89q.worldedit.forge.net.packet.LeftClickAirEventMessage;
  35. import com.sk89q.worldedit.internal.anvil.ChunkDeleter;
  36. import com.sk89q.worldedit.internal.util.LogManagerCompat;
  37. import com.sk89q.worldedit.util.Direction;
  38. import com.sk89q.worldedit.util.Location;
  39. import com.sk89q.worldedit.world.biome.BiomeType;
  40. import com.sk89q.worldedit.world.block.BlockCategory;
  41. import com.sk89q.worldedit.world.block.BlockType;
  42. import com.sk89q.worldedit.world.entity.EntityType;
  43. import com.sk89q.worldedit.world.item.ItemCategory;
  44. import com.sk89q.worldedit.world.item.ItemType;
  45. import net.minecraft.commands.CommandSourceStack;
  46. import net.minecraft.core.Registry;
  47. import net.minecraft.resources.ResourceLocation;
  48. import net.minecraft.server.MinecraftServer;
  49. import net.minecraft.server.level.ServerLevel;
  50. import net.minecraft.server.level.ServerPlayer;
  51. import net.minecraft.tags.BlockTags;
  52. import net.minecraft.tags.ItemTags;
  53. import net.minecraft.world.InteractionHand;
  54. import net.minecraftforge.common.MinecraftForge;
  55. import net.minecraftforge.event.CommandEvent;
  56. import net.minecraftforge.event.RegisterCommandsEvent;
  57. import net.minecraftforge.event.entity.player.PlayerEvent;
  58. import net.minecraftforge.event.entity.player.PlayerInteractEvent;
  59. import net.minecraftforge.event.entity.player.PlayerInteractEvent.LeftClickEmpty;
  60. import net.minecraftforge.eventbus.api.Event;
  61. import net.minecraftforge.eventbus.api.IEventBus;
  62. import net.minecraftforge.eventbus.api.SubscribeEvent;
  63. import net.minecraftforge.fml.IExtensionPoint;
  64. import net.minecraftforge.fml.ModContainer;
  65. import net.minecraftforge.fml.ModLoadingContext;
  66. import net.minecraftforge.fml.common.Mod;
  67. import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
  68. import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
  69. import net.minecraftforge.fml.loading.FMLPaths;
  70. import net.minecraftforge.fmllegacy.network.FMLNetworkConstants;
  71. import net.minecraftforge.fmlserverevents.FMLServerAboutToStartEvent;
  72. import net.minecraftforge.fmlserverevents.FMLServerStartedEvent;
  73. import net.minecraftforge.fmlserverevents.FMLServerStoppingEvent;
  74. import net.minecraftforge.registries.ForgeRegistries;
  75. import org.apache.logging.log4j.Logger;
  76. import org.enginehub.piston.Command;
  77. import java.io.IOException;
  78. import java.io.UncheckedIOException;
  79. import java.lang.reflect.InvocationTargetException;
  80. import java.nio.file.Files;
  81. import java.nio.file.Path;
  82. import java.util.Collections;
  83. import java.util.List;
  84. import java.util.Set;
  85. import java.util.function.Supplier;
  86. import static com.google.common.base.Preconditions.checkNotNull;
  87. import static com.sk89q.worldedit.forge.ForgeAdapter.adaptPlayer;
  88. import static com.sk89q.worldedit.internal.anvil.ChunkDeleter.DELCHUNKS_FILE_NAME;
  89. import static java.util.stream.Collectors.toList;
  90. /**
  91. * The Forge implementation of WorldEdit.
  92. */
  93. @Mod(ForgeWorldEdit.MOD_ID)
  94. public class ForgeWorldEdit {
  95. private static final Logger LOGGER = LogManagerCompat.getLogger();
  96. public static final String MOD_ID = "worldedit";
  97. public static final String CUI_PLUGIN_CHANNEL = "cui";
  98. private ForgePermissionsProvider provider;
  99. public static ForgeWorldEdit inst;
  100. private ForgePlatform platform;
  101. private ForgeConfiguration config;
  102. private Path workingDir;
  103. private ModContainer container;
  104. public ForgeWorldEdit() {
  105. inst = this;
  106. IEventBus modBus = FMLJavaModLoadingContext.get().getModEventBus();
  107. modBus.addListener(this::init);
  108. MinecraftForge.EVENT_BUS.register(ThreadSafeCache.getInstance());
  109. MinecraftForge.EVENT_BUS.register(this);
  110. // Mark WorldEdit as only required on the server
  111. try {
  112. // TODO compile under --release 16 and call this normally in 7.3.0
  113. ModLoadingContext.class.getDeclaredMethod("registerExtensionPoint", Class.class, Supplier.class)
  114. .invoke(
  115. ModLoadingContext.get(),
  116. IExtensionPoint.DisplayTest.class,
  117. (Supplier<?>) () -> new IExtensionPoint.DisplayTest(
  118. () -> FMLNetworkConstants.IGNORESERVERONLY,
  119. (a, b) -> true
  120. )
  121. );
  122. } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
  123. throw new IllegalStateException(e);
  124. }
  125. }
  126. private void init(FMLCommonSetupEvent event) {
  127. this.container = ModLoadingContext.get().getActiveContainer();
  128. // Setup working directory
  129. workingDir = FMLPaths.CONFIGDIR.get().resolve("worldedit");
  130. if (!Files.exists(workingDir)) {
  131. try {
  132. Files.createDirectory(workingDir);
  133. } catch (IOException e) {
  134. throw new UncheckedIOException(e);
  135. }
  136. }
  137. setupPlatform();
  138. WECUIPacketHandler.init();
  139. InternalPacketHandler.init();
  140. LOGGER.info("WorldEdit for Forge (version " + getInternalVersion() + ") is loaded");
  141. }
  142. private void setupPlatform() {
  143. this.platform = new ForgePlatform(this);
  144. WorldEdit.getInstance().getPlatformManager().register(platform);
  145. config = new ForgeConfiguration(this);
  146. // TODO if (ModList.get().isLoaded("sponge")) {
  147. // this.provider = new ForgePermissionsProvider.SpongePermissionsProvider();
  148. // } else {
  149. this.provider = new ForgePermissionsProvider.VanillaPermissionsProvider(platform);
  150. // }
  151. }
  152. private void setupRegistries(MinecraftServer server) {
  153. // Blocks
  154. for (ResourceLocation name : ForgeRegistries.BLOCKS.getKeys()) {
  155. if (BlockType.REGISTRY.get(name.toString()) == null) {
  156. BlockType.REGISTRY.register(name.toString(), new BlockType(name.toString(),
  157. input -> ForgeAdapter.adapt(ForgeAdapter.adapt(input.getBlockType()).defaultBlockState())));
  158. }
  159. }
  160. // Items
  161. for (ResourceLocation name : ForgeRegistries.ITEMS.getKeys()) {
  162. if (ItemType.REGISTRY.get(name.toString()) == null) {
  163. ItemType.REGISTRY.register(name.toString(), new ItemType(name.toString()));
  164. }
  165. }
  166. // Entities
  167. for (ResourceLocation name : ForgeRegistries.ENTITIES.getKeys()) {
  168. if (EntityType.REGISTRY.get(name.toString()) == null) {
  169. EntityType.REGISTRY.register(name.toString(), new EntityType(name.toString()));
  170. }
  171. }
  172. // Biomes
  173. for (ResourceLocation name : server.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY).keySet()) {
  174. if (BiomeType.REGISTRY.get(name.toString()) == null) {
  175. BiomeType.REGISTRY.register(name.toString(), new BiomeType(name.toString()));
  176. }
  177. }
  178. // Tags
  179. for (ResourceLocation name : BlockTags.getAllTags().getAvailableTags()) {
  180. if (BlockCategory.REGISTRY.get(name.toString()) == null) {
  181. BlockCategory.REGISTRY.register(name.toString(), new BlockCategory(name.toString()));
  182. }
  183. }
  184. for (ResourceLocation name : ItemTags.getAllTags().getAvailableTags()) {
  185. if (ItemCategory.REGISTRY.get(name.toString()) == null) {
  186. ItemCategory.REGISTRY.register(name.toString(), new ItemCategory(name.toString()));
  187. }
  188. }
  189. }
  190. @SubscribeEvent
  191. public void registerCommands(RegisterCommandsEvent event) {
  192. WorldEdit.getInstance().getEventBus().post(new PlatformsRegisteredEvent());
  193. PlatformManager manager = WorldEdit.getInstance().getPlatformManager();
  194. Platform commandsPlatform = manager.queryCapability(Capability.USER_COMMANDS);
  195. if (commandsPlatform != platform || !platform.isHookingEvents()) {
  196. // We're not in control of commands/events -- do not register.
  197. return;
  198. }
  199. List<Command> commands = manager.getPlatformCommandManager().getCommandManager()
  200. .getAllCommands().collect(toList());
  201. for (Command command : commands) {
  202. CommandWrapper.register(event.getDispatcher(), command);
  203. Set<String> perms = command.getCondition().as(PermissionCondition.class)
  204. .map(PermissionCondition::getPermissions)
  205. .orElseGet(Collections::emptySet);
  206. if (!perms.isEmpty()) {
  207. perms.forEach(getPermissionsProvider()::registerPermission);
  208. }
  209. }
  210. }
  211. @SubscribeEvent
  212. public void serverAboutToStart(FMLServerAboutToStartEvent event) {
  213. final Path delChunks = workingDir.resolve(DELCHUNKS_FILE_NAME);
  214. if (Files.exists(delChunks)) {
  215. ChunkDeleter.runFromFile(delChunks, true);
  216. }
  217. }
  218. @SubscribeEvent
  219. public void serverStopping(FMLServerStoppingEvent event) {
  220. WorldEdit worldEdit = WorldEdit.getInstance();
  221. worldEdit.getSessionManager().unload();
  222. WorldEdit.getInstance().getEventBus().post(new PlatformUnreadyEvent(platform));
  223. }
  224. @SubscribeEvent
  225. public void serverStarted(FMLServerStartedEvent event) {
  226. setupRegistries(event.getServer());
  227. config.load();
  228. WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent(platform));
  229. }
  230. @SubscribeEvent
  231. public void onPlayerInteract(PlayerInteractEvent event) {
  232. if (platform == null) {
  233. return;
  234. }
  235. if (!platform.isHookingEvents()) {
  236. return; // We have to be told to catch these events
  237. }
  238. if (event.getWorld().isClientSide && event instanceof LeftClickEmpty) {
  239. // catch LCE, pass it to server
  240. InternalPacketHandler.getHandler().sendToServer(LeftClickAirEventMessage.INSTANCE);
  241. return;
  242. }
  243. boolean isLeftDeny = event instanceof PlayerInteractEvent.LeftClickBlock
  244. && ((PlayerInteractEvent.LeftClickBlock) event)
  245. .getUseItem() == Event.Result.DENY;
  246. boolean isRightDeny =
  247. event instanceof PlayerInteractEvent.RightClickBlock
  248. && ((PlayerInteractEvent.RightClickBlock) event)
  249. .getUseItem() == Event.Result.DENY;
  250. if (isLeftDeny || isRightDeny || event.getEntity().level.isClientSide || event.getHand() == InteractionHand.OFF_HAND) {
  251. return;
  252. }
  253. WorldEdit we = WorldEdit.getInstance();
  254. ForgePlayer player = adaptPlayer((ServerPlayer) event.getPlayer());
  255. ForgeWorld world = getWorld((ServerLevel) event.getPlayer().level);
  256. Direction direction = ForgeAdapter.adaptEnumFacing(event.getFace());
  257. if (event instanceof PlayerInteractEvent.LeftClickEmpty) {
  258. we.handleArmSwing(player); // this event cannot be canceled
  259. } else if (event instanceof PlayerInteractEvent.LeftClickBlock) {
  260. Location pos = new Location(world, event.getPos().getX(), event.getPos().getY(), event.getPos().getZ());
  261. if (we.handleBlockLeftClick(player, pos, direction)) {
  262. event.setCanceled(true);
  263. }
  264. if (we.handleArmSwing(player)) {
  265. event.setCanceled(true);
  266. }
  267. } else if (event instanceof PlayerInteractEvent.RightClickBlock) {
  268. Location pos = new Location(world, event.getPos().getX(), event.getPos().getY(), event.getPos().getZ());
  269. if (we.handleBlockRightClick(player, pos, direction)) {
  270. event.setCanceled(true);
  271. }
  272. if (we.handleRightClick(player)) {
  273. event.setCanceled(true);
  274. }
  275. } else if (event instanceof PlayerInteractEvent.RightClickItem) {
  276. if (we.handleRightClick(player)) {
  277. event.setCanceled(true);
  278. }
  279. }
  280. }
  281. @SubscribeEvent
  282. public void onCommandEvent(CommandEvent event) throws CommandSyntaxException {
  283. ParseResults<CommandSourceStack> parseResults = event.getParseResults();
  284. if (!(parseResults.getContext().getSource().getEntity() instanceof ServerPlayer)) {
  285. return;
  286. }
  287. ServerPlayer player = parseResults.getContext().getSource().getPlayerOrException();
  288. if (player.level.isClientSide) {
  289. return;
  290. }
  291. if (parseResults.getContext().getCommand() != CommandWrapper.FAKE_COMMAND) {
  292. return;
  293. }
  294. event.setCanceled(true);
  295. WorldEdit.getInstance().getEventBus().post(new com.sk89q.worldedit.event.platform.CommandEvent(
  296. adaptPlayer(parseResults.getContext().getSource().getPlayerOrException()),
  297. parseResults.getReader().getString()
  298. ));
  299. }
  300. @SubscribeEvent
  301. public void onPlayerLogOut(PlayerEvent.PlayerLoggedOutEvent event) {
  302. if (event.getPlayer() instanceof ServerPlayer) {
  303. WorldEdit.getInstance().getEventBus()
  304. .post(new SessionIdleEvent(new ForgePlayer.SessionKeyImpl((ServerPlayer) event.getPlayer())));
  305. }
  306. }
  307. /**
  308. * Get the configuration.
  309. *
  310. * @return the Forge configuration
  311. */
  312. ForgeConfiguration getConfig() {
  313. return this.config;
  314. }
  315. /**
  316. * Get the session for a player.
  317. *
  318. * @param player the player
  319. * @return the session
  320. */
  321. public LocalSession getSession(ServerPlayer player) {
  322. checkNotNull(player);
  323. return WorldEdit.getInstance().getSessionManager().get(adaptPlayer(player));
  324. }
  325. /**
  326. * Get the WorldEdit proxy for the given world.
  327. *
  328. * @param world the world
  329. * @return the WorldEdit world
  330. */
  331. public ForgeWorld getWorld(ServerLevel world) {
  332. checkNotNull(world);
  333. return new ForgeWorld(world);
  334. }
  335. /**
  336. * Get the WorldEdit proxy for the platform.
  337. *
  338. * @return the WorldEdit platform
  339. */
  340. public Platform getPlatform() {
  341. return this.platform;
  342. }
  343. /**
  344. * Get the working directory where WorldEdit's files are stored.
  345. *
  346. * @return the working directory
  347. */
  348. public Path getWorkingDir() {
  349. return this.workingDir;
  350. }
  351. /**
  352. * Get the version of the WorldEdit-for-Forge implementation.
  353. *
  354. * @return a version string
  355. */
  356. String getInternalVersion() {
  357. return container.getModInfo().getVersion().toString();
  358. }
  359. public void setPermissionsProvider(ForgePermissionsProvider provider) {
  360. this.provider = provider;
  361. }
  362. public ForgePermissionsProvider getPermissionsProvider() {
  363. return provider;
  364. }
  365. }