/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.spongepowered.mod;
import com.google.common.base.Preconditions;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Stage;
import net.minecraft.launchwrapper.Launch;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.ForgeModContainer;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.client.FMLFileResourcePack;
import net.minecraftforge.fml.client.FMLFolderResourcePack;
import net.minecraftforge.fml.common.LoadController;
import net.minecraftforge.fml.common.ModContainerFactory;
import net.minecraftforge.fml.common.event.FMLConstructionEvent;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLLoadCompleteEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.event.FMLServerAboutToStartEvent;
import net.minecraftforge.fml.common.event.FMLServerStartedEvent;
import net.minecraftforge.fml.common.event.FMLServerStoppedEvent;
import net.minecraftforge.fml.common.event.FMLStateEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import net.minecraftforge.fml.common.registry.ForgeRegistries;
import net.minecraftforge.fml.common.registry.VillagerRegistry;
import org.objectweb.asm.Type;
import org.slf4j.Logger;
import org.spongepowered.api.block.BlockType;
import org.spongepowered.api.command.CommandManager;
import org.spongepowered.api.effect.potion.PotionEffectType;
import org.spongepowered.api.effect.sound.SoundType;
import org.spongepowered.api.event.Event;
import org.spongepowered.api.item.Enchantment;
import org.spongepowered.api.item.ItemType;
import org.spongepowered.api.plugin.Plugin;
import org.spongepowered.api.plugin.PluginContainer;
import org.spongepowered.api.service.permission.PermissionService;
import org.spongepowered.api.service.permission.SubjectData;
import org.spongepowered.api.service.sql.SqlService;
import org.spongepowered.api.util.Tristate;
import org.spongepowered.api.world.ChunkTicketManager;
import org.spongepowered.common.SpongeBootstrap;
import org.spongepowered.common.SpongeGame;
import org.spongepowered.common.SpongeImpl;
import org.spongepowered.common.SpongeInternalListeners;
import org.spongepowered.common.command.MinecraftCommandWrapper;
import org.spongepowered.common.entity.SpongeProfession;
import org.spongepowered.common.entity.ai.SpongeEntityAICommonSuperclass;
import org.spongepowered.common.inject.SpongeGuice;
import org.spongepowered.common.inject.SpongeModule;
import org.spongepowered.common.interfaces.IMixinServerCommandManager;
import org.spongepowered.common.interfaces.block.IMixinBlock;
import org.spongepowered.common.registry.type.BlockTypeRegistryModule;
import org.spongepowered.common.registry.type.ItemTypeRegistryModule;
import org.spongepowered.common.registry.type.effect.PotionEffectTypeRegistryModule;
import org.spongepowered.common.registry.type.effect.SoundRegistryModule;
import org.spongepowered.common.registry.type.entity.ProfessionRegistryModule;
import org.spongepowered.common.registry.type.item.EnchantmentRegistryModule;
import org.spongepowered.common.scheduler.SpongeScheduler;
import org.spongepowered.common.service.permission.SpongeContextCalculator;
import org.spongepowered.common.service.permission.SpongePermissionService;
import org.spongepowered.common.service.sql.SqlServiceImpl;
import org.spongepowered.common.util.SpongeHooks;
import org.spongepowered.common.world.WorldManager;
import org.spongepowered.common.world.storage.SpongePlayerDataHandler;
import org.spongepowered.mod.event.SpongeEventHooks;
import org.spongepowered.mod.event.SpongeModEventManager;
import org.spongepowered.mod.inject.SpongeForgeModule;
import org.spongepowered.mod.interfaces.IMixinVillagerProfession;
import org.spongepowered.mod.network.SpongeModMessageHandler;
import org.spongepowered.mod.plugin.MetaModContainer;
import org.spongepowered.mod.plugin.SpongeModPluginContainer;
import org.spongepowered.mod.registry.SpongeForgeModuleRegistry;
import org.spongepowered.mod.registry.SpongeForgeVillagerRegistry;
import org.spongepowered.mod.registry.SpongeGameData;
import org.spongepowered.mod.service.world.SpongeChunkTicketManager;
import java.io.File;
import java.io.IOException;
public class SpongeMod extends MetaModContainer {
public static SpongeMod instance;
@Inject private SpongeGame game;
@Inject private SpongeScheduler scheduler;
@Inject private Logger logger;
private LoadController controller;
private File modFile;
// This is a special Mod, provided by the IFMLLoadingPlugin. It will be
// instantiated before FML scans the system for mods (or plugins)
public SpongeMod() throws Exception {
super(SpongeModMetadata.getSpongeForgeMetadata());
// Register our special instance creator with FML
ModContainerFactory.instance().registerContainerType(Type.getType(Plugin.class), SpongeModPluginContainer.class);
SpongeMod.instance = this;
this.modFile = SpongeJava6Bridge.modFile;
// Initialize Sponge
final Stage stage = SpongeGuice.getInjectorStage((Boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment") ? Stage.DEVELOPMENT : Stage.PRODUCTION);
// Do not replace `SpongeImpl.getLogger()` with `this.getLogger()`. You've been warned.
SpongeImpl.getLogger().info("Creating injector in stage '{}'", stage);
Guice.createInjector(stage, new SpongeModule(), new SpongeForgeModule());
SpongeImpl.getRegistry().preRegistryInit();
SpongeGameData.addRegistryCallback(ForgeRegistries.BLOCKS, (obj, id, location) -> {
final ResourceLocation key = ForgeRegistries.BLOCKS.getKey(obj);
if (key == null || ((IMixinBlock) obj).isDummy()) {
return;
}
BlockTypeRegistryModule.getInstance().registerFromGameData(key.toString(), (BlockType) obj);
});
SpongeGameData.addRegistryCallback(ForgeRegistries.ITEMS, (obj, id, location) ->
ItemTypeRegistryModule.getInstance().registerFromGameData(ForgeRegistries.ITEMS.getKey(obj).toString(),
(ItemType) obj));
SpongeGameData.addRegistryCallback(ForgeRegistries.ENCHANTMENTS, (obj, id, location) ->
EnchantmentRegistryModule.getInstance().registerFromGameData(ForgeRegistries.ENCHANTMENTS.getKey(obj).toString(), (Enchantment) obj));
SpongeGameData.addRegistryCallback(ForgeRegistries.POTIONS, (obj, id, location) ->
PotionEffectTypeRegistryModule.getInstance().registerFromGameData(ForgeRegistries.POTIONS.getKey(obj).toString(),
(PotionEffectType) obj));
SpongeGameData.addRegistryCallback(ForgeRegistries.VILLAGER_PROFESSIONS, ((obj, id, slaveset) -> {
final IMixinVillagerProfession mixinProfession = (IMixinVillagerProfession) obj;
final SpongeProfession spongeProfession = new SpongeProfession(id, mixinProfession.getId(), mixinProfession.getProfessionName());
final SpongeProfession registeredProfession = SpongeForgeVillagerRegistry.validateProfession(obj, spongeProfession);
ProfessionRegistryModule.getInstance().registerAdditionalCatalog(registeredProfession);
for (VillagerRegistry.VillagerCareer career: mixinProfession.getCareers()) {
SpongeForgeVillagerRegistry.registerForgeCareer(career);
}
}));
SpongeGameData.addRegistryCallback(ForgeRegistries.SOUND_EVENTS, ((obj, id, location) -> {
SoundRegistryModule.inst().registerAdditionalCatalog((SoundType) obj);
}));
SpongeForgeModuleRegistry.registerForgeData();
this.game.getEventManager().registerListeners(this, this);
SpongeImpl.getInternalPlugins().add((PluginContainer) ForgeModContainer.getInstance());
}
@Override
public Object getMod() {
return this;
}
@Override
public File getSource() {
return this.modFile;
}
public LoadController getController() {
return this.controller;
}
private <T> void registerService(Class<T> serviceClass, T serviceImpl) {
SpongeImpl.getGame().getServiceManager().setProvider(SpongeImpl.getPlugin(), serviceClass, serviceImpl);
}
@Override
public boolean registerBus(EventBus bus, LoadController controller) {
bus.register(this);
this.controller = controller;
return true;
}
@Override
public Class<?> getCustomResourcePackClass() {
if (getSource().isDirectory()) {
return FMLFolderResourcePack.class;
} else {
return FMLFileResourcePack.class;
}
}
@Subscribe
public void onStateEvent(FMLStateEvent event) {
// We can't control Guava's event bus priority, so
// we make sure to avoid double-firing here.
if (!event.getClass().equals(FMLConstructionEvent.class)) {
((SpongeModEventManager) SpongeImpl.getGame().getEventManager()).post((Event) event, true);
}
}
@Subscribe
public void onPreInit(FMLPreInitializationEvent event) {
try {
SpongeImpl.getGame().getEventManager().registerListeners(SpongeImpl.getPlugin().getInstance().get(), SpongeInternalListeners.getInstance());
registerService(ChunkTicketManager.class, new SpongeChunkTicketManager());
SpongeBootstrap.initializeServices();
SpongeBootstrap.initializeCommands();
SpongeImpl.getRegistry().preInit();
SpongeModMessageHandler.init();
Preconditions.checkArgument(Class.forName("org.spongepowered.api.entity.ai.task.AbstractAITask").getSuperclass().equals(SpongeEntityAICommonSuperclass.class));
MinecraftForge.EVENT_BUS.register(new SpongeEventHooks());
SpongeInternalListeners.getInstance().registerServiceCallback(PermissionService.class,
input -> input.registerContextCalculator(new SpongeContextCalculator()));
// Add the SyncScheduler as a listener for ServerTickEvents
MinecraftForge.EVENT_BUS.register(this);
MinecraftForge.EVENT_BUS.register(this.game.getChannelRegistrar());
if (event.getSide().isServer()) {
SpongeHooks.enableThreadContentionMonitoring();
}
} catch (Throwable t) {
this.controller.errorOccurred(this, t);
}
}
@SubscribeEvent
public void onTick(TickEvent.ServerTickEvent event) {
if (event.phase == TickEvent.Phase.START) {
this.scheduler.tickSyncScheduler();
}
}
@Subscribe
public void onInitialization(FMLInitializationEvent event) {
try {
SpongeImpl.getRegistry().init();
if (!this.game.getServiceManager().provide(PermissionService.class).isPresent()) {
final SpongePermissionService service = new SpongePermissionService(this.game);
// Setup default permissions
service.getGroupForOpLevel(1).getSubjectData().setPermission(SubjectData.GLOBAL_CONTEXT, "minecraft.selector", Tristate.TRUE);
service.getGroupForOpLevel(2).getSubjectData().setPermission(SubjectData.GLOBAL_CONTEXT, "minecraft.commandblock", Tristate.TRUE);
this.game.getServiceManager().setProvider(this, PermissionService.class, service);
}
} catch (Throwable t) {
this.controller.errorOccurred(this, t);
}
}
@Subscribe
public void onPostInitialization(FMLPostInitializationEvent event) {
try {
SpongeImpl.getRegistry().postInit();
} catch (Throwable t) {
this.controller.errorOccurred(this, t);
}
}
@Subscribe
public void onLoadComplete(FMLLoadCompleteEvent event) {
SpongeImpl.getRegistry().registerAdditionals();
}
@Subscribe
public void onServerAboutToStart(FMLServerAboutToStartEvent event) {
try {
// Register vanilla-style commands (if necessary -- not necessary on client)
((IMixinServerCommandManager) SpongeImpl.getServer().getCommandManager()).registerEarlyCommands(this.game);
} catch (Throwable t) {
this.controller.errorOccurred(this, t);
}
// used for client
WorldManager.registerVanillaTypesAndDimensions();
}
@Subscribe
public void onServerStarted(FMLServerStartedEvent event) {
// This is intentionally called multiple times on the client -
// once for each time a new server is started (when a world is selected from the gui)
SpongePlayerDataHandler.init();
try {
((IMixinServerCommandManager) SpongeImpl.getServer().getCommandManager()).registerLowPriorityCommands(this.game);
} catch (Throwable t) {
this.controller.errorOccurred(this, t);
}
}
@Subscribe
public void onServerStopped(FMLServerStoppedEvent event) throws IOException {
try {
CommandManager service = this.game.getCommandManager();
service.getCommands().stream().filter(mapping -> mapping.getCallable() instanceof MinecraftCommandWrapper)
.forEach(service::removeMapping);
((SqlServiceImpl) this.game.getServiceManager().provideUnchecked(SqlService.class)).close();
} catch (Throwable t) {
this.controller.errorOccurred(this, t);
}
// used by client
WorldManager.unregisterAllWorldSettings();
}
// This overrides the method in PluginContainer
// (PluginContainer is implemented indirectly through the ModContainer mixin)
public Logger getLogger() {
return this.logger;
}
}