From f4961094c5e21e1e8d95dba8d2c12d62773df4ab Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Sun, 27 Oct 2019 12:56:25 -0500 Subject: [PATCH 01/11] add block method for compatible material lookup --- .../compatibility/CompatibleMaterial.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Core/src/main/java/com/songoda/core/compatibility/CompatibleMaterial.java b/Core/src/main/java/com/songoda/core/compatibility/CompatibleMaterial.java index 83c7037b..d5b14c5e 100644 --- a/Core/src/main/java/com/songoda/core/compatibility/CompatibleMaterial.java +++ b/Core/src/main/java/com/songoda/core/compatibility/CompatibleMaterial.java @@ -6,6 +6,7 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import org.bukkit.Material; +import org.bukkit.block.Block; import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; @@ -1220,6 +1221,27 @@ public enum CompatibleMaterial { return m != null ? m : lookupMap.get(key + item.getDurability()); } + /** + * Lookup a Material by Block, corrected for legacy + * + * @param block block to check + * @return LegacyMaterial or null if none found + */ + public static CompatibleMaterial getMaterial(Block block) { + if (block == null) { + return null; + } + Material mat = block.getType(); + if(mat == null) return null; + else if (useLegacy) { + LegacyMaterialBlockType legacyBlock = LegacyMaterialBlockType.getFromLegacy(mat.name()); + if (legacyBlock != null) { + return lookupMap.get(legacyBlock.name()); + } + } + return lookupMap.get(mat.name()); + } + /** * Lookup a Block Material by its modern id name.
* This also can grab materials by their legacy, but only if there is no From 19563b088ede6a29873b04956c3437711ce69317 Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Sun, 27 Oct 2019 13:11:38 -0500 Subject: [PATCH 02/11] revert 312044c8 - issue was in implementation, not in core --- .../com/songoda/core/compatibility/CompatibleMaterial.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Core/src/main/java/com/songoda/core/compatibility/CompatibleMaterial.java b/Core/src/main/java/com/songoda/core/compatibility/CompatibleMaterial.java index e5e9d73b..d5b14c5e 100644 --- a/Core/src/main/java/com/songoda/core/compatibility/CompatibleMaterial.java +++ b/Core/src/main/java/com/songoda/core/compatibility/CompatibleMaterial.java @@ -2138,14 +2138,12 @@ public enum CompatibleMaterial { case BEETROOTS: case CACTUS: case CARROTS: - case CARROT: case CHORUS_FLOWER: // FROSTED_ICE is Ageable, but not a crop case KELP: case MELON_STEM: case NETHER_WART: - case POTATOES: - case POTATO: + case POTATOES: case PUMPKIN_STEM: case SUGAR_CANE: case WHEAT: From d3233586043654e677bef2667601df79a170d7e4 Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Sun, 27 Oct 2019 15:52:52 -0500 Subject: [PATCH 03/11] handle comments with newlines as individual lines, add anonymous config --- .../java/com/songoda/core/configuration/Comment.java | 11 ++++------- .../java/com/songoda/core/configuration/Config.java | 7 +++++++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Core/src/main/java/com/songoda/core/configuration/Comment.java b/Core/src/main/java/com/songoda/core/configuration/Comment.java index bbc97482..206471b9 100644 --- a/Core/src/main/java/com/songoda/core/configuration/Comment.java +++ b/Core/src/main/java/com/songoda/core/configuration/Comment.java @@ -23,24 +23,21 @@ public class Comment { } public Comment(String... lines) { - this.lines.addAll(Arrays.asList(lines)); + this(null, Arrays.asList(lines)); } public Comment(List lines) { - if (lines != null) { - this.lines.addAll(lines); - } + this(null, lines); } public Comment(CommentStyle commentStyle, String... lines) { - this.commentStyle = commentStyle; - this.lines.addAll(Arrays.asList(lines)); + this(commentStyle, Arrays.asList(lines)); } public Comment(CommentStyle commentStyle, List lines) { this.commentStyle = commentStyle; if (lines != null) { - this.lines.addAll(lines); + lines.forEach(s -> this.lines.addAll(Arrays.asList(s.split("\n")))); } } diff --git a/Core/src/main/java/com/songoda/core/configuration/Config.java b/Core/src/main/java/com/songoda/core/configuration/Config.java index d7909b19..0cee70c2 100644 --- a/Core/src/main/java/com/songoda/core/configuration/Config.java +++ b/Core/src/main/java/com/songoda/core/configuration/Config.java @@ -109,6 +109,13 @@ public class Config extends ConfigSection { */ int commentSpacing = 1; + public Config() { + this.plugin = null; + this.file = null; + dirName = null; + fileName = null; + } + public Config(@NotNull File file) { this.plugin = null; this.file = file.getAbsoluteFile(); From 44ad32bff0a22fed1b79c58107b33a8df5f45513 Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Sun, 27 Oct 2019 15:53:56 -0500 Subject: [PATCH 04/11] fix yaml lang support, add multiline scalars and a list method --- .../java/com/songoda/core/locale/Locale.java | 122 ++++++++++++------ .../java/com/songoda/core/locale/Message.java | 11 ++ 2 files changed, 92 insertions(+), 41 deletions(-) diff --git a/Core/src/main/java/com/songoda/core/locale/Locale.java b/Core/src/main/java/com/songoda/core/locale/Locale.java index 73fcf37f..cf9da6d6 100644 --- a/Core/src/main/java/com/songoda/core/locale/Locale.java +++ b/Core/src/main/java/com/songoda/core/locale/Locale.java @@ -184,64 +184,70 @@ public class Locale { // Write new changes to existing files, if any at all private static boolean updateFiles(Plugin plugin, InputStream defaultFile, File existingFile, boolean builtin) { - boolean changed = false; - List defaultLines, existingLines; try (BufferedInputStream defaultIn = new BufferedInputStream(defaultFile); BufferedInputStream existingIn = new BufferedInputStream(new FileInputStream(existingFile))) { Charset defaultCharset = TextUtils.detectCharset(defaultIn, StandardCharsets.UTF_8); Charset existingCharset = TextUtils.detectCharset(existingIn, StandardCharsets.UTF_8); - try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(existingFile, true), existingCharset); - BufferedReader defaultReaderOriginal = new BufferedReader(new InputStreamReader(defaultIn, defaultCharset)); + try (BufferedReader defaultReaderOriginal = new BufferedReader(new InputStreamReader(defaultIn, defaultCharset)); BufferedReader existingReaderOriginal = new BufferedReader(new InputStreamReader(existingIn, existingCharset)); BufferedReader defaultReader = translatePropertyToYAML(defaultReaderOriginal, defaultCharset); BufferedReader existingReader = translatePropertyToYAML(existingReaderOriginal, existingCharset);) { - defaultLines = defaultReader.lines().map(s -> s.replaceAll("[\uFEFF\uFFFE\u200B]", "")).collect(Collectors.toList()); - existingLines = existingReader.lines().map(s -> s.replaceAll("[\uFEFF\uFFFE\u200B]", "").split("\\s*=")[0]).collect(Collectors.toList()); - for (String defaultValue : defaultLines) { + Config existingLang = new Config(existingFile); + existingLang.load(existingReader); + translateMsgRoot(existingLang, existingFile, existingCharset); - if (defaultValue.isEmpty() || defaultValue.startsWith("#")) { + Config defaultLang = new Config(); + String defaultData = defaultReader.lines().map(s -> s.replaceAll("[\uFEFF\uFFFE\u200B]", "")).collect(Collectors.joining("\n")); + defaultLang.loadFromString(defaultData); + translateMsgRoot(defaultLang, defaultData, defaultCharset); + + List added = new ArrayList(); + + for (String defaultValueKey : defaultLang.getKeys(true)) { + + Object val = defaultLang.get(defaultValueKey); + if (val instanceof ConfigSection) { continue; } - String key = defaultValue.split("\\s*:")[0]; - - if (!existingLines.contains(key)) { - if (!changed) { - writer.write("\n\n"); - // Leave a note alerting the user of the newly added messages. - writer.write("# New messages for " + plugin.getName() + " v" + plugin.getDescription().getVersion() + "."); - - // If changes were found outside of the default file leave a note explaining that. - if (!builtin) { - writer.write("\n"); - writer.write("# These translations were found untranslated, join\n"); - writer.write("# our translation Discord https://discord.gg/f7fpZEf\n"); - writer.write("# to request an official update!\n"); - } - } - - writer.write("\n"); - - // re-encode to target format? (transform down, not up) -// if (defaultCharset != existingCharset) { -// byte[] encoded = defaultValue.getBytes(defaultCharset); -// defaultValue = new String(encoded, 2, encoded.length - 2, existingCharset); -// } - writer.write(defaultValue); - - changed = true; + if (!existingLang.contains(defaultValueKey)) { + added.add(defaultValueKey); + existingLang.set(defaultValueKey, val); } } + + if (!added.isEmpty()) { + if (!builtin) { + existingLang.setHeader("New messages added for " + plugin.getName() + " v" + plugin.getDescription().getVersion() + ".", + "", + "These translations were found untranslated, join", + "our translation Discord https://discord.gg/f7fpZEf", + "to request an official update!", + "", + added.stream().collect(Collectors.joining("\n")) + ); + } else { + existingLang.setHeader("New messages added for " + plugin.getName() + " v" + plugin.getDescription().getVersion() + ".", + "", + added.stream().collect(Collectors.joining("\n")) + ); + } + existingLang.setRootNodeSpacing(0); + existingLang.save(); + } + return !added.isEmpty(); + } catch (InvalidConfigurationException ex) { + plugin.getLogger().log(Level.SEVERE, "Error checking config " + existingFile.getName(), ex); } } catch (IOException e) { return false; } - return changed; + return false; } /** @@ -271,8 +277,11 @@ public class Locale { Config lang = new Config(file); lang.load(reader); translateMsgRoot(lang, file, charset); - // todo: how should string lists be handled? - lang.getValues(true).forEach((k, v) -> nodes.put(k, v.toString())); + // load lists as strings with newlines + lang.getValues(true).forEach((k, v) -> nodes.put(k, + v instanceof List + ? (((List) v).stream().map(l -> l.toString()).collect(Collectors.joining("\n")).toString()) + : v.toString())); } catch (IOException e) { e.printStackTrace(); return false; @@ -296,9 +305,15 @@ public class Locale { } } Matcher matcher; - if ((line = line.trim().replace('\r', ' ')).isEmpty() || line.startsWith("#") /* Comment */ - || !(matcher = OLD_NODE_PATTERN.matcher(line)).find()) { - output.append(line).append("\n"); + if ((line = line.replace('\r', ' ')).trim().isEmpty() || line.trim().startsWith("#") /* Comment */ + // need to trim the search group because tab characters somehow ended up at the end of lines in a lot of these files + || !(matcher = OLD_NODE_PATTERN.matcher(line.trim())).find()) { + if (line.startsWith("//")) { + // someone used an improper comment in some files *grumble grumble* + output.append("#").append(line).append("\n"); + } else { + output.append(line).append("\n"); + } } else { output.append(matcher.group(1)).append(": \"").append(matcher.group(2)).append("\"\n"); } @@ -333,6 +348,31 @@ public class Locale { } } + protected static void translateMsgRoot(Config lang, String file, Charset charset) throws IOException { + List msgs = lang.getValues(true).entrySet().stream() + .filter(e -> e.getValue() instanceof ConfigSection) + .map(e -> e.getKey()) + .collect(Collectors.toList()); + if (!msgs.isEmpty()) { + String source[] = file.split("\n"); + String line; + for (int lineNumber = 0; lineNumber < source.length; lineNumber++) { + line = source[lineNumber]; + if (lineNumber == 0) { + // remove BOM markers, if any + line = line.replaceAll("[\uFEFF\uFFFE\u200B]", ""); + } + Matcher matcher; + if (!(line = line.trim()).isEmpty() && !line.startsWith("#") + && (matcher = OLD_NODE_PATTERN.matcher(line)).find()) { + if (msgs.contains(matcher.group(1))) { + lang.set(matcher.group(1) + ".message", matcher.group(2)); + } + } + } + } + } + /** * Supply the Message object with the plugins prefix. * diff --git a/Core/src/main/java/com/songoda/core/locale/Message.java b/Core/src/main/java/com/songoda/core/locale/Message.java index 9561441d..8472f4f6 100644 --- a/Core/src/main/java/com/songoda/core/locale/Message.java +++ b/Core/src/main/java/com/songoda/core/locale/Message.java @@ -1,6 +1,8 @@ package com.songoda.core.locale; import com.songoda.core.compatibility.ServerVersion; +import java.util.Arrays; +import java.util.List; import java.util.regex.Matcher; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; @@ -126,6 +128,15 @@ public class Message { return ChatColor.translateAlternateColorCodes('&', this.message); } + /** + * Get and format the held message + * + * @return the message + */ + public List getMessageLines() { + return Arrays.asList(ChatColor.translateAlternateColorCodes('&', this.message).split("\n|\\|")); + } + /** * Get the held message * From 39f119cdebcae84d91e3926f823d739f758c49dd Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Sun, 27 Oct 2019 15:54:26 -0500 Subject: [PATCH 05/11] version 2.2.7 --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bc2c7f3d..f0b34429 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,7 +4,7 @@ stages: variables: name: "SongodaCore" path: "/builds/$CI_PROJECT_PATH" - version: "2.2.6" + version: "2.2.7" build: stage: build From 9e7b513288d72c3322c796d0f537c2fbf2aa7722 Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Sun, 2 Feb 2020 15:10:37 -0600 Subject: [PATCH 06/11] add 1.15 anvils, add close method for Gui --- Core/pom.xml | 7 + .../songoda/core/database/MySQLConnector.java | 2 + .../main/java/com/songoda/core/gui/Gui.java | 12 + .../java/com/songoda/core/nms/NmsManager.java | 2 + NMS/NMS-v1_15_R1/pom.xml | 29 +++ .../nms/v1_15_R1/AnvilInventoryCustom.java | 22 ++ .../songoda/core/nms/v1_15_R1/AnvilView.java | 229 ++++++++++++++++++ .../com/songoda/core/nms/v1_15_R1/NMS.java | 24 ++ pom.xml | 1 + 9 files changed, 328 insertions(+) create mode 100644 NMS/NMS-v1_15_R1/pom.xml create mode 100644 NMS/NMS-v1_15_R1/src/com/songoda/core/nms/v1_15_R1/AnvilInventoryCustom.java create mode 100644 NMS/NMS-v1_15_R1/src/com/songoda/core/nms/v1_15_R1/AnvilView.java create mode 100644 NMS/NMS-v1_15_R1/src/com/songoda/core/nms/v1_15_R1/NMS.java diff --git a/Core/pom.xml b/Core/pom.xml index 04b9827b..53b2ea6b 100644 --- a/Core/pom.xml +++ b/Core/pom.xml @@ -182,6 +182,13 @@ jar compile + + ${project.groupId} + SongodaCore-NMS-v1_15_R1 + ${project.version} + jar + compile + diff --git a/Core/src/main/java/com/songoda/core/database/MySQLConnector.java b/Core/src/main/java/com/songoda/core/database/MySQLConnector.java index d072ecbc..5494e682 100644 --- a/Core/src/main/java/com/songoda/core/database/MySQLConnector.java +++ b/Core/src/main/java/com/songoda/core/database/MySQLConnector.java @@ -16,6 +16,8 @@ public class MySQLConnector implements DatabaseConnector { public MySQLConnector(Plugin plugin, String hostname, int port, String database, String username, String password, boolean useSSL) { this.plugin = plugin; + System.out.println("connecting to " + hostname + " : " + port + " with " + password); + HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://" + hostname + ":" + port + "/" + database + "?useSSL=" + useSSL); config.setUsername(username); diff --git a/Core/src/main/java/com/songoda/core/gui/Gui.java b/Core/src/main/java/com/songoda/core/gui/Gui.java index ad44859a..fb36f231 100644 --- a/Core/src/main/java/com/songoda/core/gui/Gui.java +++ b/Core/src/main/java/com/songoda/core/gui/Gui.java @@ -155,6 +155,18 @@ public class Gui { .collect(Collectors.toList()) .forEach(Player::closeInventory); } + + /** + * Close the GUI as if the player closed it normally + */ + public void close() { + allowClose = true; + inventory.getViewers().stream() + .filter(e -> e instanceof Player) + .map(e -> (Player) e) + .collect(Collectors.toList()) + .forEach(Player::closeInventory); + } @NotNull public GuiType getType() { diff --git a/Core/src/main/java/com/songoda/core/nms/NmsManager.java b/Core/src/main/java/com/songoda/core/nms/NmsManager.java index e0fec81b..d7fe0795 100644 --- a/Core/src/main/java/com/songoda/core/nms/NmsManager.java +++ b/Core/src/main/java/com/songoda/core/nms/NmsManager.java @@ -40,6 +40,8 @@ public class NmsManager { return new com.songoda.core.nms.v1_13_R2.NMS(); case "v1_14_R1": return new com.songoda.core.nms.v1_14_R1.NMS(); + case "v1_15_R1": + return new com.songoda.core.nms.v1_15_R1.NMS(); } Logger.getLogger(NmsManager.class.getName()).log(Level.SEVERE, "Failed to load NMS for this server version: version {0} not found", serverPackageVersion); return null; diff --git a/NMS/NMS-v1_15_R1/pom.xml b/NMS/NMS-v1_15_R1/pom.xml new file mode 100644 index 00000000..9b46038d --- /dev/null +++ b/NMS/NMS-v1_15_R1/pom.xml @@ -0,0 +1,29 @@ + + + + com.songoda + SongodaCore-Modules + maven-version-number + ../../ + + + SongodaCore-NMS-v1_15_R1 + + 4.0.0 + jar + + + + com.destroystokyo.papermc + paper + 1.15.1 + provided + + + com.songoda + SongodaCore-NMS-API + ${project.version} + provided + + + \ No newline at end of file diff --git a/NMS/NMS-v1_15_R1/src/com/songoda/core/nms/v1_15_R1/AnvilInventoryCustom.java b/NMS/NMS-v1_15_R1/src/com/songoda/core/nms/v1_15_R1/AnvilInventoryCustom.java new file mode 100644 index 00000000..4377c4e5 --- /dev/null +++ b/NMS/NMS-v1_15_R1/src/com/songoda/core/nms/v1_15_R1/AnvilInventoryCustom.java @@ -0,0 +1,22 @@ +package com.songoda.core.nms.v1_15_R1; + +import net.minecraft.server.v1_15_R1.ContainerAnvil; +import net.minecraft.server.v1_15_R1.IInventory; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryAnvil; +import org.bukkit.inventory.InventoryHolder; + +public class AnvilInventoryCustom extends CraftInventoryAnvil { + + final InventoryHolder holder; + + public AnvilInventoryCustom(InventoryHolder holder, Location location, IInventory inventory, IInventory resultInventory, ContainerAnvil container) { + super(location, inventory, resultInventory, container); + this.holder = holder; + } + + @Override + public InventoryHolder getHolder() { + return holder; + } +} diff --git a/NMS/NMS-v1_15_R1/src/com/songoda/core/nms/v1_15_R1/AnvilView.java b/NMS/NMS-v1_15_R1/src/com/songoda/core/nms/v1_15_R1/AnvilView.java new file mode 100644 index 00000000..34821035 --- /dev/null +++ b/NMS/NMS-v1_15_R1/src/com/songoda/core/nms/v1_15_R1/AnvilView.java @@ -0,0 +1,229 @@ +package com.songoda.core.nms.v1_15_R1; + +import com.songoda.core.nms.CustomAnvil; +import com.songoda.core.nms.methods.AnvilTextChange; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.logging.Level; +import java.util.logging.Logger; +import net.minecraft.server.v1_15_R1.BlockPosition; +import net.minecraft.server.v1_15_R1.ChatMessage; +import net.minecraft.server.v1_15_R1.Container; +import net.minecraft.server.v1_15_R1.ContainerAccess; +import net.minecraft.server.v1_15_R1.ContainerAnvil; +import net.minecraft.server.v1_15_R1.Containers; +import net.minecraft.server.v1_15_R1.EntityHuman; +import net.minecraft.server.v1_15_R1.EntityPlayer; +import net.minecraft.server.v1_15_R1.IInventory; +import net.minecraft.server.v1_15_R1.PacketPlayOutOpenWindow; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventoryView; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; + +public class AnvilView extends ContainerAnvil implements CustomAnvil { + + private final EntityPlayer entity; + private final Inventory inventory; + private String customTitle = "Repairing"; + private int cost = -1; + private boolean canUse = true; + private AnvilTextChange textChange = null; + + // used for setting custom inventory + static Field mc_ContainerAnvil_repairInventory; // subcontainer with only the result + static Field mc_ContainerAnvil_resultInventory; // full inventory + static Field mc_ContainerAnvil_bukkitEntity; + + static { + try { + mc_ContainerAnvil_repairInventory = ContainerAnvil.class.getDeclaredField("repairInventory"); + mc_ContainerAnvil_repairInventory.setAccessible(true); + mc_ContainerAnvil_resultInventory = ContainerAnvil.class.getDeclaredField("resultInventory"); + mc_ContainerAnvil_resultInventory.setAccessible(true); + mc_ContainerAnvil_bukkitEntity = ContainerAnvil.class.getDeclaredField("bukkitEntity"); + mc_ContainerAnvil_bukkitEntity.setAccessible(true); + } catch (Exception ex) { + Logger.getLogger(AnvilView.class.getName()).log(Level.SEVERE, "Anvil Setup Error", ex); + } + } + + static Method mc_ContainerProperty_set; + static Method mc_ContainerProperty_get; + // 1.15 made this field public again, but now it's final. idk. + static Field mc_Container_windowId; + // 1.14 also introduced a title field, also private, which can only be set once and can't be checked + static Field mc_Container_title; + + static { + try { + mc_Container_title = Container.class.getDeclaredField("title"); + mc_Container_title.setAccessible(true); + mc_Container_windowId = Container.class.getDeclaredField("windowId"); + mc_Container_windowId.setAccessible(true); + + // remove the final modifier + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + modifiersField.setInt(mc_Container_windowId, mc_Container_windowId.getModifiers() & ~Modifier.FINAL); + } catch (Exception ex) { + Logger.getLogger(AnvilView.class.getName()).log(Level.SEVERE, "Anvil Setup Error", ex); + } + } + + public AnvilView(int id, EntityPlayer entity, InventoryHolder holder) { + super(id, entity.inventory, ContainerAccess.at(entity.world, new BlockPosition(0, 0, 0))); + this.setTitle(new ChatMessage(customTitle != null ? customTitle : "")); + this.checkReachable = false; + this.entity = entity; + if(holder != null) { + this.inventory = getBukkitView(entity, holder).getTopInventory(); + } else { + this.inventory = getBukkitView().getTopInventory(); + } + } + + public CraftInventoryView getBukkitView(EntityHuman player, InventoryHolder holder) { + try { + AnvilInventoryCustom craftInventory = new AnvilInventoryCustom(holder, + new Location(entity.world.getWorld(), 0, 0, 0), + (IInventory) mc_ContainerAnvil_repairInventory.get(this), + (IInventory) mc_ContainerAnvil_resultInventory.get(this), this); + CraftInventoryView view = new CraftInventoryView(player.getBukkitEntity(), craftInventory, this); + mc_ContainerAnvil_bukkitEntity.set(this, view); + return view; + } catch (Exception ex) { + Logger.getLogger(AnvilView.class.getName()).log(Level.SEVERE, "Anvil Setup Error", ex); + } + return getBukkitView(); + } + + @Override + public boolean canUse(EntityHuman entityhuman) { + return canUse; + } + + @Override + public void e() { + super.e(); + if (cost >= 0) { + this.levelCost.set(cost); + } + textChange.onChange(); + } + + @Override + public void update() { + e(); + } + + @Override + public String getRenameText() { + return this.renameText; + } + + @Override + public void setRenameText(String text) { + this.a(text); + } + + @Override + public void setOnChange(AnvilTextChange handler) { + textChange = handler; + } + + @Override + public String getCustomTitle() { + return customTitle; + } + + @Override + public void setCustomTitle(String title) { + this.customTitle = title; + try { + mc_Container_title.set(this, new ChatMessage(customTitle != null ? customTitle : "")); + } catch (Exception ex) { + Logger.getLogger(AnvilView.class.getName()).log(Level.SEVERE, "Anvil Error", ex); + } + } + + @Override + public void setLevelCost(int cost) { + this.cost = cost; + } + + @Override + public int getLevelCost() { + if (cost >= 0) { + return cost; + } else { + return this.levelCost.get(); + } + } + + @Override + public void setCanUse(boolean bool) { + this.canUse = bool; + } + + @Override + public ItemStack getLeftInput() { + return inventory.getItem(0); + } + + @Override + public ItemStack getRightInput() { + return inventory.getItem(1); + } + + @Override + public ItemStack getOutput() { + return inventory.getItem(2); + } + + @Override + public void setLeftInput(ItemStack item) { + inventory.setItem(0, item); + } + + @Override + public void setRightInput(ItemStack item) { + inventory.setItem(1, item); + } + + @Override + public void setOutput(ItemStack item) { + inventory.setItem(2, item); + } + + @Override + public Inventory getInventory() { + return inventory; + } + + @Override + public void open() { + + // Counter stuff that the game uses to keep track of inventories + int id = entity.nextContainerCounter(); + + // Send the packet + entity.playerConnection.sendPacket(new PacketPlayOutOpenWindow(id, Containers.ANVIL, new ChatMessage(customTitle != null ? customTitle : ""))); + + // Set their active container to this anvil + entity.activeContainer = this; + + try { + // Set their active container window id to that counter stuff + mc_Container_windowId.set(this, id); + } catch (Exception ex) { + Logger.getLogger(AnvilView.class.getName()).log(Level.SEVERE, "Anvil Create Error", ex); + } + + // Add the slot listener + entity.activeContainer.addSlotListener(entity); + } + +} diff --git a/NMS/NMS-v1_15_R1/src/com/songoda/core/nms/v1_15_R1/NMS.java b/NMS/NMS-v1_15_R1/src/com/songoda/core/nms/v1_15_R1/NMS.java new file mode 100644 index 00000000..f27203aa --- /dev/null +++ b/NMS/NMS-v1_15_R1/src/com/songoda/core/nms/v1_15_R1/NMS.java @@ -0,0 +1,24 @@ +package com.songoda.core.nms.v1_15_R1; + +import com.songoda.core.nms.CoreNMS; +import com.songoda.core.nms.CustomAnvil; +import net.minecraft.server.v1_15_R1.EntityPlayer; +import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.inventory.InventoryHolder; + +public class NMS implements CoreNMS { + + @Override + public CustomAnvil createAnvil(Player player) { + EntityPlayer p = ((CraftPlayer) player).getHandle(); + return new AnvilView(p.nextContainerCounter(), p, null); + } + + @Override + public CustomAnvil createAnvil(Player player, InventoryHolder holder) { + EntityPlayer p = ((CraftPlayer) player).getHandle(); + return new AnvilView(p.nextContainerCounter(), p, holder); + } + +} diff --git a/pom.xml b/pom.xml index b42ccf44..3cd4a036 100644 --- a/pom.xml +++ b/pom.xml @@ -20,6 +20,7 @@ NMS/NMS-v1_13_R1 NMS/NMS-v1_13_R2 NMS/NMS-v1_14_R1 + NMS/NMS-v1_15_R1 From cae75c423e139701113d69d4930bdb4974750dfe Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Sun, 2 Feb 2020 15:28:24 -0600 Subject: [PATCH 07/11] add 1.15 material analouges --- .../core/compatibility/CompatibleSound.java | 41 ++++++++++--------- .../compatibility/LegacyMaterialAnalouge.java | 9 ++++ 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/Core/src/main/java/com/songoda/core/compatibility/CompatibleSound.java b/Core/src/main/java/com/songoda/core/compatibility/CompatibleSound.java index 0cb6762a..76f36597 100644 --- a/Core/src/main/java/com/songoda/core/compatibility/CompatibleSound.java +++ b/Core/src/main/java/com/songoda/core/compatibility/CompatibleSound.java @@ -25,26 +25,27 @@ public enum CompatibleSound { // https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/mapping-and-modding-tutorials/2213619-1-8-all-playsound-sound-arguments /* 1.15 */ - BLOCK_BEEHIVE_DRIP, - BLOCK_BEEHIVE_ENTER, - BLOCK_BEEHIVE_EXIT, - BLOCK_BEEHIVE_SHEAR, - BLOCK_BEEHIVE_WORK, - BLOCK_HONEY_BLOCK_BREAK, - BLOCK_HONEY_BLOCK_FALL, - BLOCK_HONEY_BLOCK_HIT, - BLOCK_HONEY_BLOCK_PLACE, - BLOCK_HONEY_BLOCK_SLIDE, - BLOCK_HONEY_BLOCK_STEP, - ENTITY_BEE_DEATH, - ENTITY_BEE_HURT, - ENTITY_BEE_LOOP, - ENTITY_BEE_LOOP_AGGRESSIVE, - ENTITY_BEE_POLLINATE, - ENTITY_BEE_STING, - ENTITY_IRON_GOLEM_DAMAGE, - ENTITY_IRON_GOLEM_REPAIR, - ITEM_HONEY_BOTTLE_DRINK, + // TODO: add similar sounds for older versions + BLOCK_BEEHIVE_DRIP(ServerVersion.V1_15, v(null, true)), + BLOCK_BEEHIVE_ENTER(ServerVersion.V1_15, v(null, true)), + BLOCK_BEEHIVE_EXIT(ServerVersion.V1_15, v(null, true)), + BLOCK_BEEHIVE_SHEAR(ServerVersion.V1_15, v(null, true)), + BLOCK_BEEHIVE_WORK(ServerVersion.V1_15, v(null, true)), + BLOCK_HONEY_BLOCK_BREAK(ServerVersion.V1_15, v(null, true)), + BLOCK_HONEY_BLOCK_FALL(ServerVersion.V1_15, v(null, true)), + BLOCK_HONEY_BLOCK_HIT(ServerVersion.V1_15, v(null, true)), + BLOCK_HONEY_BLOCK_PLACE(ServerVersion.V1_15, v(null, true)), + BLOCK_HONEY_BLOCK_SLIDE(ServerVersion.V1_15, v(null, true)), + BLOCK_HONEY_BLOCK_STEP(ServerVersion.V1_15, v(null, true)), + ENTITY_BEE_DEATH(ServerVersion.V1_15, v(null, true)), + ENTITY_BEE_HURT(ServerVersion.V1_15, v(null, true)), + ENTITY_BEE_LOOP(ServerVersion.V1_15, v(null, true)), + ENTITY_BEE_LOOP_AGGRESSIVE(ServerVersion.V1_15, v(null, true)), + ENTITY_BEE_POLLINATE(ServerVersion.V1_15, v(null, true)), + ENTITY_BEE_STING(ServerVersion.V1_15, v(null, true)), + ENTITY_IRON_GOLEM_DAMAGE(ServerVersion.V1_15, v(null, true)), + ENTITY_IRON_GOLEM_REPAIR(ServerVersion.V1_15, v(null, true)), + ITEM_HONEY_BOTTLE_DRINK(ServerVersion.V1_15, v(null, true)), AMBIENT_CAVE(ServerVersion.V1_9, v("AMBIENCE_CAVE")), AMBIENT_UNDERWATER_ENTER, diff --git a/Core/src/main/java/com/songoda/core/compatibility/LegacyMaterialAnalouge.java b/Core/src/main/java/com/songoda/core/compatibility/LegacyMaterialAnalouge.java index b457b954..12377773 100644 --- a/Core/src/main/java/com/songoda/core/compatibility/LegacyMaterialAnalouge.java +++ b/Core/src/main/java/com/songoda/core/compatibility/LegacyMaterialAnalouge.java @@ -12,6 +12,15 @@ import org.bukkit.inventory.ItemStack; */ public enum LegacyMaterialAnalouge { + /* 1.15 */ + BEE_SPAWN_EGG(ServerVersion.V1_15, "PARROT_SPAWN_EGG", ServerVersion.V1_12, "MONSTER_EGG", (byte) 65), + BEE_NEST(ServerVersion.V1_15, "BIRCH_LOG", "LOG", (byte) 2), + BEEHIVE(ServerVersion.V1_15, "SLIME_BLOCK", ServerVersion.V1_8, "WOOL", (byte) 4), + HONEY_BLOCK(ServerVersion.V1_15, "SLIME_BLOCK", ServerVersion.V1_8, "WOOL", (byte) 4), + HONEY_BOTTLE(ServerVersion.V1_15, "DRAGON_BREATH", ServerVersion.V1_9, "POTION", (byte) 0), + HONEYCOMB(ServerVersion.V1_15, "SUNFLOWER", "DOUBLE_PLANT", (byte) 0), + HONEYCOMB_BLOCK(ServerVersion.V1_15, "SLIME_BLOCK", ServerVersion.V1_8, "WOOL", (byte) 4), + ACACIA_BOAT(ServerVersion.V1_9, "BOAT"), ACACIA_BUTTON(ServerVersion.V1_13, "WOOD_BUTTON"), ACACIA_DOOR(ServerVersion.V1_8, "WOOD_DOOR"), // TODO? ACACIA_DOOR & WOODEN_DOOR are the legacy block variants From a588d18d26f0b97b4d658cb6f3941942ad663acd Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Sun, 2 Feb 2020 15:30:01 -0600 Subject: [PATCH 08/11] fix modern redstone update methods --- .../songoda/core/utils/BlockUtilsModern.java | 195 ++++++++++++++---- 1 file changed, 157 insertions(+), 38 deletions(-) diff --git a/Core/src/main/java/com/songoda/core/utils/BlockUtilsModern.java b/Core/src/main/java/com/songoda/core/utils/BlockUtilsModern.java index aeac8efa..4ec4891f 100644 --- a/Core/src/main/java/com/songoda/core/utils/BlockUtilsModern.java +++ b/Core/src/main/java/com/songoda/core/utils/BlockUtilsModern.java @@ -1,5 +1,9 @@ package com.songoda.core.utils; +import java.lang.reflect.Method; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.bukkit.Bukkit; import org.bukkit.Effect; import org.bukkit.Material; import org.bukkit.block.Block; @@ -8,6 +12,7 @@ import org.bukkit.block.data.Ageable; import org.bukkit.block.data.Bisected; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.AnaloguePowerable; +import org.bukkit.block.data.Powerable; import org.bukkit.block.data.type.Door; import org.bukkit.block.data.type.Gate; import org.bukkit.block.data.type.Switch; @@ -15,47 +20,161 @@ import org.bukkit.block.data.type.TrapDoor; public class BlockUtilsModern { - protected static void _updatePressurePlateModern(Block plate, int power) { - BlockData blockData = plate.getBlockData(); - if (blockData instanceof AnaloguePowerable) { - AnaloguePowerable a = (AnaloguePowerable) blockData; - a.setPower(Math.max(a.getMaximumPower(), power)); - plate.setBlockData(a); - } - } + protected static void _updatePressurePlateModern(Block plate, int power) { + BlockData blockData = plate.getBlockData(); + boolean update = false; + if (blockData instanceof AnaloguePowerable) { + AnaloguePowerable a = (AnaloguePowerable) blockData; + int toPower = Math.min(a.getMaximumPower(), power); + if ((update = toPower != a.getPower())) { + a.setPower(toPower); + plate.setBlockData(a); + } + } else if (blockData instanceof Powerable) { + Powerable p = (Powerable) blockData; + if ((update = p.isPowered() != (power != 0))) { + p.setPowered(power != 0); + plate.setBlockData(p); + } + } + if (update) { + _updateRedstoneNeighbours(plate); + } + } - protected static void _toggleLeverModern(Block lever) { - BlockData blockData = lever.getBlockData(); - if (blockData instanceof Switch) { - Switch s = (Switch) blockData; - s.setPowered(!s.isPowered()); - lever.setBlockData(s); - //lever.getWorld().playEffect(lever.getLocation(), Effect.CLICK1, 0); - lever.getState().update(); - } - } + protected static void _toggleLeverModern(Block lever) { + BlockData blockData = lever.getBlockData(); + if (blockData instanceof Switch) { + Switch s = (Switch) blockData; + s.setPowered(!s.isPowered()); + lever.setBlockData(s); + _updateRedstoneNeighbours(lever); + } + } - protected static void _pressButtonModern(Block button) { - BlockData blockData = button.getBlockData(); - if (blockData instanceof Switch) { - Switch s = (Switch) blockData; - s.setPowered(true); - button.setBlockData(s); - //lever.getWorld().playEffect(lever.getLocation(), Effect.CLICK1, 0); - button.getState().update(); - } - } + protected static void _pressButtonModern(Block button) { + BlockData blockData = button.getBlockData(); + if (blockData instanceof Switch) { + Switch s = (Switch) blockData; + s.setPowered(true); + button.setBlockData(s); + _updateRedstoneNeighbours(button); + } + } - static void _releaseButtonModern(Block button) { - BlockData blockData = button.getBlockData(); - if (blockData instanceof Switch) { - Switch s = (Switch) blockData; - s.setPowered(false); - button.setBlockData(s); - //lever.getWorld().playEffect(lever.getLocation(), Effect.CLICK1, 0); - button.getState().update(); - } - } + static void _releaseButtonModern(Block button) { + BlockData blockData = button.getBlockData(); + if (blockData instanceof Switch) { + Switch s = (Switch) blockData; + s.setPowered(false); + button.setBlockData(s); + _updateRedstoneNeighbours(button); + } + } + + private static Class clazzCraftWorld, clazzCraftBlock, + clazzLeverBlock, clazzButtonBlock, clazzPressurePlateBlock; + private static Method craftWorld_getHandle, craftBlock_getNMSBlock, craftBlock_getPostition, craftBlockData_getState, + nmsLever_updateNeighbours, nmsButton_updateNeighbours, nmsPlate_updateNeighbours; + + static { + try { + // Cache reflection. + + String ver = Bukkit.getServer().getClass().getPackage().getName().substring(23); + clazzCraftWorld = Class.forName("org.bukkit.craftbukkit." + ver + ".CraftWorld"); + clazzCraftBlock = Class.forName("org.bukkit.craftbukkit." + ver + ".block.CraftBlock"); + //clazzBlockPosition = Class.forName("net.minecraft.server." + ver + ".BlockPosition"); + + craftWorld_getHandle = clazzCraftWorld.getMethod("getHandle"); + craftBlock_getNMSBlock = clazzCraftBlock.getDeclaredMethod("getNMSBlock"); + craftBlock_getNMSBlock.setAccessible(true); + craftBlock_getPostition = clazzCraftBlock.getDeclaredMethod("getPosition"); + + Class clazzCraftBlockData = Class.forName("org.bukkit.craftbukkit." + ver + ".block.data.CraftBlockData"); + craftBlockData_getState = clazzCraftBlockData.getDeclaredMethod("getState"); + + Class clazzWorld = Class.forName("net.minecraft.server." + ver + ".World"); + Class clazzBlockState = Class.forName("net.minecraft.server." + ver + ".IBlockData"); + Class clazzBlockPos = Class.forName("net.minecraft.server." + ver + ".BlockPosition"); + clazzLeverBlock = Class.forName("net.minecraft.server." + ver + ".BlockLever"); + clazzButtonBlock = Class.forName("net.minecraft.server." + ver + ".BlockButtonAbstract"); + clazzPressurePlateBlock = Class.forName("net.minecraft.server." + ver + ".BlockPressurePlateAbstract"); + + // nmsLever_updateNeighbours, nmsButton_updateNeighbours, nmsPlate_updateNeighbours + nmsLever_updateNeighbours = clazzLeverBlock.getDeclaredMethod("e", clazzBlockState, clazzWorld, clazzBlockPos); + nmsLever_updateNeighbours.setAccessible(true); + + nmsButton_updateNeighbours = clazzButtonBlock.getDeclaredMethod("f", clazzBlockState, clazzWorld, clazzBlockPos); + nmsButton_updateNeighbours.setAccessible(true); + + nmsPlate_updateNeighbours = clazzPressurePlateBlock.getDeclaredMethod("a", clazzWorld, clazzBlockPos); + nmsPlate_updateNeighbours.setAccessible(true); + } catch (Throwable ex) { + Logger.getLogger(BlockUtilsModern.class.getName()).log(Level.SEVERE, null, ex); + } + } + + static void _updateRedstoneNeighbours(Block block) { + try { + // spigot made some changes to how data updates work in 1.13+ + // updating the data value of a redstone power source + // does NOT update attatched block power, + // even if you update the block state. (Still broken last I checked in 1.15.2) + // so now we're going to manually call the updateNeighbours block method + + // invoke and cast objects. + Object cworld = clazzCraftWorld.cast(block.getWorld()); + Object mworld = craftWorld_getHandle.invoke(cworld); + Object cblock = clazzCraftBlock.cast(block); + Object mblock = craftBlock_getNMSBlock.invoke(cblock); + Object mpos = craftBlock_getPostition.invoke(cblock); + + //System.out.println(mblock.getClass()); + // now for testing stuff + if (clazzLeverBlock.isAssignableFrom(mblock.getClass())) { + final Object mstate = craftBlockData_getState.invoke(block.getBlockData()); + nmsLever_updateNeighbours.invoke(mblock, mstate, mworld, mpos); + } else if (clazzButtonBlock.isAssignableFrom(mblock.getClass())) { + final Object mstate = craftBlockData_getState.invoke(block.getBlockData()); + nmsButton_updateNeighbours.invoke(mblock, mstate, mworld, mpos); + } else if (clazzPressurePlateBlock.isAssignableFrom(mblock.getClass())) { + nmsPlate_updateNeighbours.invoke(mblock, mworld, mpos); + } else { + System.out.println("Unknown redstone: " + mblock.getClass().getName()); + } +// +// if(mblock instanceof net.minecraft.server.v1_15_R1.BlockLever) { +// Method updateNeighbours = net.minecraft.server.v1_15_R1.BlockLever.class.getDeclaredMethod("e", net.minecraft.server.v1_15_R1.IBlockData.class, net.minecraft.server.v1_15_R1.World.class, net.minecraft.server.v1_15_R1.BlockPosition.class); +// updateNeighbours.setAccessible(true); +// // IBlockData = block state after being powered +// +// updateNeighbours.invoke(mblock, +// ((org.bukkit.craftbukkit.v1_15_R1.block.data.CraftBlockData) block.getBlockData()).getState(), +// mworld, +// mpos); +// } else if(mblock instanceof net.minecraft.server.v1_15_R1.BlockButtonAbstract) { +// Method updateNeighbours = net.minecraft.server.v1_15_R1.BlockButtonAbstract.class.getDeclaredMethod("f", net.minecraft.server.v1_15_R1.IBlockData.class, net.minecraft.server.v1_15_R1.World.class, net.minecraft.server.v1_15_R1.BlockPosition.class); +// updateNeighbours.setAccessible(true); +// // IBlockData = block state after being powered +// +// updateNeighbours.invoke(mblock, +// ((org.bukkit.craftbukkit.v1_15_R1.block.data.CraftBlockData) block.getBlockData()).getState(), +// mworld, +// mpos); +// } else if(mblock instanceof net.minecraft.server.v1_15_R1.BlockPressurePlateAbstract) { +// Method updateNeighbours = net.minecraft.server.v1_15_R1.BlockPressurePlateAbstract.class.getDeclaredMethod("a", net.minecraft.server.v1_15_R1.World.class, net.minecraft.server.v1_15_R1.BlockPosition.class); +// updateNeighbours.setAccessible(true); +// // IBlockData = block state after being powered +// +// updateNeighbours.invoke(mblock, +// mworld, +// mpos); +// } + } catch (Throwable ex) { + Logger.getLogger(BlockUtilsModern.class.getName()).log(Level.SEVERE, null, ex); + } + } protected static void _toggleDoorStatesModern(boolean allowDoorToOpen, Block... doors) { for (Block door : doors) { From 9c4ac1efd2823e38bfb6ea2c911783cffb7c6c56 Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Sun, 2 Feb 2020 15:57:34 -0600 Subject: [PATCH 09/11] testing gitlab variables with maven --- .gitlab-ci.yml | 1 + settings.xml | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 settings.xml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5a050c5b..b2c5b9b4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,6 +10,7 @@ build: stage: build image: maven:3.5.3-jdk-8 script: + - sed -e "s/{REPO_USER}/$REPO_USER/g" -e "s/{REPO_PASS}/$REPO_PASS/g" settings.xml > ~/.m2/settings.xml - find $path/ -type f -name "*.xml" -print0 | xargs -0 sed -i -e s/maven-version-number/$version/g - find $path/ -type f -name "*.yml" -print0 | xargs -0 sed -i -e s/maven-version-number/$version/g - find $path/ -type f -name "*SongodaCore.java" -print0 | xargs -0 sed -i -e s/maven-version-number/$version/g diff --git a/settings.xml b/settings.xml new file mode 100644 index 00000000..35377ca4 --- /dev/null +++ b/settings.xml @@ -0,0 +1,9 @@ + + + + private + {REPO_USER} + {REPO_PASS} + + + \ No newline at end of file From 9821ec87bcc8affa6c8b002cfa415259e59016a0 Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Sun, 2 Feb 2020 16:00:08 -0600 Subject: [PATCH 10/11] testing gitlab variables with maven --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b2c5b9b4..e53fbdc0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,7 +10,7 @@ build: stage: build image: maven:3.5.3-jdk-8 script: - - sed -e "s/{REPO_USER}/$REPO_USER/g" -e "s/{REPO_PASS}/$REPO_PASS/g" settings.xml > ~/.m2/settings.xml + - sed -e "s/{REPO_USER}/$REPO_USER/g" -e "s/{REPO_PASS}/$REPO_PASS/g" $path/settings.xml > ~/.m2/settings.xml - find $path/ -type f -name "*.xml" -print0 | xargs -0 sed -i -e s/maven-version-number/$version/g - find $path/ -type f -name "*.yml" -print0 | xargs -0 sed -i -e s/maven-version-number/$version/g - find $path/ -type f -name "*SongodaCore.java" -print0 | xargs -0 sed -i -e s/maven-version-number/$version/g From 63705e95449457a3bab8781f8e9958488a78fc5a Mon Sep 17 00:00:00 2001 From: jascotty2 Date: Sun, 2 Feb 2020 16:05:07 -0600 Subject: [PATCH 11/11] add plugin version for maven source plugin --- Core/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/pom.xml b/Core/pom.xml index 0a0a5fb3..431e4b80 100644 --- a/Core/pom.xml +++ b/Core/pom.xml @@ -30,6 +30,7 @@ org.apache.maven.plugins maven-source-plugin + 3.2.1 attach-sources