Merge pull request #96 from D3v1s0m/2.X
End Crystal, display name property (to remove), small fix, Added SQLite Storage
This commit is contained in:
		
						commit
						0873ddab45
					
				
					 16 changed files with 659 additions and 10 deletions
				
			
		
							
								
								
									
										46
									
								
								api/src/main/java/lol/pyr/znpcsplus/util/Vector3i.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								api/src/main/java/lol/pyr/znpcsplus/util/Vector3i.java
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,46 @@ | ||||||
|  | package lol.pyr.znpcsplus.util; | ||||||
|  | 
 | ||||||
|  | public class Vector3i { | ||||||
|  |     private final int x; | ||||||
|  |     private final int y; | ||||||
|  |     private final int z; | ||||||
|  | 
 | ||||||
|  |     public Vector3i(int x, int y, int z) { | ||||||
|  |         this.x = x; | ||||||
|  |         this.y = y; | ||||||
|  |         this.z = z; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public int getX() { | ||||||
|  |         return this.x; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public int getY() { | ||||||
|  |         return this.y; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public int getZ() { | ||||||
|  |         return this.z; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String toString() { | ||||||
|  |         return this.x + "," + this.y + "," + this.z; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String toPrettyString() { | ||||||
|  |         return "(" + this.x + ", " + this.y + ", " + this.z + ")"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static Vector3i fromString(String s) { | ||||||
|  |         String[] split = s.split(","); | ||||||
|  |         if (split.length < 3) { | ||||||
|  |             return null; | ||||||
|  |         } else { | ||||||
|  |             try { | ||||||
|  |                 return new Vector3i(Integer.parseInt(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2])); | ||||||
|  |             } catch (NumberFormatException var3) { | ||||||
|  |                 return null; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -251,6 +251,7 @@ public class ZNpcsPlus extends JavaPlugin { | ||||||
|         manager.registerParser(Color.class, new ColorParser(incorrectUsageMessage)); |         manager.registerParser(Color.class, new ColorParser(incorrectUsageMessage)); | ||||||
|         manager.registerParser(Vector3f.class, new Vector3fParser(incorrectUsageMessage)); |         manager.registerParser(Vector3f.class, new Vector3fParser(incorrectUsageMessage)); | ||||||
|         manager.registerParser(String.class, new StringParser(incorrectUsageMessage)); |         manager.registerParser(String.class, new StringParser(incorrectUsageMessage)); | ||||||
|  |         manager.registerParser(Vector3i.class, new Vector3iParser(incorrectUsageMessage)); | ||||||
| 
 | 
 | ||||||
|         // TODO: Need to find a better way to do this |         // TODO: Need to find a better way to do this | ||||||
|         registerEnumParser(manager, NpcPose.class, incorrectUsageMessage); |         registerEnumParser(manager, NpcPose.class, incorrectUsageMessage); | ||||||
|  |  | ||||||
|  | @ -19,6 +19,9 @@ import net.kyori.adventure.text.Component; | ||||||
| import net.kyori.adventure.text.format.NamedTextColor; | import net.kyori.adventure.text.format.NamedTextColor; | ||||||
| import org.bukkit.Color; | import org.bukkit.Color; | ||||||
| import com.github.retrooper.packetevents.protocol.item.ItemStack; | import com.github.retrooper.packetevents.protocol.item.ItemStack; | ||||||
|  | import org.bukkit.Material; | ||||||
|  | import org.bukkit.block.Block; | ||||||
|  | import org.bukkit.entity.Player; | ||||||
| 
 | 
 | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
|  | @ -117,9 +120,20 @@ public class PropertySetCommand implements CommandHandler { | ||||||
|             value = context.parse(type); |             value = context.parse(type); | ||||||
|             valueName = value == null ? "NONE" : ((NpcEntryImpl) value).getId(); |             valueName = value == null ? "NONE" : ((NpcEntryImpl) value).getId(); | ||||||
|         } |         } | ||||||
|  |         else if (type == Vector3i.class) { | ||||||
|  |             value = context.parse(type); | ||||||
|  |             valueName = value == null ? "NONE" : ((Vector3i) value).toPrettyString(); | ||||||
|  |         } | ||||||
|         else { |         else { | ||||||
|  |             try { | ||||||
|                 value = context.parse(type); |                 value = context.parse(type); | ||||||
|                 valueName = String.valueOf(value); |                 valueName = String.valueOf(value); | ||||||
|  |             } catch (NullPointerException e) { | ||||||
|  |                 context.send(Component.text("An error occurred while trying to parse the value. Please report this to the plugin author.", | ||||||
|  |                         NamedTextColor.RED)); | ||||||
|  |                 e.printStackTrace(); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         npc.UNSAFE_setProperty(property, value); |         npc.UNSAFE_setProperty(property, value); | ||||||
|  | @ -144,6 +158,17 @@ public class PropertySetCommand implements CommandHandler { | ||||||
|                         context.suggestEnum(Arrays.stream(SpellType.values()).filter(spellType -> spellType.ordinal() <= 3).toArray(SpellType[]::new)) : |                         context.suggestEnum(Arrays.stream(SpellType.values()).filter(spellType -> spellType.ordinal() <= 3).toArray(SpellType[]::new)) : | ||||||
|                         context.suggestEnum(SpellType.values()); |                         context.suggestEnum(SpellType.values()); | ||||||
| 
 | 
 | ||||||
|  |                 if (type == Vector3i.class) { | ||||||
|  |                     if (context.getSender() instanceof Player) { | ||||||
|  |                         Player player = (Player) context.getSender(); | ||||||
|  |                         Block targetBlock = player.getTargetBlock(Collections.singleton(Material.AIR), 5); | ||||||
|  |                         if (targetBlock.getType().equals(Material.AIR)) return Collections.emptyList(); | ||||||
|  |                         return context.suggestLiteral( | ||||||
|  |                                 targetBlock.getX() + "", | ||||||
|  |                                 targetBlock.getX() + " " + targetBlock.getY(), | ||||||
|  |                                 targetBlock.getX() + " " + targetBlock.getY() + " " + targetBlock.getZ()); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|                 // Suggest enum values directly |                 // Suggest enum values directly | ||||||
|                 if (type.isEnum()) { |                 if (type.isEnum()) { | ||||||
|                     return context.suggestEnum((Enum<?>[]) type.getEnumConstants()); |                     return context.suggestEnum((Enum<?>[]) type.getEnumConstants()); | ||||||
|  | @ -154,6 +179,25 @@ public class PropertySetCommand implements CommandHandler { | ||||||
|                     // TODO: suggest block with nbt like minecraft setblock command |                     // TODO: suggest block with nbt like minecraft setblock command | ||||||
|                     return context.suggestionParse(2, String.class).equals("block") ? context.suggestStream(StateTypes.values().stream().map(StateType::getName)) : Collections.emptyList(); |                     return context.suggestionParse(2, String.class).equals("block") ? context.suggestStream(StateTypes.values().stream().map(StateType::getName)) : Collections.emptyList(); | ||||||
|                 } |                 } | ||||||
|  |                 if (type == Vector3i.class) { | ||||||
|  |                     if (context.getSender() instanceof Player) { | ||||||
|  |                         Player player = (Player) context.getSender(); | ||||||
|  |                         Block targetBlock = player.getTargetBlock(Collections.singleton(Material.AIR), 5); | ||||||
|  |                         if (targetBlock.getType().equals(Material.AIR)) return Collections.emptyList(); | ||||||
|  |                         return context.suggestLiteral( | ||||||
|  |                                 targetBlock.getY() + "", | ||||||
|  |                                 targetBlock.getY() + " " + targetBlock.getZ()); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } else if (context.argSize() == 5) { | ||||||
|  |                 if (type == Vector3i.class) { | ||||||
|  |                     if (context.getSender() instanceof Player) { | ||||||
|  |                         Player player = (Player) context.getSender(); | ||||||
|  |                         Block targetBlock = player.getTargetBlock(Collections.singleton(Material.AIR), 5); | ||||||
|  |                         if (targetBlock.getType().equals(Material.AIR)) return Collections.emptyList(); | ||||||
|  |                         return context.suggestLiteral(targetBlock.getZ() + ""); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         return Collections.emptyList(); |         return Collections.emptyList(); | ||||||
|  |  | ||||||
|  | @ -28,7 +28,7 @@ public interface MainConfig { | ||||||
|     boolean debugEnabled(); |     boolean debugEnabled(); | ||||||
| 
 | 
 | ||||||
|     @ConfKey("storage-type") |     @ConfKey("storage-type") | ||||||
|     @ConfComments("The storage type to use. Available storage types: YAML") |     @ConfComments("The storage type to use. Available storage types: YAML, SQLITE") | ||||||
|     @DefaultString("YAML") |     @DefaultString("YAML") | ||||||
|     NpcStorageType storageType(); |     NpcStorageType storageType(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -56,6 +56,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { | ||||||
|         registerSerializer(new Vector3fPropertySerializer()); |         registerSerializer(new Vector3fPropertySerializer()); | ||||||
|         registerSerializer(new BlockStatePropertySerializer()); |         registerSerializer(new BlockStatePropertySerializer()); | ||||||
|         registerSerializer(new LookTypeSerializer()); |         registerSerializer(new LookTypeSerializer()); | ||||||
|  |         registerSerializer(new GenericSerializer<>(Vector3i::toString, Vector3i::fromString, Vector3i.class)); | ||||||
| 
 | 
 | ||||||
|         registerEnumSerializer(NpcPose.class); |         registerEnumSerializer(NpcPose.class); | ||||||
|         registerEnumSerializer(DyeColor.class); |         registerEnumSerializer(DyeColor.class); | ||||||
|  | @ -119,6 +120,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { | ||||||
|         register(new EquipmentProperty(packetFactory, "offhand", EquipmentSlot.OFF_HAND)); |         register(new EquipmentProperty(packetFactory, "offhand", EquipmentSlot.OFF_HAND)); | ||||||
| 
 | 
 | ||||||
|         register(new NameProperty(legacyNames, optionalComponents)); |         register(new NameProperty(legacyNames, optionalComponents)); | ||||||
|  |         register(new DummyProperty<>("display_name", String.class)); | ||||||
|         register(new DinnerboneProperty(legacyNames, optionalComponents)); |         register(new DinnerboneProperty(legacyNames, optionalComponents)); | ||||||
| 
 | 
 | ||||||
|         register(new DummyProperty<>("look", LookType.FIXED)); |         register(new DummyProperty<>("look", LookType.FIXED)); | ||||||
|  | @ -251,6 +253,17 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { | ||||||
|         register(new BitsetProperty("is_rearing", horseIndex, horseEating << 1, false, legacyBooleans)); |         register(new BitsetProperty("is_rearing", horseIndex, horseEating << 1, false, legacyBooleans)); | ||||||
|         register(new BitsetProperty("has_mouth_open", horseIndex, horseEating << 2, false, legacyBooleans)); |         register(new BitsetProperty("has_mouth_open", horseIndex, horseEating << 2, false, legacyBooleans)); | ||||||
| 
 | 
 | ||||||
|  |         // End Crystal | ||||||
|  |         if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) { | ||||||
|  |             int endCrystalIndex; | ||||||
|  |             if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) endCrystalIndex = 8; | ||||||
|  |             else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) endCrystalIndex = 7; | ||||||
|  |             else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) endCrystalIndex = 6; | ||||||
|  |             else endCrystalIndex = 5; | ||||||
|  |             register(new OptionalBlockPosProperty("beam_target", null, endCrystalIndex++)); | ||||||
|  |             register(new BooleanProperty("show_base", endCrystalIndex, true, false)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         // Horse |         // Horse | ||||||
|         if (ver.isNewerThanOrEquals(ServerVersion.V_1_8) && ver.isOlderThan(ServerVersion.V_1_9)) { |         if (ver.isNewerThanOrEquals(ServerVersion.V_1_8) && ver.isOlderThan(ServerVersion.V_1_9)) { | ||||||
|             register(new EncodedByteProperty<>("horse_type", HorseType.HORSE, 19, obj -> (byte) obj.ordinal())); |             register(new EncodedByteProperty<>("horse_type", HorseType.HORSE, 19, obj -> (byte) obj.ordinal())); | ||||||
|  | @ -367,7 +380,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { | ||||||
|         else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) witherIndex = 11; |         else if (ver.isNewerThanOrEquals(ServerVersion.V_1_9)) witherIndex = 11; | ||||||
|         else witherIndex = 17; |         else witherIndex = 17; | ||||||
|         witherIndex += 3; // skip the first 3 indexes, will be used for the other properties later |         witherIndex += 3; // skip the first 3 indexes, will be used for the other properties later | ||||||
|         register(new IntegerProperty("invulnerable_time", witherIndex++, 0, false)); |         register(new IntegerProperty("invulnerable_time", witherIndex, 0, false)); | ||||||
| 
 | 
 | ||||||
|         if (!ver.isNewerThanOrEquals(ServerVersion.V_1_9)) return; |         if (!ver.isNewerThanOrEquals(ServerVersion.V_1_9)) return; | ||||||
|         // Shulker |         // Shulker | ||||||
|  |  | ||||||
|  | @ -0,0 +1,28 @@ | ||||||
|  | package lol.pyr.znpcsplus.entity.properties; | ||||||
|  | 
 | ||||||
|  | import com.github.retrooper.packetevents.protocol.entity.data.EntityData; | ||||||
|  | import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes; | ||||||
|  | import lol.pyr.znpcsplus.entity.EntityPropertyImpl; | ||||||
|  | import lol.pyr.znpcsplus.entity.PacketEntity; | ||||||
|  | import lol.pyr.znpcsplus.util.Vector3i; | ||||||
|  | import org.bukkit.entity.Player; | ||||||
|  | 
 | ||||||
|  | import java.util.Map; | ||||||
|  | import java.util.Optional; | ||||||
|  | 
 | ||||||
|  | public class OptionalBlockPosProperty extends EntityPropertyImpl<Vector3i> { | ||||||
|  |     private final int index; | ||||||
|  | 
 | ||||||
|  |     public OptionalBlockPosProperty(String name, Vector3i defaultValue, int index) { | ||||||
|  |         super(name, defaultValue, Vector3i.class); | ||||||
|  |         this.index = index; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) { | ||||||
|  |         Vector3i value = entity.getProperty(this); | ||||||
|  |         if (value == null) properties.put(index, new EntityData(index, EntityDataTypes.OPTIONAL_BLOCK_POSITION, Optional.empty())); | ||||||
|  |         else properties.put(index, new EntityData(index, EntityDataTypes.OPTIONAL_BLOCK_POSITION, | ||||||
|  |                 Optional.of(new com.github.retrooper.packetevents.util.Vector3i(value.getX(), value.getY(), value.getZ())))); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,32 @@ | ||||||
|  | package lol.pyr.znpcsplus.entity.serializers; | ||||||
|  | 
 | ||||||
|  | import lol.pyr.znpcsplus.entity.PropertySerializer; | ||||||
|  | 
 | ||||||
|  | import java.util.function.Function; | ||||||
|  | 
 | ||||||
|  | public class GenericSerializer<T> implements PropertySerializer<T> { | ||||||
|  |     private final Function<T, String> encoder; | ||||||
|  |     private final Function<String, T> decoder; | ||||||
|  |     private final Class<T> typeClass; | ||||||
|  | 
 | ||||||
|  |     public GenericSerializer(Function<T, String> encoder, Function<String, T> decoder, Class<T> typeClass) { | ||||||
|  |         this.encoder = encoder; | ||||||
|  |         this.decoder = decoder; | ||||||
|  |         this.typeClass = typeClass; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public String serialize(T property) { | ||||||
|  |         return encoder.apply(property); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public T deserialize(String property) { | ||||||
|  |         return decoder.apply(property); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public Class<T> getTypeClass() { | ||||||
|  |         return typeClass; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -10,15 +10,17 @@ import lol.pyr.znpcsplus.interaction.ActionRegistry; | ||||||
| import lol.pyr.znpcsplus.packets.PacketFactory; | import lol.pyr.znpcsplus.packets.PacketFactory; | ||||||
| import lol.pyr.znpcsplus.scheduling.TaskScheduler; | import lol.pyr.znpcsplus.scheduling.TaskScheduler; | ||||||
| import lol.pyr.znpcsplus.storage.NpcStorage; | import lol.pyr.znpcsplus.storage.NpcStorage; | ||||||
|  | import lol.pyr.znpcsplus.storage.NpcStorageType; | ||||||
| import lol.pyr.znpcsplus.util.NpcLocation; | import lol.pyr.znpcsplus.util.NpcLocation; | ||||||
| import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; | import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; | ||||||
|  | import org.bukkit.Bukkit; | ||||||
| import org.bukkit.World; | import org.bukkit.World; | ||||||
| 
 | 
 | ||||||
| import java.util.*; | import java.util.*; | ||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
| 
 | 
 | ||||||
| public class NpcRegistryImpl implements NpcRegistry { | public class NpcRegistryImpl implements NpcRegistry { | ||||||
|     private final NpcStorage storage; |     private NpcStorage storage; | ||||||
|     private final PacketFactory packetFactory; |     private final PacketFactory packetFactory; | ||||||
|     private final ConfigManager configManager; |     private final ConfigManager configManager; | ||||||
|     private final LegacyComponentSerializer textSerializer; |     private final LegacyComponentSerializer textSerializer; | ||||||
|  | @ -32,6 +34,10 @@ public class NpcRegistryImpl implements NpcRegistry { | ||||||
|         this.textSerializer = textSerializer; |         this.textSerializer = textSerializer; | ||||||
|         this.propertyRegistry = propertyRegistry; |         this.propertyRegistry = propertyRegistry; | ||||||
|         storage = configManager.getConfig().storageType().create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer); |         storage = configManager.getConfig().storageType().create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer); | ||||||
|  |         if (storage == null) { | ||||||
|  |             Bukkit.getLogger().warning("Failed to initialize storage, falling back to YAML"); | ||||||
|  |             storage = NpcStorageType.YAML.create(configManager, plugin, packetFactory, actionRegistry, typeRegistry, propertyRegistry, textSerializer); | ||||||
|  |         } | ||||||
|         this.packetFactory = packetFactory; |         this.packetFactory = packetFactory; | ||||||
|         this.configManager = configManager; |         this.configManager = configManager; | ||||||
| 
 | 
 | ||||||
|  | @ -57,7 +63,9 @@ public class NpcRegistryImpl implements NpcRegistry { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void unregisterAll() { |     private void unregisterAll() { | ||||||
|         for (NpcEntryImpl entry : npcList) entry.getNpc().delete(); |         for (NpcEntryImpl entry : getAll()) { | ||||||
|  |             if (entry.isSave()) entry.getNpc().delete(); | ||||||
|  |         } | ||||||
|         npcList.clear(); |         npcList.clear(); | ||||||
|         npcIdLookupMap.clear(); |         npcIdLookupMap.clear(); | ||||||
|         npcUuidLookupMap.clear(); |         npcUuidLookupMap.clear(); | ||||||
|  |  | ||||||
|  | @ -115,7 +115,7 @@ public class NpcTypeImpl implements NpcType { | ||||||
|         public NpcTypeImpl build() { |         public NpcTypeImpl build() { | ||||||
|             ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion(); |             ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion(); | ||||||
|             addProperties("fire", "invisible", "silent", "look", "look_distance", "view_distance", |             addProperties("fire", "invisible", "silent", "look", "look_distance", "view_distance", | ||||||
|                     "potion_color", "potion_ambient"); |                     "potion_color", "potion_ambient", "display_name"); | ||||||
|             if (!type.equals(EntityTypes.PLAYER)) addProperties("dinnerbone"); |             if (!type.equals(EntityTypes.PLAYER)) addProperties("dinnerbone"); | ||||||
|             // TODO: make this look nicer after completing the rest of the properties |             // TODO: make this look nicer after completing the rest of the properties | ||||||
|             if (version.isNewerThanOrEquals(ServerVersion.V_1_9)) addProperties("glow"); |             if (version.isNewerThanOrEquals(ServerVersion.V_1_9)) addProperties("glow"); | ||||||
|  |  | ||||||
|  | @ -73,6 +73,10 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry { | ||||||
|                 .setHologramOffset(-0.275) |                 .setHologramOffset(-0.275) | ||||||
|                 .addProperties("creeper_state", "creeper_charged")); |                 .addProperties("creeper_state", "creeper_charged")); | ||||||
| 
 | 
 | ||||||
|  |         register(builder(p, "end_crystal", EntityTypes.END_CRYSTAL) | ||||||
|  |                 .setHologramOffset(0.025) | ||||||
|  |                 .addProperties("beam_target", "show_base")); | ||||||
|  | 
 | ||||||
|         register(builder(p, "ender_dragon", EntityTypes.ENDER_DRAGON) |         register(builder(p, "ender_dragon", EntityTypes.ENDER_DRAGON) | ||||||
|                 .setHologramOffset(6.0245)); |                 .setHologramOffset(6.0245)); | ||||||
| 
 | 
 | ||||||
|  | @ -206,8 +210,7 @@ public class NpcTypeRegistryImpl implements NpcTypeRegistry { | ||||||
|                 .addEquipmentProperties()); |                 .addEquipmentProperties()); | ||||||
| 
 | 
 | ||||||
|         register(builder(p, "evoker", EntityTypes.EVOKER) |         register(builder(p, "evoker", EntityTypes.EVOKER) | ||||||
|                 .setHologramOffset(-0.025) |                 .setHologramOffset(-0.025)); | ||||||
|                 .addProperties("evoker_spell")); |  | ||||||
| 
 | 
 | ||||||
|         register(builder(p, "llama", EntityTypes.LLAMA) |         register(builder(p, "llama", EntityTypes.LLAMA) | ||||||
|                 .setHologramOffset(-0.105) |                 .setHologramOffset(-0.105) | ||||||
|  |  | ||||||
|  | @ -14,6 +14,6 @@ public class StringParser extends ParserType<String> { | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public String parse(Deque<String> deque) throws CommandExecutionException { |     public String parse(Deque<String> deque) throws CommandExecutionException { | ||||||
|         return deque.pollFirst(); |         return String.join(" ", deque); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,30 @@ | ||||||
|  | package lol.pyr.znpcsplus.parsers; | ||||||
|  | 
 | ||||||
|  | import lol.pyr.director.adventure.command.CommandContext; | ||||||
|  | import lol.pyr.director.adventure.parse.ParserType; | ||||||
|  | import lol.pyr.director.common.command.CommandExecutionException; | ||||||
|  | import lol.pyr.director.common.message.Message; | ||||||
|  | import lol.pyr.znpcsplus.util.Vector3i; | ||||||
|  | 
 | ||||||
|  | import java.util.Deque; | ||||||
|  | 
 | ||||||
|  | public class Vector3iParser extends ParserType<Vector3i> { | ||||||
|  |     public Vector3iParser(Message<CommandContext> message) { | ||||||
|  |         super(message); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public Vector3i parse(Deque<String> deque) throws CommandExecutionException { | ||||||
|  |         if (deque.size() == 0) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |         try { | ||||||
|  |             return new Vector3i( | ||||||
|  |                     Integer.parseInt(deque.pop()), | ||||||
|  |                     Integer.parseInt(deque.pop()), | ||||||
|  |                     Integer.parseInt(deque.pop())); | ||||||
|  |         } catch (NumberFormatException e) { | ||||||
|  |             throw new CommandExecutionException(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -6,6 +6,7 @@ import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl; | ||||||
| import lol.pyr.znpcsplus.interaction.ActionRegistry; | import lol.pyr.znpcsplus.interaction.ActionRegistry; | ||||||
| import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl; | import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl; | ||||||
| import lol.pyr.znpcsplus.packets.PacketFactory; | import lol.pyr.znpcsplus.packets.PacketFactory; | ||||||
|  | import lol.pyr.znpcsplus.storage.sqlite.SQLiteStorage; | ||||||
| import lol.pyr.znpcsplus.storage.yaml.YamlStorage; | import lol.pyr.znpcsplus.storage.yaml.YamlStorage; | ||||||
| import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; | import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; | ||||||
| 
 | 
 | ||||||
|  | @ -17,6 +18,17 @@ public enum NpcStorageType { | ||||||
|         public NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistry actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) { |         public NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistry actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) { | ||||||
|             return new YamlStorage(packetFactory, configManager, actionRegistry, typeRegistry, propertyRegistry, textSerializer, new File(plugin.getDataFolder(), "data")); |             return new YamlStorage(packetFactory, configManager, actionRegistry, typeRegistry, propertyRegistry, textSerializer, new File(plugin.getDataFolder(), "data")); | ||||||
|         } |         } | ||||||
|  |     }, | ||||||
|  |     SQLITE { | ||||||
|  |         @Override | ||||||
|  |         public NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistry actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer) { | ||||||
|  |             try { | ||||||
|  |                 return new SQLiteStorage(packetFactory, configManager, actionRegistry, typeRegistry, propertyRegistry, textSerializer, new File(plugin.getDataFolder(), "znpcsplus.sqlite")); | ||||||
|  |             } catch (Exception e) { | ||||||
|  |                 e.printStackTrace(); | ||||||
|  |                 return null; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     public abstract NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistry actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer); |     public abstract NpcStorage create(ConfigManager configManager, ZNpcsPlus plugin, PacketFactory packetFactory, ActionRegistry actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer); | ||||||
|  |  | ||||||
|  | @ -0,0 +1,16 @@ | ||||||
|  | package lol.pyr.znpcsplus.storage.database; | ||||||
|  | 
 | ||||||
|  | import java.sql.Connection; | ||||||
|  | import java.util.logging.Logger; | ||||||
|  | 
 | ||||||
|  | public abstract class Database { | ||||||
|  |     protected final Logger logger; | ||||||
|  |     protected Connection connection; | ||||||
|  |     public Database(Logger logger){ | ||||||
|  |         this.logger = logger; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public abstract Connection getSQLConnection(); | ||||||
|  | 
 | ||||||
|  |     public abstract void load(); | ||||||
|  | } | ||||||
|  | @ -0,0 +1,101 @@ | ||||||
|  | package lol.pyr.znpcsplus.storage.sqlite; | ||||||
|  | 
 | ||||||
|  | import lol.pyr.znpcsplus.storage.database.Database; | ||||||
|  | 
 | ||||||
|  | import java.io.File; | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.sql.*; | ||||||
|  | import java.util.logging.Level; | ||||||
|  | import java.util.logging.Logger; | ||||||
|  | 
 | ||||||
|  | public class SQLite extends Database{ | ||||||
|  |     private final File dbFile; | ||||||
|  |     public SQLite(File file, Logger logger){ | ||||||
|  |         super(logger); | ||||||
|  |         dbFile = file; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public Connection getSQLConnection() { | ||||||
|  |         if (!dbFile.exists()){ | ||||||
|  |             try { | ||||||
|  |                 dbFile.createNewFile(); | ||||||
|  |             } catch (IOException e) { | ||||||
|  |                 logger.log(Level.SEVERE, "File write error: "+dbFile.getName()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         try { | ||||||
|  |             if(connection!=null&&!connection.isClosed()){ | ||||||
|  |                 return connection; | ||||||
|  |             } | ||||||
|  |             Class.forName("org.sqlite.JDBC"); | ||||||
|  |             connection = DriverManager.getConnection("jdbc:sqlite:" + dbFile.getAbsolutePath()); | ||||||
|  |             return connection; | ||||||
|  |         } catch (SQLException ex) { | ||||||
|  |             logger.log(Level.SEVERE,"SQLite exception on initialize", ex); | ||||||
|  |         } catch (ClassNotFoundException ex) { | ||||||
|  |             logger.log(Level.SEVERE, "SQLite JDBC library not found", ex); | ||||||
|  |         } | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public void load() { | ||||||
|  |         connection = getSQLConnection(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public boolean tableExists(String tableName) { | ||||||
|  |         try { | ||||||
|  |             Statement s = connection.createStatement(); | ||||||
|  |             s.executeQuery("SELECT * FROM " + tableName + ";"); | ||||||
|  |             s.close(); | ||||||
|  |             return true; | ||||||
|  |         } catch (SQLException e) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public boolean columnExists(String tableName, String columnName) { | ||||||
|  |         try { | ||||||
|  |             Statement s = connection.createStatement(); | ||||||
|  |             s.executeQuery("SELECT " + columnName + " FROM " + tableName + ";"); | ||||||
|  |             s.close(); | ||||||
|  |             return true; | ||||||
|  |         } catch (SQLException e) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public boolean addColumn(String tableName, String columnName, String type) { | ||||||
|  |         if (columnExists(tableName, columnName)) return false; | ||||||
|  |         try { | ||||||
|  |             Statement s = connection.createStatement(); | ||||||
|  |             s.executeQuery("ALTER TABLE " + tableName + " ADD COLUMN " + columnName + " " + type + ";"); | ||||||
|  |             s.close(); | ||||||
|  |         } catch (SQLException e) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public ResultSet executeQuery(String query) { | ||||||
|  |         try { | ||||||
|  |             Statement s = connection.createStatement(); | ||||||
|  |             ResultSet rs = s.executeQuery(query); | ||||||
|  |             s.close(); | ||||||
|  |             return rs; | ||||||
|  |         } catch (SQLException e) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public int executeUpdate(String query) { | ||||||
|  |         try { | ||||||
|  |             Statement s = connection.createStatement(); | ||||||
|  |             int rowCount = s.executeUpdate(query); | ||||||
|  |             s.close(); | ||||||
|  |             return rowCount; | ||||||
|  |         } catch (SQLException e) { | ||||||
|  |             e.printStackTrace(); | ||||||
|  |             return -1; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,315 @@ | ||||||
|  | package lol.pyr.znpcsplus.storage.sqlite; | ||||||
|  | 
 | ||||||
|  | import lol.pyr.znpcsplus.api.entity.EntityProperty; | ||||||
|  | import lol.pyr.znpcsplus.config.ConfigManager; | ||||||
|  | import lol.pyr.znpcsplus.entity.EntityPropertyImpl; | ||||||
|  | import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl; | ||||||
|  | import lol.pyr.znpcsplus.entity.PropertySerializer; | ||||||
|  | import lol.pyr.znpcsplus.hologram.HologramImpl; | ||||||
|  | import lol.pyr.znpcsplus.interaction.ActionRegistry; | ||||||
|  | import lol.pyr.znpcsplus.npc.NpcEntryImpl; | ||||||
|  | import lol.pyr.znpcsplus.npc.NpcImpl; | ||||||
|  | import lol.pyr.znpcsplus.npc.NpcTypeRegistryImpl; | ||||||
|  | import lol.pyr.znpcsplus.packets.PacketFactory; | ||||||
|  | import lol.pyr.znpcsplus.storage.NpcStorage; | ||||||
|  | import lol.pyr.znpcsplus.util.NpcLocation; | ||||||
|  | import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; | ||||||
|  | 
 | ||||||
|  | import java.io.File; | ||||||
|  | import java.math.BigDecimal; | ||||||
|  | import java.sql.PreparedStatement; | ||||||
|  | import java.sql.ResultSet; | ||||||
|  | import java.sql.SQLException; | ||||||
|  | import java.util.*; | ||||||
|  | import java.util.logging.Logger; | ||||||
|  | import java.util.stream.Collectors; | ||||||
|  | 
 | ||||||
|  | public class SQLiteStorage implements NpcStorage { | ||||||
|  |     private final static Logger logger = Logger.getLogger("SQLiteStorage"); | ||||||
|  | 
 | ||||||
|  |     private final PacketFactory packetFactory; | ||||||
|  |     private final ConfigManager configManager; | ||||||
|  |     private final ActionRegistry actionRegistry; | ||||||
|  |     private final NpcTypeRegistryImpl typeRegistry; | ||||||
|  |     private final EntityPropertyRegistryImpl propertyRegistry; | ||||||
|  |     private final LegacyComponentSerializer textSerializer; | ||||||
|  |     private final SQLite database; | ||||||
|  | 
 | ||||||
|  |     private final String TABLE_NPCS; | ||||||
|  |     private final String TABLE_NPCS_PROPERTIES; | ||||||
|  |     private final String TABLE_NPCS_HOLOGRAMS; | ||||||
|  |     private final String TABLE_NPCS_ACTIONS; | ||||||
|  | 
 | ||||||
|  |     public SQLiteStorage(PacketFactory packetFactory, ConfigManager configManager, ActionRegistry actionRegistry, NpcTypeRegistryImpl typeRegistry, EntityPropertyRegistryImpl propertyRegistry, LegacyComponentSerializer textSerializer, File file) { | ||||||
|  |         this.packetFactory = packetFactory; | ||||||
|  |         this.configManager = configManager; | ||||||
|  |         this.actionRegistry = actionRegistry; | ||||||
|  |         this.typeRegistry = typeRegistry; | ||||||
|  |         this.propertyRegistry = propertyRegistry; | ||||||
|  |         this.textSerializer = textSerializer; | ||||||
|  |         this.database = new SQLite(file, logger); | ||||||
|  |         database.load(); | ||||||
|  |         if (database.getSQLConnection() == null) { | ||||||
|  |             throw new RuntimeException("Failed to initialize SQLite Storage."); | ||||||
|  |         } | ||||||
|  |         TABLE_NPCS = "npcs"; | ||||||
|  |         TABLE_NPCS_PROPERTIES = "npcs_properties"; | ||||||
|  |         TABLE_NPCS_HOLOGRAMS =  "npcs_holograms"; | ||||||
|  |         TABLE_NPCS_ACTIONS = "npcs_actions"; | ||||||
|  |         validateTables(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void validateTables() { | ||||||
|  |         if (!database.tableExists(TABLE_NPCS)) { | ||||||
|  |             logger.info("Creating table " + TABLE_NPCS + "..."); | ||||||
|  |             createNpcsTable(); | ||||||
|  |         } | ||||||
|  |         if (!database.tableExists(TABLE_NPCS_PROPERTIES)) { | ||||||
|  |             logger.info("Creating table " + TABLE_NPCS_PROPERTIES + "..."); | ||||||
|  |             createNpcsPropertiesTable(); | ||||||
|  |         } | ||||||
|  |         if (!database.tableExists(TABLE_NPCS_HOLOGRAMS)) { | ||||||
|  |             logger.info("Creating table " + TABLE_NPCS_HOLOGRAMS + "..."); | ||||||
|  |             createNpcsHologramsTable(); | ||||||
|  |         } | ||||||
|  |         if (!database.tableExists(TABLE_NPCS_ACTIONS)) { | ||||||
|  |             logger.info("Creating table " + TABLE_NPCS_ACTIONS + "..."); | ||||||
|  |             createNpcsActionsTable(); | ||||||
|  |         } | ||||||
|  |         updateTables(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void createNpcsTable() { | ||||||
|  |         if (database.executeUpdate("CREATE TABLE " + TABLE_NPCS + " " + | ||||||
|  |                 "(id TEXT PRIMARY KEY, isProcessed BOOLEAN, allowCommands BOOLEAN, enabled BOOLEAN, " + | ||||||
|  |                 "uuid TEXT, world TEXT, x REAL, y REAL, z REAL, yaw REAL, pitch REAL, type TEXT, hologramOffset REAL, hologramRefreshDelay INTEGER)") != -1) { | ||||||
|  |             logger.info("Table " + TABLE_NPCS + " created."); | ||||||
|  |         } else { | ||||||
|  |             logger.severe("Failed to create table " + TABLE_NPCS + "."); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void createNpcsPropertiesTable() { | ||||||
|  |         if (database.executeUpdate("CREATE TABLE " + TABLE_NPCS_PROPERTIES + " " + | ||||||
|  |                 "(npc_id TEXT, property TEXT, value TEXT, PRIMARY KEY (npc_id, property))") != -1) { | ||||||
|  |             logger.info("Table " + TABLE_NPCS_PROPERTIES + " created."); | ||||||
|  |         } else { | ||||||
|  |             logger.severe("Failed to create table " + TABLE_NPCS_PROPERTIES + "."); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void createNpcsHologramsTable() { | ||||||
|  |         if (database.executeUpdate("CREATE TABLE " + TABLE_NPCS_HOLOGRAMS + " " + | ||||||
|  |                 "(npc_id TEXT, line INTEGER, text TEXT, PRIMARY KEY (npc_id, line))") != -1) { | ||||||
|  |             logger.info("Table " + TABLE_NPCS_HOLOGRAMS + " created."); | ||||||
|  |         } else { | ||||||
|  |             logger.severe("Failed to create table " + TABLE_NPCS_HOLOGRAMS + "."); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void createNpcsActionsTable() { | ||||||
|  |         if (database.executeUpdate("CREATE TABLE " + TABLE_NPCS_ACTIONS + " " + | ||||||
|  |                 "(npc_id TEXT, action_id INTEGER, action_data TEXT, PRIMARY KEY (npc_id, action_id))") != -1) { | ||||||
|  |             logger.info("Table " + TABLE_NPCS_ACTIONS + " created."); | ||||||
|  |         } else { | ||||||
|  |             logger.severe("Failed to create table " + TABLE_NPCS_ACTIONS + "."); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void updateTables() { | ||||||
|  |         // Any table updates go here | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public Collection<NpcEntryImpl> loadNpcs() { | ||||||
|  |         Map<String, NpcEntryImpl> npcMap = new HashMap<>(); | ||||||
|  |         try { | ||||||
|  |             PreparedStatement st = database.getSQLConnection().prepareStatement("SELECT * FROM " + TABLE_NPCS); | ||||||
|  |             ResultSet rs = st.executeQuery(); | ||||||
|  |             while (rs.next()) { | ||||||
|  |                 NpcImpl npc = new NpcImpl(UUID.fromString(rs.getString("uuid")), propertyRegistry, configManager, packetFactory, textSerializer, | ||||||
|  |                         rs.getString("world"), typeRegistry.getByName(rs.getString("type")), | ||||||
|  |                         new NpcLocation(rs.getDouble("x"), rs.getDouble("y"), rs.getDouble("z"), rs.getFloat("yaw"), rs.getFloat("pitch"))); | ||||||
|  | 
 | ||||||
|  |                 if (!rs.getBoolean("enabled")) npc.setEnabled(false); | ||||||
|  | 
 | ||||||
|  |                 npc.getHologram().setOffset(rs.getDouble("hologramOffset")); | ||||||
|  |                 if (rs.getBigDecimal("hologramRefreshDelay") != null) npc.getHologram().setRefreshDelay(rs.getBigDecimal("hologramRefreshDelay").longValue()); | ||||||
|  | 
 | ||||||
|  |                 NpcEntryImpl entry = new NpcEntryImpl(rs.getString("id"), npc); | ||||||
|  |                 entry.setProcessed(rs.getBoolean("isProcessed")); | ||||||
|  |                 entry.setAllowCommandModification(rs.getBoolean("allowCommands")); | ||||||
|  |                 entry.setSave(true); | ||||||
|  |                 npcMap.put(rs.getString("id"), entry); | ||||||
|  |             } | ||||||
|  |         } catch (SQLException e) { | ||||||
|  |             e.printStackTrace(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             PreparedStatement st = database.getSQLConnection().prepareStatement("SELECT * FROM " + TABLE_NPCS_PROPERTIES); | ||||||
|  |             ResultSet rs = st.executeQuery(); | ||||||
|  |             while (rs.next()) { | ||||||
|  |                 NpcEntryImpl entry = npcMap.get(rs.getString("npc_id")); | ||||||
|  |                 String key = rs.getString("property"); | ||||||
|  |                 if (entry != null) { | ||||||
|  |                     EntityPropertyImpl<?> property = propertyRegistry.getByName(key); | ||||||
|  |                     if (property == null) { | ||||||
|  |                         logger.warning("Unknown property '" + key + "' for npc '" + rs.getString("npc_id") + "'. skipping ..."); | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  |                     PropertySerializer<?> serializer = propertyRegistry.getSerializer(property.getType()); | ||||||
|  |                     if (serializer == null) { | ||||||
|  |                         logger.warning("Unknown serializer for property '" + key + "' for npc '" + rs.getString("npc_id") + "'. skipping ..."); | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  |                     Object value = serializer.deserialize(rs.getString("value")); | ||||||
|  |                     if (value == null) { | ||||||
|  |                         logger.warning("Failed to deserialize property '" + key + "' for npc '" + rs.getString("npc_id") + "'. Resetting to default ..."); | ||||||
|  |                         value = property.getDefaultValue(); | ||||||
|  |                     } | ||||||
|  |                     entry.getNpc().UNSAFE_setProperty(property, value); | ||||||
|  |                     npcMap.put(rs.getString("npc_id"), entry); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } catch (SQLException e) { | ||||||
|  |             e.printStackTrace(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             PreparedStatement st = database.getSQLConnection().prepareStatement("SELECT * FROM " + TABLE_NPCS_HOLOGRAMS + " ORDER BY line ASC"); | ||||||
|  |             ResultSet rs = st.executeQuery(); | ||||||
|  | 
 | ||||||
|  |             while (rs.next()) { | ||||||
|  |                 NpcEntryImpl entry = npcMap.get(rs.getString("npc_id")); | ||||||
|  |                 if (entry != null) { | ||||||
|  |                     entry.getNpc().getHologram().insertLine(rs.getInt("line"), rs.getString("text")); | ||||||
|  |                 } | ||||||
|  |                 npcMap.put(rs.getString("npc_id"), entry); | ||||||
|  |             } | ||||||
|  |         } catch (SQLException e) { | ||||||
|  |             throw new RuntimeException(e); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             PreparedStatement st = database.getSQLConnection().prepareStatement("SELECT * FROM " + TABLE_NPCS_ACTIONS + " ORDER BY action_id ASC"); | ||||||
|  |             ResultSet rs = st.executeQuery(); | ||||||
|  | 
 | ||||||
|  |             while (rs.next()) { | ||||||
|  |                 NpcEntryImpl entry = npcMap.get(rs.getString("npc_id")); | ||||||
|  |                 if (entry != null) { | ||||||
|  |                     entry.getNpc().addAction(actionRegistry.deserialize(rs.getString("action_data"))); | ||||||
|  |                 } | ||||||
|  |                 npcMap.put(rs.getString("npc_id"), entry); | ||||||
|  |             } | ||||||
|  |         } catch (SQLException e) { | ||||||
|  |             throw new RuntimeException(e); | ||||||
|  |         } | ||||||
|  |         return npcMap.values().stream().filter(Objects::nonNull).collect(Collectors.toList()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public void saveNpcs(Collection<NpcEntryImpl> npcs) { | ||||||
|  |         long start = System.currentTimeMillis(); | ||||||
|  |         for (NpcEntryImpl entry : npcs) try { | ||||||
|  |             PreparedStatement ps; | ||||||
|  |             ps = database.getSQLConnection().prepareStatement("REPLACE INTO " + TABLE_NPCS + " (id, isProcessed, allowCommands, enabled, uuid, world, x, y, z, yaw, pitch, type, hologramOffset, hologramRefreshDelay) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)"); | ||||||
|  |             ps.setString(1, entry.getId()); | ||||||
|  |             ps.setBoolean(2, entry.isProcessed()); | ||||||
|  |             ps.setBoolean(3, entry.isAllowCommandModification()); | ||||||
|  |             NpcImpl npc = entry.getNpc(); | ||||||
|  |             ps.setBoolean(4, npc.isEnabled()); | ||||||
|  |             ps.setString(5, npc.getUuid().toString()); | ||||||
|  |             ps.setString(6, npc.getWorldName()); | ||||||
|  |             ps.setDouble(7, npc.getLocation().getX()); | ||||||
|  |             ps.setDouble(8, npc.getLocation().getY()); | ||||||
|  |             ps.setDouble(9, npc.getLocation().getZ()); | ||||||
|  |             ps.setFloat(10, npc.getLocation().getYaw()); | ||||||
|  |             ps.setFloat(11, npc.getLocation().getPitch()); | ||||||
|  |             ps.setString(12, npc.getType().getName()); | ||||||
|  |             HologramImpl hologram = npc.getHologram(); | ||||||
|  |             ps.setDouble(13, hologram.getOffset()); | ||||||
|  |             if(hologram.getRefreshDelay() != -1) ps.setBigDecimal(14, new BigDecimal(hologram.getRefreshDelay())); | ||||||
|  |             ps.executeUpdate(); | ||||||
|  | 
 | ||||||
|  |             ps = database.getSQLConnection().prepareStatement("DELETE FROM " + TABLE_NPCS_PROPERTIES + " WHERE npc_id = ?"); | ||||||
|  |             ps.setString(1, entry.getId()); | ||||||
|  |             ps.executeUpdate(); | ||||||
|  | 
 | ||||||
|  |             for (EntityProperty<?> property : npc.getAllProperties()) try { | ||||||
|  |                 PropertySerializer<?> serializer = propertyRegistry.getSerializer(((EntityPropertyImpl<?>) property).getType()); | ||||||
|  |                 if (serializer == null) { | ||||||
|  |                     logger.warning("Unknown serializer for property '" + property.getName() + "' for npc '" + entry.getId() + "'. skipping ..."); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 ps = database.getSQLConnection().prepareStatement("REPLACE INTO " + TABLE_NPCS_PROPERTIES + " (npc_id, property, value) VALUES(?,?,?)"); | ||||||
|  |                 ps.setString(1, entry.getId()); | ||||||
|  |                 ps.setString(2, property.getName()); | ||||||
|  |                 ps.setString(3, serializer.UNSAFE_serialize(npc.getProperty(property))); | ||||||
|  |                 ps.executeUpdate(); | ||||||
|  |             } catch (Exception exception) { | ||||||
|  |                 logger.severe("Failed to serialize property " + property.getName() + " for npc with id " + entry.getId()); | ||||||
|  |                 exception.printStackTrace(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             ps = database.getSQLConnection().prepareStatement("DELETE FROM " + TABLE_NPCS_HOLOGRAMS + " WHERE npc_id = ? AND line > ?"); | ||||||
|  |             ps.setString(1, entry.getId()); | ||||||
|  |             ps.setInt(2, hologram.getLines().size() - 1); | ||||||
|  |             ps.executeUpdate(); | ||||||
|  | 
 | ||||||
|  |             for (int i = 0; i < hologram.getLines().size(); i++) { | ||||||
|  |                 ps = database.getSQLConnection().prepareStatement("REPLACE INTO " + TABLE_NPCS_HOLOGRAMS + " (npc_id, line, text) VALUES(?,?,?)"); | ||||||
|  |                 ps.setString(1, entry.getId()); | ||||||
|  |                 ps.setInt(2, i); | ||||||
|  |                 ps.setString(3, hologram.getLine(i)); | ||||||
|  |                 ps.executeUpdate(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             ps = database.getSQLConnection().prepareStatement("DELETE FROM " + TABLE_NPCS_ACTIONS + " WHERE npc_id = ? AND action_id > ?"); | ||||||
|  |             ps.setString(1, entry.getId()); | ||||||
|  |             ps.setInt(2, npc.getActions().size() - 1); | ||||||
|  |             ps.executeUpdate(); | ||||||
|  | 
 | ||||||
|  |             for (int i = 0; i < npc.getActions().size(); i++) { | ||||||
|  |                 ps = database.getSQLConnection().prepareStatement("REPLACE INTO " + TABLE_NPCS_ACTIONS + " (npc_id, action_id, action_data) VALUES(?,?,?)"); | ||||||
|  |                 ps.setString(1, entry.getId()); | ||||||
|  |                 ps.setInt(2, i); | ||||||
|  |                 String action = actionRegistry.serialize(npc.getActions().get(i)); | ||||||
|  |                 if (action == null) continue; | ||||||
|  |                 ps.setString(3, action); | ||||||
|  |                 ps.executeUpdate(); | ||||||
|  |             } | ||||||
|  |         } catch (SQLException exception) { | ||||||
|  |             logger.severe("Failed to save npc with id " + entry.getId()); | ||||||
|  |             exception.printStackTrace(); | ||||||
|  |         } | ||||||
|  |         if (configManager.getConfig().debugEnabled()) { | ||||||
|  |             logger.info("Saved " + npcs.size() + " npcs in " + (System.currentTimeMillis() - start) + "ms"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public void deleteNpc(NpcEntryImpl entry) { | ||||||
|  |         try { | ||||||
|  |             PreparedStatement ps; | ||||||
|  |             ps = database.getSQLConnection().prepareStatement("DELETE FROM " + TABLE_NPCS + " WHERE id = ?"); | ||||||
|  |             ps.setString(1, entry.getId()); | ||||||
|  |             ps.executeUpdate(); | ||||||
|  | 
 | ||||||
|  |             ps = database.getSQLConnection().prepareStatement("DELETE FROM " + TABLE_NPCS_PROPERTIES + " WHERE npc_id = ?"); | ||||||
|  |             ps.setString(1, entry.getId()); | ||||||
|  |             ps.executeUpdate(); | ||||||
|  | 
 | ||||||
|  |             ps = database.getSQLConnection().prepareStatement("DELETE FROM " + TABLE_NPCS_HOLOGRAMS + " WHERE npc_id = ?"); | ||||||
|  |             ps.setString(1, entry.getId()); | ||||||
|  |             ps.executeUpdate(); | ||||||
|  | 
 | ||||||
|  |             ps = database.getSQLConnection().prepareStatement("DELETE FROM " + TABLE_NPCS_ACTIONS + " WHERE npc_id = ?"); | ||||||
|  |             ps.setString(1, entry.getId()); | ||||||
|  |             ps.executeUpdate(); | ||||||
|  |         } catch (SQLException exception) { | ||||||
|  |             logger.severe("Failed to delete npc with id " + entry.getId()); | ||||||
|  |             exception.printStackTrace(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
		Loading…
	
		Reference in a new issue