/*
* This file is part of ElementalArrows, licensed under the MIT License (MIT).
*
* Copyright (c) LanternPowered <https://github.com/Cybermaxke/ElementalArrows>
* 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.lanternpowered.elementalarrows;
import static org.spongepowered.api.Sponge.getEventManager;
import static org.spongepowered.api.Sponge.getRegistry;
import com.flowpowered.math.vector.Vector3d;
import com.google.common.base.Throwables;
import com.google.common.io.ByteStreams;
import com.google.inject.Inject;
import org.lanternpowered.elementalarrows.arrow.ArrowEventHandler;
import org.lanternpowered.elementalarrows.arrow.ArrowKeys;
import org.lanternpowered.elementalarrows.arrow.CustomArrow;
import org.lanternpowered.elementalarrows.arrow.event.ArrowHitEntityEvent;
import org.lanternpowered.elementalarrows.arrow.event.ArrowHitGroundEvent;
import org.lanternpowered.elementalarrows.arrow.event.ArrowShotEvent;
import org.lanternpowered.elementalarrows.event.EventActionSet;
import org.lanternpowered.elementalarrows.function.ObjectConsumer;
import org.lanternpowered.elementalarrows.function.data.AddPotionEffects;
import org.lanternpowered.elementalarrows.function.data.ModifyVelocity;
import org.lanternpowered.elementalarrows.function.data.SetDataKey;
import org.lanternpowered.elementalarrows.function.entity.DestroyEntity;
import org.lanternpowered.elementalarrows.function.locatable.CreateExplosion;
import org.lanternpowered.elementalarrows.function.locatable.PlaySound;
import org.lanternpowered.elementalarrows.function.locatable.SpawnEntity;
import org.lanternpowered.elementalarrows.function.locatable.SpawnParticles;
import org.lanternpowered.elementalarrows.item.BaseItem;
import org.lanternpowered.elementalarrows.item.ConstructableItem;
import org.lanternpowered.elementalarrows.item.InbuiltBaseItem;
import org.lanternpowered.elementalarrows.item.CustomItem;
import org.lanternpowered.elementalarrows.parser.gson.BlockStateDeserializer;
import org.lanternpowered.elementalarrows.parser.gson.CatalogTypeAdapterFactory;
import org.lanternpowered.elementalarrows.parser.gson.EventActionSetDeserializer;
import org.lanternpowered.elementalarrows.parser.gson.GsonParser;
import org.lanternpowered.elementalarrows.parser.gson.JsonTypeRegistryObjectDeserializer;
import org.lanternpowered.elementalarrows.parser.gson.ObjectConsumerDeserializer;
import org.lanternpowered.elementalarrows.parser.gson.ParticleEffectDeserializer;
import org.lanternpowered.elementalarrows.parser.gson.PotionEffectDeserializer;
import org.lanternpowered.elementalarrows.parser.gson.TextDeserializer;
import org.lanternpowered.elementalarrows.parser.gson.Vector3dDeserializer;
import org.lanternpowered.elementalarrows.registry.CatalogTypeRegistry;
import org.lanternpowered.elementalarrows.registry.SimpleCatalogTypeRegistry;
import org.lanternpowered.elementalarrows.registry.SimpleTypeRegistry;
import org.lanternpowered.elementalarrows.registry.TypeRegistry;
import org.slf4j.Logger;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.block.BlockState;
import org.spongepowered.api.config.ConfigDir;
import org.spongepowered.api.data.key.Key;
import org.spongepowered.api.effect.particle.ParticleEffect;
import org.spongepowered.api.effect.potion.PotionEffect;
import org.spongepowered.api.event.Event;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.game.state.GameInitializationEvent;
import org.spongepowered.api.event.game.state.GamePostInitializationEvent;
import org.spongepowered.api.event.game.state.GamePreInitializationEvent;
import org.spongepowered.api.item.ItemType;
import org.spongepowered.api.plugin.Plugin;
import org.spongepowered.api.plugin.PluginContainer;
import org.spongepowered.api.text.Text;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Pattern;
@Plugin(id = "elemental_arrows")
public final class ElementalArrowsPlugin {
/**
* The registry of all the {@link ObjectConsumer}s classes.
*/
private final TypeRegistry<ObjectConsumer> objectConsumersTypeRegistry = new SimpleTypeRegistry<>();
/**
* The registry of all the {@link BaseItem}s classes.
*/
private final TypeRegistry<BaseItem> itemTypeRegistry = new SimpleTypeRegistry<>();
/**
* The registry of all the {@link BaseItem}s.
*/
private final CatalogTypeRegistry<BaseItem> itemRegistry = new SimpleCatalogTypeRegistry<>();
private final TypeRegistry<Event> eventTypeRegistry = new SimpleTypeRegistry<>();
@Inject
private PluginContainer pluginContainer;
@Inject
private Logger logger;
@Inject
@ConfigDir(sharedRoot = false)
private Path configFolder;
@Listener
public void onPreInit(GamePreInitializationEvent event) {
}
@Listener
public void onInit(GameInitializationEvent event) {
// Extract the default configs
final JarFile jarFile;
try {
jarFile = new JarFile(this.pluginContainer.getSource().get().toFile());
} catch (IOException e) {
throw Throwables.propagate(e);
}
final Pattern jsonPattern = Pattern.compile(".+");
final Path assets = this.configFolder.resolve("assets");
if (!Files.exists(assets)) {
try {
extractFolder(jarFile, "assets", assets, jsonPattern);
} catch (IOException e) {
throw new IllegalStateException("Unable to extract assets", e);
}
}
// Object consumer types
this.objectConsumersTypeRegistry.register("add-potion-effects", AddPotionEffects.class);
this.objectConsumersTypeRegistry.register("modify-velocity", ModifyVelocity.class);
this.objectConsumersTypeRegistry.register("set-data-key", SetDataKey.class);
this.objectConsumersTypeRegistry.register("destroy-entity", DestroyEntity.class);
this.objectConsumersTypeRegistry.register("create-explosion", CreateExplosion.class);
this.objectConsumersTypeRegistry.register("play-sound", PlaySound.class);
this.objectConsumersTypeRegistry.register("spawn-entity", SpawnEntity.class);
this.objectConsumersTypeRegistry.register("spawn-particles", SpawnParticles.class);
// Base item types
this.itemTypeRegistry.register("base-item", CustomItem.class);
this.itemTypeRegistry.register("base-arrow", CustomArrow.class);
this.eventTypeRegistry.register("arrow-hit-entity", ArrowHitEntityEvent.class);
this.eventTypeRegistry.register("arrow-hit-ground", ArrowHitGroundEvent.class);
this.eventTypeRegistry.register("arrow-shot", ArrowShotEvent.class);
getRegistry().registerModule(BaseItem.class, this.itemRegistry);
// Register the default item types
getRegistry().getAllOf(ItemType.class)
.forEach(type -> this.itemRegistry.register(type.getId(), new InbuiltBaseItem(type)));
getRegistry().register(Key.class, ArrowKeys.ARROW_TYPE);
getEventManager().registerListeners(this, new ArrowEventHandler());
// Setup the gson parser
final GsonParser gsonParser = new GsonParser();
gsonParser.registerTypeAdapter(BlockState.class, new BlockStateDeserializer());
gsonParser.registerTypeAdapterFactory(new CatalogTypeAdapterFactory());
gsonParser.registerTypeAdapter(ConstructableItem.class, new JsonTypeRegistryObjectDeserializer<>(this.itemTypeRegistry));
gsonParser.registerTypeAdapter(BaseItem.class, new JsonTypeRegistryObjectDeserializer<>(this.itemTypeRegistry));
gsonParser.registerTypeAdapter(PotionEffect.class, new PotionEffectDeserializer());
gsonParser.registerTypeAdapter(ParticleEffect.class, new ParticleEffectDeserializer());
gsonParser.registerTypeAdapter(Text.class, new TextDeserializer());
gsonParser.registerTypeAdapter(Vector3d.class, new Vector3dDeserializer());
gsonParser.registerTypeAdapter(ModifyVelocity.Type.class, new ModifyVelocity.TypeDeserializer());
gsonParser.registerTypeAdapter(EventActionSet.class, new EventActionSetDeserializer(this.eventTypeRegistry));
gsonParser.registerTypeAdapter(ObjectConsumer.class, new ObjectConsumerDeserializer(this.objectConsumersTypeRegistry));
try {
Files.list(assets).forEach(path -> {
final Path items = path.resolve("items");
if (Files.exists(items)) {
try {
Files.list(items).forEach(item -> {
if (!item.getFileName().toString().endsWith(".json")) {
return;
}
try (BufferedReader reader = Files.newBufferedReader(item)) {
try {
final BaseItem baseItem = gsonParser.getGson().fromJson(reader, ConstructableItem.class);
this.itemRegistry.register(baseItem.getId(), baseItem);
this.logger.info("Successfully registered the BaseItem with id {} from the path: {}", baseItem.getId(), item);
} catch (Exception e) {
this.logger.error("Failed to parse the BaseItem from the path: {}", item, e);
}
} catch (IOException e) {
throw Throwables.propagate(e);
}
});
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
});
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
@Listener
public void onPostInit(GamePostInitializationEvent event) {
}
private static void extractFolder(JarFile jarFile, String folder, Path outputFolder, Pattern namePattern) throws IOException {
if (!folder.endsWith("/")) {
folder = folder + "/";
}
final java.util.Enumeration<JarEntry> it = jarFile.entries();
while (it.hasMoreElements()) {
final JarEntry entry = it.nextElement();
if (entry.isDirectory()) {
continue;
}
String name = entry.getName();
if (!name.startsWith(folder)) {
continue;
}
name = name.replaceFirst(folder, "");
final String name0;
if (name.contains("/")) {
name0 = name.substring(name.lastIndexOf('/') + 1, name.length());
} else {
name0 = name;
}
if (!namePattern.matcher(name0).matches()) {
continue;
}
final Path path = outputFolder.resolve(name);
if (!Files.exists(path.getParent())) {
Files.createDirectories(path.getParent());
}
try (OutputStream os = Files.newOutputStream(path); InputStream is = jarFile.getInputStream(entry)) {
ByteStreams.copy(is, os);
os.flush();
}
}
}
}