├── .github
├── FUNDING.yml
└── stale.yml
├── .travis.yml
├── ISSUE_TEMPLATE.md
├── LICENSE
├── README.md
├── pom.xml
├── renovate.json
├── resources
├── config.yml
└── plugin.yml
├── settings.xml
└── src
└── org
└── inventivetalent
└── mapmanager
├── ArrayImage.java
├── CommandHandler.java
├── DefaultMapManager.java
├── DefaultMapWrapper.java
├── MapLimitExceededException.java
├── MapListener.java
├── MapManagerPlugin.java
├── MapSender.java
├── MultiMapWrapper.java
├── PacketListener.java
├── TimingsHelper.java
├── controller
├── MapController.java
└── MultiMapController.java
├── event
├── CreativeInventoryMapUpdateEvent.java
├── MapCancelEvent.java
├── MapContentUpdateEvent.java
└── MapInteractEvent.java
├── manager
└── MapManager.java
├── metrics
└── Metrics.java
├── util
├── Converter.java
├── MapColorPalette.java
├── MapColorSpaceData.java
├── bit
│ ├── BitInputStream.java
│ └── BitPacket.java
├── map
│ ├── map_1_12.ab
│ └── map_1_8_8.ab
└── mcsd
│ ├── MCSDBubbleFormat.java
│ ├── MCSDGenBukkit.java
│ └── MCSDWebbingCodec.java
└── wrapper
├── MapWrapper.java
└── MultiWrapper.java
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: InventivetalentDev
2 | patreon: inventivetalent
3 | custom: ["https://www.paypal.me/inventivetalent", "https://donation.inventivetalent.org"]
4 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/InventivetalentDev/MapManager/c038c00e36fd43b01dd78ef7b6389fc8ce89ae27/.github/stale.yml
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | jdk:
3 | - openjdk8
4 |
5 | script: "mvn deploy --settings settings.xml"
6 | env:
7 | global:
8 | - secure: "DY+dOd0Gz236ybU/s+6XyXD+JsjPEMkm4pozMwq6s5vI+hugPKF6v9vQtu/YJ1KV4fVPC5ZnNhmbx+qoUYtKNaC6W96h8UR7QUvC1WfWAp1/Epr6GSgqKn80mqD7Q+sUisUWRLf0l3xQvuVQqmPzcMOgIaIZfKn6VrPMS242GpFOsyK1Vuoyn3uOOmBuxeDQLfuYQRAsCqsWUQsC6QRD1rD1+zcqZo42rripvWIxsPVtMv15Hp8KuE1bl1Or/xCIv8JeSottw+6/hrjIAB0ewZ/kJwvMOxiQltfy9PY+NAHmVuX2hmnzS5BWEcDrRuBh1vHANGzIH3Jad8gwO7epiIykFU2CbpTkZ1htJeXDB9dSx311TTHBK6bZhHJJxaqeoNpWpS9Um9CkuWUC1s7tvmR5AwuMezyhgI3Luq71R17GnsseFxfrBOrJpJX0MTIAyU8PZm0zTmM0MW2fjaXmApw8InQOSBaVp+4WkoAAP1q6B3lcOThnhtgCZ+oR2zTnVxEVrV3pSpLwXQWlr+u8Zbwh4bFP39fwZZiYXnHRr9E0Ie2/nSQTF/JcewfgHQ/oo6oNJqgnCYTLuYKaSr9H0r2oTexyfcT3/arbTg6bDnqpcvuFYsA/8uPLrMKQ2QdOwRiIniCUoXhTe23fNOeZPcz682ndP8DuIx6e9saXT7M="
9 | - secure: "miJ6x2bqw/zy/6kYBA+s8HjFqy5m7D69LsD9TSa8EjVoEt7IGUOdevjAAyxba7RBmXUSq/bXiQa6DDkqb2Eb4lq/nucKO4Ub766+6Ci4Rcfp0c/QYjKDbZGthfEfe1imH8Y2u5b4Q7t/Vfmwktg7XagZ8ixD/n5/P0/gosjIiUE6Clw8sp3YZCtAUmS7Qq46q6yUkH2INNj0Q3biXmBdiVxO5RMqwcCipJSU/SdCRwrJmDoQaKx0YerKeQUHqNQ4J4u776+eIu3zotaVzw36kJieLrekkBNYqbLHK6MbcoKX70J09KFirRwoY4YGNLAal179rQpyZ6zWWokH+jfKTiez1pCb4zSxXYXFnn9hfYN2GC8oUrs87lrm1foZ6HtKag2d8GbdxYOTKXiuFGxwBJUad0As0jH81c5SCIYbzs2vfddb70p8fhDWWrs1Y6IcGgR8vcTwMvjIrOPSSLs3ERCZ+e+PGXLvy/HsKtFzXPC26pwvSfgYeSVJDs59ltNGbdfDmAItiLfQdPh2piPouYS61FaWpaO0Y3shI9HFpWL2eARe2b5cfbK9MMAn4/zV+AepsuGwXoOrHpqLH+B2CUFm1tuE9MegLry0jOjFs/NUnwpQb7KvE+3OF9uDR2R8j8DfUMuMqQygqm+18xtVvodfT/f0yOiiQNJ4lccMuJ8="
10 | deploy:
11 | provider: releases
12 | api_key:
13 | secure: "ruzHebe4wUDA1Rz5xwsOM9Kb3BuA57wvRDHLi5zL8u8SsRQwgBVtzgi6i8fOHu2trbVjmYMs/1dCjI09qW684FX3CWFC8QqE4MD73phwej+EQKVINwCBnAVbQ/RudK5LmkjhAHNuYjMnk33qT3ASQDFBb3LUVBTXqwQC2B5Ll2t+o7Zcsd8na/4loRNtor16kl7Rxj3s9jCKsRQAWA5wfOPVEDSZC3JVEroIytDF8avP1icTd+0m5vWvMh+BQbUvnuiU9n//U6Vc70jydo9EmTGF6PIQ978mbAbEbhcjiAyH9xJ9z8GOWc31JZS0OQfTNOwGpAk0euE0VHvMZ0tV/jICCpAOX5k5LJkK02jXkWZ/sARpC5aCx5cu8nSSuMhlCgqqXMq1WjdQlG76JtO2VOW/z+A4tsW3rlJ+sanAZxFby2ukgJRVPPwxjUIMS8ZfqskKN6SEXo/3IQrcIWCEUUQ0QbGjmsUBvJqQzad6lP2Uylt9ICCczuVcng1exUk64sbS5fcDdrsjmxVEcmEQJ3wGxeHDK3Lhtk6NxdItqlsOnXgeAA75b+Gix/A9VUb2PIU7Xq/CIytV+iBxuYsbTyS3E4M/ZFgSOjB/xNZz+3nzfvXQDsvbRxCX2uSYBfChKmRDAwJjsFkKI4lG624bUzj1feMcuv7p/31mOdwREyI="
14 | file_glob: true
15 | file:
16 | - "target/MapManager_v*.jar"
17 | skip_cleanup: true
18 | on:
19 | tags: true
20 |
--------------------------------------------------------------------------------
/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## What steps will reproduce the problem?
2 | 1.
3 | 2.
4 | 3.
5 |
6 | ## What were you expecting to happen? What happened instead?
7 |
8 | ## What version of the plugin are you using? *Type /version <Plugin Name>*
9 |
10 | ## What Spigot version are you using? *Type /version*
11 |
12 | ## What plugins ae you using? *Type /plugins*
13 |
14 | ## Do you have an error log? Use [pastebin.com](http://pastebin.com). *If you're not sure, upload your whole server log*
15 |
16 | ## Did your client crash? *Upload errors in .minecraft/logs/latest.log as well*
17 |
18 | ## Additional information? *(Are you using Bungeecord? Did it work in previous versions? etc.)*
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) inventivetalent
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MapManager
2 |
3 | [](https://travis-ci.org/InventivetalentDev/MapManager)
4 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | org.inventivetalent
8 | mapmanager
9 | 1.8.8-SNAPSHOT
10 | MapManager
11 |
12 |
13 | 16
14 | 16
15 |
16 |
17 |
18 | MapManager_v${project.version}
19 | src
20 |
21 |
22 | src
23 |
24 | **/*.java
25 |
26 |
27 |
28 | resources
29 | true
30 |
31 | plugin.yml
32 | config.yml
33 |
34 |
35 |
36 |
37 |
38 |
39 | org.apache.maven.plugins
40 | maven-compiler-plugin
41 | 3.8.1
42 |
43 | 16
44 | 16
45 |
46 |
47 |
48 | org.apache.maven.plugins
49 | maven-shade-plugin
50 | 3.3.0-SNAPSHOT
51 |
52 |
53 | package
54 |
55 | shade
56 |
57 |
58 |
59 |
60 | org.inventivetalent:mapmanager**
61 | org.inventivetalent:reflectionhelper**
62 | org.inventivetalent.spiget-update:bukkit**
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | sonatype-nexus-releases
74 | https://repo.inventivetalent.org/repository/maven-releases/
75 |
76 |
77 | sonatype-nexus-snapshots
78 | https://repo.inventivetalent.org/repository/maven-snapshots/
79 |
80 |
81 |
82 |
83 | io.papermc.paper
84 | paper-api
85 | 1.17.1-R0.1-SNAPSHOT
86 | provided
87 |
88 |
89 | org.inventivetalent.packetlistenerapi
90 | api
91 | 3.9.10-SNAPSHOT
92 |
93 |
94 | org.inventivetalent
95 | reflectionhelper
96 | 1.18.10-SNAPSHOT
97 |
98 |
99 | org.inventivetalent.spiget-update
100 | bukkit
101 | 1.4.2-SNAPSHOT
102 |
103 |
104 |
105 |
106 |
107 | inventive-repo
108 | https://repo.inventivetalent.org/content/groups/public/
109 |
110 |
111 | jitpack.io
112 | https://jitpack.io
113 |
114 |
115 | md_5-repo
116 | http://repo.md-5.net/content/repositories/public/
117 |
118 |
119 | sonatype
120 | https://oss.sonatype.org/content/groups/public/
121 |
122 |
123 | spigot-repo
124 | https://hub.spigotmc.org/nexus/content/groups/public/
125 |
126 |
127 |
128 |
129 | maven-snapshots
130 | https://repository.apache.org/content/repositories/snapshots/
131 |
132 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base"
4 | ],
5 | "maven": {
6 | "enabled": true
7 | },
8 | "ignoreUnstable": false,
9 | "hostRules": [{
10 | "hostType": "maven",
11 | "endpoint": "https://repo.inventivetalent.org/content/groups/public/"
12 | }]
13 | }
14 |
--------------------------------------------------------------------------------
/resources/config.yml:
--------------------------------------------------------------------------------
1 | # If vanilla maps should be allowed to be sent to the players (less efficient, since we need to check the id of every sent map)
2 | allowVanilla: true
3 |
4 | # Change this to a higher number to "preserve" a number of map IDs that won't be used by MapManager plugins
5 | # Please note that increasing this too much limits the amount of maps that plugins can create
6 | forcedOffset: 0
7 |
8 | # If the plugin checks for duplicate images before creating a new one (Less efficient when first creating a image, but more efficient overall)
9 | checkDuplicates: true
10 |
11 | # Cache the packet data in the image object (less CPU intensive for a lot of players, but probably a bit more memory intensive depending on the image size)
12 | cacheData: true
13 |
14 | sender:
15 | # Delay between map packets (ticks)
16 | delay: 2
17 |
18 | # Maximum amount of map packets sent at once
19 | amount: 10
20 |
21 | # Allow immediate sending of map data
22 | allowQueueBypass: true
23 |
24 | # Enable this if you are using PaperSpigot (and/or you get this error: http://paste.inventivetalent.org/damuhonebu)
25 | paperSpigot: false
--------------------------------------------------------------------------------
/resources/plugin.yml:
--------------------------------------------------------------------------------
1 | name: MapManager
2 | main: org.inventivetalent.mapmanager.MapManagerPlugin
3 | author: inventivetalent
4 | version: ${project.version}
5 | api-version: '1.17'
6 |
7 | softdepend: [PacketListenerApi]
8 |
9 | commands:
10 | mapmanager:
11 | aliases: [mmanager]
12 |
--------------------------------------------------------------------------------
/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | sonatype-nexus-releases
5 | ${env.CI_DEPLOY_USERNAME}
6 | ${env.CI_DEPLOY_PASSWORD}
7 |
8 |
9 | sonatype-nexus-snapshots
10 | ${env.CI_DEPLOY_USERNAME}
11 | ${env.CI_DEPLOY_PASSWORD}
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/ArrayImage.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager;
2 |
3 | import com.google.common.primitives.Ints;
4 | import org.inventivetalent.mapmanager.util.Converter;
5 | import org.inventivetalent.mapmanager.util.MapColorPalette;
6 |
7 | import java.awt.image.BufferedImage;
8 | import java.io.IOException;
9 | import java.io.InputStream;
10 | import java.io.OutputStream;
11 | import java.util.Arrays;
12 |
13 | /**
14 | * Container class for images
15 | *
16 | * Stores colors as an integer array
17 | */
18 | public class ArrayImage {
19 |
20 | protected byte[] array;
21 |
22 | private int width;
23 | private int height;
24 |
25 | protected int minX = 0;
26 | protected int minY = 0;
27 | protected int maxX = 128;
28 | protected int maxY = 128;
29 |
30 | private int imageType = BufferedImage.TYPE_4BYTE_ABGR;
31 |
32 | protected ArrayImage(byte[] data) {
33 | this.array = data;
34 | }
35 |
36 | /**
37 | * Convert a {@link BufferedImage} to an ArrayImage
38 | *
39 | * @param image image to convert
40 | */
41 | public ArrayImage(BufferedImage image) {
42 | this.imageType = image.getType();
43 |
44 | this.width = image.getWidth();
45 | this.height = image.getHeight();
46 |
47 | this.array = Converter.imageToBytes(image);
48 | }
49 |
50 | /**
51 | * @return the width of the image
52 | */
53 | @Deprecated
54 | public int getWidth() {
55 | return width;
56 | }
57 |
58 | /**
59 | * @return the height of the image
60 | */
61 | @Deprecated
62 | public int getHeight() {
63 | return height;
64 | }
65 |
66 | /**
67 | * Convert this image back to a {@link BufferedImage}
68 | *
69 | * @return new {@link BufferedImage}
70 | */
71 | @Deprecated
72 | public BufferedImage toBuffered() {
73 | BufferedImage image = new BufferedImage(getWidth(), getHeight(), this.imageType);
74 | for (int x = 0; x < width; x++) {
75 | for (int y = 0; y < height; y++) {
76 | image.setRGB(x, y, MapColorPalette.getRealColor(array[y * getWidth() + x]).getRGB());
77 | }
78 | }
79 | return image;
80 | }
81 |
82 | @Override
83 | public boolean equals(Object o) {
84 | if (this == o) { return true; }
85 | if (o == null || getClass() != o.getClass()) { return false; }
86 |
87 | ArrayImage that = (ArrayImage) o;
88 |
89 | if (width != that.width) { return false; }
90 | if (height != that.height) { return false; }
91 | return Arrays.equals(array, that.array);
92 |
93 | }
94 |
95 | @Override
96 | public int hashCode() {
97 | int result = array != null ? Arrays.hashCode(array) : 0;
98 | result = 31 * result + width;
99 | result = 31 * result + height;
100 | return result;
101 | }
102 |
103 | public static void writeToStream(ArrayImage image, OutputStream outputStream) throws IOException {
104 | outputStream.write(Ints.toByteArray(image.array.length));
105 |
106 | outputStream.write(image.array);
107 | }
108 |
109 | public static ArrayImage readFromStream(InputStream inputStream) throws IOException {
110 | byte[] lengthBytes = new byte[4];
111 | inputStream.read(lengthBytes, 0, 4);
112 |
113 | int length = Ints.fromByteArray(lengthBytes);
114 |
115 | byte[] data = new byte[length];
116 | inputStream.read(data);
117 |
118 | ArrayImage image = new ArrayImage(data);
119 |
120 | return image;
121 | }
122 |
123 | public static void writeMultiToSream(ArrayImage[][] images, OutputStream outputStream) throws IOException {
124 | outputStream.write(Ints.toByteArray(images.length));// width
125 | outputStream.write(Ints.toByteArray(images[0].length));// height
126 |
127 | for (int x = 0; x < images.length; x++) {
128 | if (images[x].length != images[0].length) { throw new IllegalArgumentException("image is not rectangular"); }
129 | for (int y = 0; y < images[x].length; y++) {
130 | outputStream.write(Ints.toByteArray(x));
131 | outputStream.write(Ints.toByteArray(y));
132 |
133 | writeToStream(images[x][y], outputStream);
134 | }
135 | }
136 | }
137 |
138 | public static ArrayImage[][] readMultiFromStream(InputStream inputStream) throws IOException {
139 | byte[] widthBytes = new byte[4];
140 | byte[] heightBytes = new byte[4];
141 | inputStream.read(widthBytes, 0, 4);
142 | inputStream.read(heightBytes, 0, 4);
143 |
144 | int width = Ints.fromByteArray(widthBytes);
145 | int height = Ints.fromByteArray(heightBytes);
146 |
147 | ArrayImage[][] images = new ArrayImage[width][height];
148 |
149 | byte[] xBytes = new byte[4];
150 | byte[] yBytes = new byte[4];
151 | for (int x = 0; x < width; x++) {
152 | for (int y = 0; y < height; y++) {
153 | inputStream.read(xBytes, 0, 4);
154 | inputStream.read(yBytes, 0, 4);
155 |
156 | int actualX = Ints.fromByteArray(xBytes);
157 | int actualY = Ints.fromByteArray(yBytes);
158 |
159 | images[actualX][actualY] = readFromStream(inputStream);
160 | }
161 | }
162 |
163 | return images;
164 | }
165 |
166 | }
167 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/CommandHandler.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager;
2 |
3 | import org.bukkit.command.Command;
4 | import org.bukkit.command.CommandExecutor;
5 | import org.bukkit.command.CommandSender;
6 | import org.bukkit.command.TabCompleter;
7 |
8 | import java.util.List;
9 |
10 | class CommandHandler implements CommandExecutor, TabCompleter {
11 |
12 | @Override
13 | public boolean onCommand(CommandSender sender, Command command, String s, String[] args) {
14 | if (args.length == 0) {
15 | if (sender.hasPermission("mapmanager.reload")) {
16 | sender.sendMessage("§7/mapmanager reload");
17 | sender.sendMessage("§eReload the configuration");
18 | }
19 | return true;
20 | }
21 | if ("reload".equalsIgnoreCase(args[0])) {
22 | if (!sender.hasPermission("mapmanager.reload")) {
23 | sender.sendMessage("§cNo permission");
24 | return false;
25 | }
26 | sender.sendMessage("§7Reloading...");
27 | MapManagerPlugin.instance.reload();
28 | sender.sendMessage("§aConfiguration reloaded.");
29 | return true;
30 | }
31 | // if ("test".equalsIgnoreCase(args[0])) {
32 | // try {
33 | // BufferedImage bufferedImage = ImageIO.read(new URL("https://i.imgur.com/iJU3GUq.png"));
34 | // MapManager mapManager = ((MapManagerPlugin) Bukkit.getPluginManager().getPlugin("MapManager")).getMapManager();
35 | // MapWrapper wrapper = mapManager.wrapImage(bufferedImage);
36 | // MapController controller = wrapper.getController();
37 | //
38 | // controller.addViewer((Player) sender);
39 | // controller.sendContent((Player) sender);
40 | //
41 | // controller.showInHand((Player) sender,true);
42 | // } catch (Exception e) {
43 | // e.printStackTrace();
44 | // }
45 | // }
46 | return false;
47 | }
48 |
49 | @Override
50 | public List onTabComplete(CommandSender commandSender, Command command, String s, String[] strings) {
51 | return null;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/DefaultMapManager.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager;
2 |
3 | import org.bukkit.OfflinePlayer;
4 | import org.bukkit.entity.Player;
5 | import org.inventivetalent.mapmanager.manager.MapManager;
6 | import org.inventivetalent.mapmanager.wrapper.MapWrapper;
7 |
8 | import java.awt.image.BufferedImage;
9 | import java.util.HashSet;
10 | import java.util.List;
11 | import java.util.Set;
12 | import java.util.concurrent.CopyOnWriteArrayList;
13 |
14 | class DefaultMapManager implements MapManager {
15 |
16 | protected final Set OCCUPIED_IDS = new HashSet<>();
17 | private final List MANAGED_MAPS = new CopyOnWriteArrayList<>();
18 |
19 | @Override
20 | public MapWrapper wrapImage(BufferedImage image) {
21 | return wrapImage(new ArrayImage(image));
22 | }
23 |
24 | @Override
25 | public MapWrapper wrapImage(ArrayImage image) {
26 | if (Options.CHECK_DUPLICATES) {
27 | for (int i = 0; i < MANAGED_MAPS.size(); i++) {
28 | MapWrapper wrapper = MANAGED_MAPS.get(i);
29 | if (image.equals(wrapper.getContent())) { return wrapper; }
30 | }
31 | }
32 | return wrapNewImage(image);
33 | }
34 |
35 | @Override
36 | public MapWrapper wrapMultiImage(BufferedImage image, int rows, int columns) {
37 | //Don't add the wrapper to the MANAGED_MAPS, since we're already registering all the single wrapped maps
38 | return new MultiMapWrapper(image, rows, columns);
39 | }
40 |
41 | @Override
42 | public MapWrapper wrapMultiImage(ArrayImage image, int rows, int columns) {
43 | //Don't add the wrapper to the MANAGED_MAPS, since we're already registering all the single wrapped maps
44 | return new MultiMapWrapper(image, rows, columns);
45 | }
46 |
47 | @Override
48 | public MapWrapper wrapMultiImage(ArrayImage[][] images) {
49 | return new MultiMapWrapper(images);
50 | }
51 |
52 | public MapWrapper wrapNewImage(ArrayImage image) {
53 | MapWrapper wrapper = new DefaultMapWrapper(image);
54 | MANAGED_MAPS.add(wrapper);
55 | return wrapper;
56 | }
57 |
58 | @Override
59 | public void unwrapImage(MapWrapper wrapper) {
60 | if (wrapper instanceof DefaultMapWrapper) {
61 | for (int s : ((DefaultMapWrapper) wrapper).viewers.values()) {
62 | MapSender.cancelIDs(new int[] { s });
63 | }
64 | }
65 | wrapper.getController().clearViewers();
66 | MANAGED_MAPS.remove(wrapper);
67 | if (wrapper instanceof MultiMapWrapper) {
68 | ((MultiMapWrapper) wrapper).unwrap();
69 | }
70 | }
71 |
72 | @Override
73 | public Set getMapsVisibleTo(OfflinePlayer player) {
74 | Set visible = new HashSet<>();
75 | for (MapWrapper wrapper : MANAGED_MAPS) {
76 | if (wrapper.getController().isViewing(player)) {
77 | visible.add(wrapper);
78 | }
79 | }
80 | return visible;
81 | }
82 |
83 | @Override
84 | public MapWrapper getWrapperForId(OfflinePlayer player, int id) {
85 | for (MapWrapper wrapper : getMapsVisibleTo(player)) {
86 | if (wrapper.getController().getMapId(player) == id) { return wrapper; }
87 | }
88 | return null;
89 | }
90 |
91 | @Override
92 | public void registerOccupiedID(int id) {
93 | if (!OCCUPIED_IDS.contains(id)) { OCCUPIED_IDS.add(id); }
94 | }
95 |
96 | @Override
97 | public void unregisterOccupiedID(int id) {
98 | OCCUPIED_IDS.remove(id);
99 | }
100 |
101 | @Override
102 | public Set getOccupiedIdsFor(OfflinePlayer player) {
103 | Set ids = new HashSet<>();
104 | for (MapWrapper wrapper : MANAGED_MAPS) {
105 | int s;
106 | if ((s = wrapper.getController().getMapId(player)) >= 0) {
107 | ids.add(s);
108 | }
109 | }
110 | return ids;
111 | }
112 |
113 | @Override
114 | public boolean isIdUsedBy(OfflinePlayer player, int id) {
115 | return id > Options.FORCED_OFFSET && getOccupiedIdsFor(player).contains(id);
116 | }
117 |
118 | @Override
119 | public int getNextFreeIdFor(Player player) throws MapLimitExceededException {
120 | Set occupied = getOccupiedIdsFor(player);
121 | //Add the 'default' occupied IDs
122 | occupied.addAll(OCCUPIED_IDS);
123 |
124 | int largest = Options.FORCED_OFFSET;
125 | for (Integer s : occupied) {
126 | if (s > largest) { largest = s; }
127 | }
128 |
129 | //Simply increase the maximum id if it's still small enough
130 | if (largest + 1 < Integer.MAX_VALUE) { return (int) (largest + 1); }
131 |
132 | //Otherwise iterate through all options until there is an unused id
133 | for (int s = 0; s < Integer.MAX_VALUE; s++) {
134 | if (!occupied.contains(s)) {
135 | return s;
136 | }
137 | }
138 |
139 | //If we end up here, this player has no more free ids. Let's hope nobody uses this many Maps.
140 | throw new MapLimitExceededException("'" + player + "' reached the maximum amount of available Map-IDs");
141 | }
142 |
143 | @Override
144 | public void clearAllMapsFor(OfflinePlayer player) {
145 | for (MapWrapper wrapper : getMapsVisibleTo(player)) {
146 | wrapper.getController().removeViewer(player);
147 | }
148 | }
149 |
150 | public MapWrapper getDuplicate(ArrayImage image) {
151 | for (int i = 0; i < MANAGED_MAPS.size(); i++) {
152 | MapWrapper wrapper = MANAGED_MAPS.get(i);
153 | if (image.equals(wrapper.getContent())) { return wrapper; }
154 | }
155 | return null;
156 | }
157 |
158 | }
159 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/DefaultMapWrapper.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager;
2 |
3 | import org.bukkit.Bukkit;
4 | import org.bukkit.GameMode;
5 | import org.bukkit.Material;
6 | import org.bukkit.OfflinePlayer;
7 | import org.bukkit.entity.ItemFrame;
8 | import org.bukkit.entity.Player;
9 | import org.bukkit.inventory.ItemStack;
10 | import org.bukkit.inventory.meta.ItemMeta;
11 | import org.bukkit.inventory.meta.MapMeta;
12 | import org.bukkit.metadata.FixedMetadataValue;
13 | import org.inventivetalent.mapmanager.controller.MapController;
14 | import org.inventivetalent.mapmanager.event.MapContentUpdateEvent;
15 | import org.inventivetalent.mapmanager.manager.MapManager;
16 | import org.inventivetalent.mapmanager.wrapper.MapWrapper;
17 | import org.inventivetalent.reflection.minecraft.Minecraft;
18 | import org.inventivetalent.reflection.minecraft.MinecraftVersion;
19 | import org.inventivetalent.reflection.resolver.ConstructorResolver;
20 | import org.inventivetalent.reflection.resolver.FieldResolver;
21 | import org.inventivetalent.reflection.resolver.MethodResolver;
22 | import org.inventivetalent.reflection.resolver.ResolverQuery;
23 |
24 | import java.lang.reflect.Constructor;
25 | import java.lang.reflect.Method;
26 | import java.util.*;
27 |
28 | class DefaultMapWrapper implements MapWrapper {
29 |
30 | static int ID_COUNTER = 1;
31 | protected final int id = ID_COUNTER++;
32 |
33 | protected ArrayImage content;
34 | protected final Map viewers = new HashMap<>();
35 |
36 | private static Class> Entity;
37 | private static Class> DataWatcher;
38 | private static Class> PacketPlayOutEntityMetadata;
39 |
40 | private static FieldResolver PacketEntityMetadataFieldResolver;
41 | private static FieldResolver EntityHumanFieldResolver;
42 | private static FieldResolver ContainerFieldResolver;
43 | private static ConstructorResolver WatchableObjectConstructorResolver;
44 | private static ConstructorResolver PacketPlayOutSlotConstructorResolver;
45 | private static MethodResolver CraftItemStackMethodResolver;
46 | private static MethodResolver ItemStackMethodResolver;
47 | private static MethodResolver NBTTagMethodResolver;
48 |
49 | //1.9
50 | private static FieldResolver DataWatcherRegistryFieldResolver;
51 | private static ConstructorResolver DataWatcherItemConstructorResolver;
52 | private static ConstructorResolver DataWatcherObjectConstructorResolver;
53 | private static FieldResolver EntityItemFrameFieldResolver;
54 |
55 | static Material MAP_MATERIAL;
56 |
57 | static {
58 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_13_R1)) {
59 | MAP_MATERIAL = Material.FILLED_MAP;
60 | } else {
61 | MAP_MATERIAL = Material.MAP;
62 | }
63 | }
64 |
65 | protected MapController controller = new MapController() {
66 | @Override
67 | public void addViewer(Player player) {
68 | if (!isViewing(player)) {
69 | viewers.put(player.getUniqueId(), MapManagerPlugin.instance.getMapManager().getNextFreeIdFor(player));
70 | }
71 | }
72 |
73 | @Override
74 | public void removeViewer(OfflinePlayer player) {
75 | viewers.remove(player.getUniqueId());
76 | }
77 |
78 | @Override
79 | public void clearViewers() {
80 | Set uuids = new HashSet<>(viewers.keySet());
81 | for (UUID uuid : uuids) {
82 | viewers.remove(uuid);
83 | }
84 | }
85 |
86 | @Override
87 | public boolean isViewing(OfflinePlayer player) {
88 | if (player == null) {return false;}
89 | return viewers.containsKey(player.getUniqueId());
90 | }
91 |
92 | @Override
93 | public int getMapId(OfflinePlayer player) {
94 | if (isViewing(player)) {
95 | return viewers.get(player.getUniqueId());
96 | }
97 | return -1;
98 | }
99 |
100 | @Override
101 | public void update(ArrayImage content) {
102 | MapContentUpdateEvent event = new MapContentUpdateEvent(DefaultMapWrapper.this, content);
103 | Bukkit.getPluginManager().callEvent(event);
104 |
105 | if (event.getContent() != null) {
106 | if (MapManager.Options.CHECK_DUPLICATES) {
107 | MapWrapper duplicate = ((DefaultMapManager) ((MapManagerPlugin) Bukkit.getPluginManager().getPlugin("MapManager")).getMapManager()).getDuplicate(event.getContent());
108 | if (duplicate != null) {
109 | DefaultMapWrapper.this.content = duplicate.getContent();
110 | return;
111 | }
112 | }
113 | DefaultMapWrapper.this.content = event.getContent();
114 | }
115 |
116 | if (event.isSendContent()) {
117 | for (UUID id : viewers.keySet()) {
118 | sendContent(Bukkit.getPlayer(id));
119 | }
120 | }
121 | }
122 |
123 | @Override
124 | public ArrayImage getContent() {
125 | return content;
126 | }
127 |
128 | @Override
129 | public void sendContent(Player player) {
130 | sendContent(player, false);
131 | }
132 |
133 | @Override
134 | public void sendContent(Player player, boolean withoutQueue) {
135 | if (!isViewing(player)) {return;}
136 | int id = getMapId(player);
137 | if (withoutQueue && MapManager.Options.Sender.ALLOW_QUEUE_BYPASS) {
138 | MapSender.sendMap(id, DefaultMapWrapper.this.content, player);
139 | } else {
140 | MapSender.addToQueue(id, DefaultMapWrapper.this.content, player);
141 | }
142 | }
143 |
144 | @Override
145 | public void showInInventory(Player player, int slot, boolean force) {
146 | if (!isViewing(player)) {
147 | return;
148 | }
149 |
150 | if (player.getGameMode() == GameMode.CREATIVE) {
151 | //Clients in creative mode will send a 'PacketPlayInCreativeSlot' which tells the server there's a new item in the inventory and creates a new map
152 | if (!force) {
153 | return;
154 | }
155 | }
156 |
157 | //Adjust the slot ID
158 | if (slot < 9) {slot += 36;} else if (slot > 35 && slot != 45) {slot = 8 - (slot - 36);}
159 |
160 | try {
161 | if (PacketPlayOutSlotConstructorResolver == null) {
162 | PacketPlayOutSlotConstructorResolver = new ConstructorResolver(MapManagerPlugin.nmsClassResolver.resolve("PacketPlayOutSetSlot", "network.protocol.game.PacketPlayOutSetSlot"));
163 | }
164 | if (EntityHumanFieldResolver == null) {
165 | EntityHumanFieldResolver = new FieldResolver(MapManagerPlugin.nmsClassResolver.resolve("EntityHuman", "world.entity.player.EntityHuman"));
166 | }
167 | if (ContainerFieldResolver == null) {
168 | ContainerFieldResolver = new FieldResolver(MapManagerPlugin.nmsClassResolver.resolve("Container", "world.inventory.Container"));
169 | }
170 | if (CraftItemStackMethodResolver == null) {
171 | CraftItemStackMethodResolver = new MethodResolver(MapManagerPlugin.obcClassResolver.resolve("inventory.CraftItemStack"));
172 | }
173 |
174 | Object entityPlayer = Minecraft.getHandle(player);
175 | Object defaultContainer = EntityHumanFieldResolver.resolveAccessor("defaultContainer").get(entityPlayer);
176 | Object windowId = ContainerFieldResolver.resolveAccessor("windowId").get(defaultContainer);
177 |
178 | //Create the ItemStack with the player's map ID
179 | ItemStack itemStack;
180 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_13_R1)) {
181 | itemStack = new ItemStack(MAP_MATERIAL, 1);
182 | } else {
183 | itemStack = new ItemStack(MAP_MATERIAL, 1, (short) getMapId(player));
184 | }
185 | Object craftItemStack = createCraftItemStack(itemStack, getMapId(player));
186 |
187 | Object setSlot = PacketPlayOutSlotConstructorResolver.resolve(new Class[]{
188 | int.class,
189 | int.class,
190 | MapManagerPlugin.nmsClassResolver.resolve("world.item.ItemStack")
191 | }).newInstance(windowId, slot, craftItemStack);
192 |
193 | //Send the packet
194 | sendPacket(player, setSlot);
195 | } catch (Exception e) {
196 | throw new RuntimeException(e);
197 | }
198 | }
199 |
200 | @Override
201 | public void showInInventory(Player player, int slot) {
202 | showInInventory(player, slot, false);
203 | }
204 |
205 | @Override
206 | public void showInHand(Player player, boolean force) {
207 | if (player.getItemInHand() == null || player.getItemInHand().getType() != MAP_MATERIAL) {
208 | if (!force) {//Player is not holding a map
209 | return;
210 | }
211 | }
212 | showInInventory(player, player.getInventory().getHeldItemSlot(), force);
213 | }
214 |
215 | @Override
216 | public void showInHand(Player player) {
217 | showInHand(player, false);
218 | }
219 |
220 | @Override
221 | public void showInFrame(Player player, ItemFrame frame, boolean force) {
222 | if (frame.getItem() == null || frame.getItem().getType() != MAP_MATERIAL) {
223 | if (!force) {//There's no map in the item frame: don't do anything
224 | return;
225 | }
226 | }
227 | showInFrame(player, frame.getEntityId());
228 | }
229 |
230 | @Override
231 | public void showInFrame(Player player, int entityId, String debugInfo) {
232 | if (!isViewing(player)) {
233 | return;
234 | }
235 | //Create the ItemStack with the player's map ID
236 | ItemStack itemStack = new ItemStack(MAP_MATERIAL, 1);
237 | if (debugInfo != null) {
238 | //Add the debug info to the display
239 | ItemMeta itemMeta = itemStack.getItemMeta();
240 | itemMeta.setDisplayName(debugInfo);
241 | itemStack.setItemMeta(itemMeta);
242 | }
243 |
244 | Bukkit.getScheduler().runTask(MapManagerPlugin.instance, () -> {
245 | ItemFrame itemFrame = MapManagerPlugin.getItemFrameById(player.getWorld(), entityId);
246 | if (itemFrame != null) {
247 | //Add a reference to this MapWrapper (can be used in MapWrapper#getWrapperForId)
248 | itemFrame.removeMetadata("MAP_WRAPPER_REF", MapManagerPlugin.instance);
249 | itemFrame.setMetadata("MAP_WRAPPER_REF", new FixedMetadataValue(MapManagerPlugin.instance, DefaultMapWrapper.this));
250 | }
251 |
252 | sendItemFramePacket(player, entityId, itemStack, getMapId(player));
253 | });
254 | }
255 |
256 | @Override
257 | public void showInFrame(Player player, int entityId) {
258 | showInFrame(player, entityId, null);
259 | }
260 |
261 | @Override
262 | public void showInFrame(Player player, ItemFrame frame) {
263 | showInFrame(player, frame, false);
264 | }
265 |
266 | @Override
267 | public void clearFrame(Player player, int entityId) {
268 | sendItemFramePacket(player, entityId, null, -1);
269 | Bukkit.getScheduler().runTask(MapManagerPlugin.instance, () -> {
270 | ItemFrame itemFrame = MapManagerPlugin.getItemFrameById(player.getWorld(), entityId);
271 | if (itemFrame != null) {
272 | //Remove the reference
273 | itemFrame.removeMetadata("MAP_WRAPPER_REF", MapManagerPlugin.instance);
274 | }
275 | });
276 | }
277 |
278 | @Override
279 | public void clearFrame(Player player, ItemFrame frame) {
280 | clearFrame(player, frame.getEntityId());
281 | }
282 |
283 | };
284 |
285 | DefaultMapWrapper(ArrayImage content) {
286 | this.content = content;
287 | }
288 |
289 | public MapController getController() {
290 | return controller;
291 | }
292 |
293 | @Override
294 | public ArrayImage getContent() {
295 | return content;
296 | }
297 |
298 | protected void sendPacket(Player player, Object packet) {
299 | try {
300 | MapSender.sendPacket(packet, player);
301 | } catch (Exception e) {
302 | throw new RuntimeException(e);
303 | }
304 | }
305 |
306 | Object createCraftItemStack(ItemStack itemStack, int mapId) throws ReflectiveOperationException {
307 | Object craftItemStack = CraftItemStackMethodResolver.resolve(new ResolverQuery("asNMSCopy", ItemStack.class)).invoke(null, itemStack);
308 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_16_R1)) {
309 | MapMeta meta = (MapMeta) itemStack.getItemMeta();
310 | meta.setMapId(mapId); //TODO
311 | itemStack.setItemMeta(meta);
312 | craftItemStack = CraftItemStackMethodResolver.resolve(new ResolverQuery("asNMSCopy", ItemStack.class)).invoke(null, itemStack);
313 | } else if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_13_R1)) {
314 | if (ItemStackMethodResolver == null) {
315 | ItemStackMethodResolver = new MethodResolver(MapManagerPlugin.nmsClassResolver.resolve("ItemStack", "world.item.ItemStack"));
316 | }
317 | if (NBTTagMethodResolver == null) {
318 | NBTTagMethodResolver = new MethodResolver(MapManagerPlugin.nmsClassResolver.resolve("NBTTagCompound", "nbt.NBTTagCompound"));
319 | }
320 | if (itemStack != null && craftItemStack != null && mapId >= 0) {
321 | Object nbtTag = ItemStackMethodResolver.resolveSignature("NBTTagCompound getOrCreateTag()", "NBTTagCompound u()", "NBTTagCompound getTag()").invoke(craftItemStack);
322 | Method setInt = NBTTagMethodResolver.resolveSignature("void setInt(String, int)", "void a(String, int)");
323 | setInt.invoke(nbtTag, "map", mapId);
324 | }
325 | }
326 |
327 | return craftItemStack;
328 | }
329 |
330 | public void sendItemFramePacket(Player player, int entityId, ItemStack itemStack, int mapId) {
331 | try {
332 | if (Entity == null) {
333 | Entity = MapManagerPlugin.nmsClassResolver.resolve("world.entity.Entity", "Entity");
334 | }
335 | if (DataWatcher == null) {
336 | DataWatcher = MapManagerPlugin.nmsClassResolver.resolve("network.syncher.DataWatcher", "network.syncer.DataWatcher", "DataWatcher");
337 | }
338 | if (PacketPlayOutEntityMetadata == null) {
339 | PacketPlayOutEntityMetadata = MapManagerPlugin.nmsClassResolver
340 | .resolve("PacketPlayOutEntityMetadata", "network.protocol.game.PacketPlayOutEntityMetadata");
341 | }
342 | if (PacketEntityMetadataFieldResolver == null) {
343 | PacketEntityMetadataFieldResolver = new FieldResolver(PacketPlayOutEntityMetadata);
344 | }
345 | if (WatchableObjectConstructorResolver == null) {
346 | WatchableObjectConstructorResolver = new ConstructorResolver(MapManagerPlugin.nmsClassResolver.resolve("network.syncher.DataWatcher$Item", "network.syncer.DataWatcher$Item", "WatchableObject", "DataWatcher$WatchableObject", "DataWatcher$Item"/*1.9*/));
347 | }
348 | if (CraftItemStackMethodResolver == null) {
349 | CraftItemStackMethodResolver = new MethodResolver(MapManagerPlugin.obcClassResolver.resolve("inventory.CraftItemStack"));
350 | }
351 |
352 | //1.9
353 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_9_R1)) {
354 | if (DataWatcherRegistryFieldResolver == null) {
355 | DataWatcherRegistryFieldResolver = new FieldResolver(MapManagerPlugin.nmsClassResolver.resolve("network.syncher.DataWatcherRegistry", "network.syncer.DataWatcherRegistry", "DataWatcherRegistry"));
356 | }
357 | if (DataWatcherItemConstructorResolver == null) {
358 | DataWatcherItemConstructorResolver = new ConstructorResolver(MapManagerPlugin.nmsClassResolver.resolve("network.syncher.DataWatcher$Item", "network.syncer.DataWatcher$Item", "DataWatcher$Item"));
359 | }
360 | if (DataWatcherObjectConstructorResolver == null) {
361 | DataWatcherObjectConstructorResolver = new ConstructorResolver(MapManagerPlugin.nmsClassResolver.resolve("network.syncher.DataWatcherObject", "network.syncer.DataWatcherObject", "DataWatcherObject"));
362 | }
363 | if (EntityItemFrameFieldResolver == null) {
364 | EntityItemFrameFieldResolver = new FieldResolver(MapManagerPlugin.nmsClassResolver.resolve("EntityItemFrame", "world.entity.decoration.EntityItemFrame"));
365 | }
366 | }
367 |
368 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_13_R1)) {
369 | if (ItemStackMethodResolver == null) {
370 | ItemStackMethodResolver = new MethodResolver(MapManagerPlugin.nmsClassResolver.resolve("ItemStack", "world.item.ItemStack"));
371 | }
372 | if (NBTTagMethodResolver == null) {
373 | NBTTagMethodResolver = new MethodResolver(MapManagerPlugin.nmsClassResolver.resolve("NBTTagCompound", "nbt.NBTTagCompound"));
374 | }
375 | }
376 |
377 |
378 | Object meta;
379 | try {
380 | Constructor> noArgConstructor = PacketPlayOutEntityMetadata.getConstructor();
381 | meta = noArgConstructor.newInstance();
382 | } catch (ReflectiveOperationException e) {
383 | Object dummyDataWatcher = DataWatcher.getConstructor(Entity).newInstance((Object) null);
384 | meta = PacketPlayOutEntityMetadata.getConstructor(int.class, DataWatcher, boolean.class)
385 | .newInstance(entityId, dummyDataWatcher, true);
386 | }
387 |
388 | //Set the Entity ID of the frame
389 | PacketEntityMetadataFieldResolver.resolveAccessor("a").set(meta, entityId);
390 |
391 | Object craftItemStack = createCraftItemStack(itemStack, mapId);
392 |
393 | List list = new ArrayList();
394 |
395 | //<= 1.8
396 | if (MinecraftVersion.VERSION.olderThan(Minecraft.Version.v1_9_R1)) {
397 | // 0 = Byte
398 | // 1 = Short
399 | // 2 = Int
400 | // 3 = Float
401 | // 4 = String
402 | // 5 = ItemStack
403 | // 6 = BlockPosition / ChunkCoordinates
404 | // 7 = Vector3f / Vector(?)
405 | list.add(WatchableObjectConstructorResolver.resolve(new Class[]{
406 | int.class,
407 | int.class,
408 | Object.class
409 | }).newInstance(5, 8, craftItemStack));
410 |
411 | //<= 1.7
412 | if (MinecraftVersion.VERSION.olderThan(Minecraft.Version.v1_8_R1)) {
413 | list.add(WatchableObjectConstructorResolver.resolve(new Class[]{
414 | int.class,
415 | int.class,
416 | Object.class
417 | }).newInstance(5, 2, craftItemStack));
418 | }
419 | } else {
420 | Object dataWatcherObject;
421 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_17_R1)) {
422 | if (MinecraftVersion.VERSION.equal(Minecraft.Version.v1_18_R1)) {
423 | dataWatcherObject = EntityItemFrameFieldResolver.resolveAccessor("ITEM", "ap").get(null);
424 | } else {
425 | dataWatcherObject = EntityItemFrameFieldResolver.resolveAccessor("ITEM", "ao").get(null);
426 | }
427 | } else if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_13_R1)) {
428 | dataWatcherObject = EntityItemFrameFieldResolver.resolveAccessor("ITEM", "e").get(null);
429 | } else {
430 | dataWatcherObject = EntityItemFrameFieldResolver.resolveAccessor("c").get(null);
431 | }
432 |
433 | Constructor constructor = DataWatcherItemConstructorResolver.resolveFirstConstructor();
434 | Object dataWatcherItem;
435 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_11_R1)) {
436 | // For some reason, it doesn't like Optionals anymore in 1.11...
437 | dataWatcherItem = constructor.newInstance(dataWatcherObject, craftItemStack);
438 | } else {
439 | dataWatcherItem = constructor.newInstance(dataWatcherObject, com.google.common.base.Optional.fromNullable(craftItemStack));
440 | }
441 |
442 | list.add(dataWatcherItem);
443 | }
444 |
445 | PacketEntityMetadataFieldResolver.resolveAccessor("b").set(meta, list);
446 |
447 |
448 | //Send the completed packet
449 | sendPacket(player, meta);
450 | } catch (Exception e) {
451 | throw new RuntimeException(e);
452 | }
453 | }
454 |
455 | @Override
456 | public boolean equals(Object o) {
457 | if (this == o) {return true;}
458 | if (o == null || getClass() != o.getClass()) {return false;}
459 |
460 | DefaultMapWrapper that = (DefaultMapWrapper) o;
461 |
462 | return id == that.id;
463 |
464 | }
465 |
466 | @Override
467 | public int hashCode() {
468 | return id;
469 | }
470 |
471 | }
472 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/MapLimitExceededException.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager;
2 |
3 | /**
4 | * Exception thrown if no more map IDs are available
5 | */
6 | public class MapLimitExceededException extends RuntimeException {
7 |
8 | public MapLimitExceededException() {
9 | }
10 |
11 | public MapLimitExceededException(String message) {
12 | super(message);
13 | }
14 |
15 | public MapLimitExceededException(String message, Throwable cause) {
16 | super(message, cause);
17 | }
18 |
19 | public MapLimitExceededException(Throwable cause) {
20 | super(cause);
21 | }
22 |
23 | public MapLimitExceededException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
24 | super(message, cause, enableSuppression, writableStackTrace);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/MapListener.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager;
2 |
3 | import org.bukkit.event.EventHandler;
4 | import org.bukkit.event.Listener;
5 | import org.bukkit.event.player.PlayerJoinEvent;
6 | import org.bukkit.event.player.PlayerQuitEvent;
7 | import org.bukkit.event.server.MapInitializeEvent;
8 | import org.inventivetalent.mapmanager.manager.MapManager;
9 |
10 | class MapListener implements Listener {
11 |
12 | private MapManagerPlugin plugin;
13 |
14 | public MapListener(MapManagerPlugin plugin) {
15 | this.plugin = plugin;
16 | }
17 |
18 | @EventHandler
19 | public void onJoin(PlayerJoinEvent event) {
20 | }
21 |
22 | @EventHandler
23 | public void onQuit(PlayerQuitEvent event) {
24 | plugin.getMapManager().clearAllMapsFor(event.getPlayer());
25 | }
26 |
27 | @EventHandler
28 | public void onMapInitialize(MapInitializeEvent event) {
29 | if (MapManager.Options.ALLOW_VANILLA) {
30 | int id = event.getMap().getId();
31 | if (id > MapManager.Options.FORCED_OFFSET) {
32 | if (MapManager.Options.FORCED_OFFSET > 0) {
33 | plugin.getLogger().warning("The configured forcedOffset has been exceeded. Increase the number in the config to keep future IDs from being overwritten.");
34 | }
35 | plugin.getLogger().finer("Adding new Map #" + id + " to occupied IDs.");
36 | plugin.getMapManager().registerOccupiedID(id);
37 | }
38 | }
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/MapManagerPlugin.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager;
2 |
3 | import org.bukkit.Bukkit;
4 | import org.bukkit.World;
5 | import org.bukkit.command.PluginCommand;
6 | import org.bukkit.configuration.file.FileConfiguration;
7 | import org.bukkit.entity.Entity;
8 | import org.bukkit.entity.ItemFrame;
9 | import org.bukkit.map.MapView;
10 | import org.bukkit.plugin.java.JavaPlugin;
11 | import org.inventivetalent.mapmanager.manager.MapManager;
12 | import org.inventivetalent.mapmanager.metrics.Metrics;
13 | import org.inventivetalent.reflection.minecraft.Minecraft;
14 | import org.inventivetalent.reflection.minecraft.MinecraftVersion;
15 | import org.inventivetalent.reflection.resolver.FieldResolver;
16 | import org.inventivetalent.reflection.resolver.MethodResolver;
17 | import org.inventivetalent.reflection.resolver.ResolverQuery;
18 | import org.inventivetalent.reflection.resolver.minecraft.NMSClassResolver;
19 | import org.inventivetalent.reflection.resolver.minecraft.OBCClassResolver;
20 | import org.inventivetalent.update.spiget.SpigetUpdate;
21 | import org.inventivetalent.update.spiget.UpdateCallback;
22 | import org.inventivetalent.update.spiget.comparator.VersionComparator;
23 |
24 | import java.util.HashSet;
25 | import java.util.Map;
26 | import java.util.Set;
27 | import java.util.logging.Level;
28 |
29 | import static org.inventivetalent.mapmanager.manager.MapManager.Options.*;
30 |
31 | /**
32 | * MapManager-Plugin
33 | *
34 | * use Bukkit.getPluginManager().getPlugin("MapManager")
to access the plugin instance or Bukkit.getPluginManager().getPlugin("MapManager").getMapManager()
to access the {@link MapManager} instance
35 | */
36 | public class MapManagerPlugin extends JavaPlugin {
37 |
38 | protected static MapManagerPlugin instance;
39 |
40 | protected MapManager mapManagerInstance;
41 |
42 | private PacketListener packetListener;
43 | protected MapListener mapListener;
44 |
45 | protected static NMSClassResolver nmsClassResolver = new NMSClassResolver();
46 | protected static OBCClassResolver obcClassResolver = new OBCClassResolver();
47 |
48 | private static FieldResolver CraftWorldFieldResolver;
49 | private static FieldResolver WorldFieldResolver;
50 | private static FieldResolver WorldServerFieldResolver;
51 | private static MethodResolver IntHashMapMethodResolver;
52 | private static MethodResolver EntityMethodResolver;
53 | private static MethodResolver WorldServerMethodResolver;
54 |
55 | public MapManagerPlugin() {
56 | instance = this;
57 | }
58 |
59 | @Override
60 | public void onEnable() {
61 | if (!Bukkit.getPluginManager().isPluginEnabled("PacketListenerApi")) {
62 | getLogger().severe("****************************************");
63 | getLogger().severe("This plugin depends on PacketListenerApi");
64 | getLogger().severe("Download it here: https://r.spiget.org/2930");
65 | getLogger().severe("****************************************");
66 | Bukkit.getPluginManager().disablePlugin(this);
67 | return;
68 | }
69 |
70 | packetListener = new PacketListener(this);
71 | Bukkit.getPluginManager().registerEvents(mapListener = new MapListener(this), this);
72 |
73 | mapManagerInstance = new DefaultMapManager();
74 |
75 | saveDefaultConfig();
76 | reload();
77 |
78 | PluginCommand command = getCommand("mapmanager");
79 | CommandHandler commandHandler = new CommandHandler();
80 | command.setExecutor(commandHandler);
81 | command.setTabCompleter(commandHandler);
82 |
83 | if (MapManager.Options.ALLOW_VANILLA) {
84 | getLogger().info("Vanilla Maps are allowed. Trying to discover occupied Map IDs...");
85 |
86 | Set occupied = new HashSet<>();
87 | for (int s = 0; s < Short.MAX_VALUE; s++) { // Integer.max is just too much
88 | try {
89 | MapView view = Bukkit.getMap(s);
90 | if (view != null) {
91 | occupied.add(s);
92 | }
93 | } catch (Exception e) {
94 | if (!e.getMessage().toLowerCase().contains("invalid map dimension")) {
95 | getLogger().log(Level.WARNING, e.getMessage(), e);
96 | }
97 | }
98 | }
99 | getLogger().info("Found " + occupied.size() + " occupied IDs.");
100 |
101 | for (int s : occupied) {
102 | getMapManager().registerOccupiedID(s);
103 | }
104 | getLogger().fine("These IDs will not be used: " + occupied);
105 | }
106 |
107 | new Metrics(this);
108 |
109 | SpigetUpdate updater = new SpigetUpdate(this, 19198);
110 | updater.setUserAgent("MapManager/" + getDescription().getVersion()).setVersionComparator(VersionComparator.SEM_VER_SNAPSHOT);
111 | updater.checkForUpdate(new UpdateCallback() {
112 | @Override
113 | public void updateAvailable(String s, String s1, boolean b) {
114 | getLogger().info("A new version is available: https://r.spiget.org/19198");
115 | }
116 |
117 | @Override
118 | public void upToDate() {
119 | getLogger().info("Plugin is up-to-date");
120 | }
121 | });
122 | }
123 |
124 | @Override
125 | public void onDisable() {
126 | this.packetListener.disable();
127 | }
128 |
129 | void reload() {
130 | FileConfiguration config = getConfig();
131 |
132 | ALLOW_VANILLA = config.getBoolean("allowVanilla", ALLOW_VANILLA);
133 | FORCED_OFFSET = config.getInt("forcedOffset", FORCED_OFFSET);
134 | CHECK_DUPLICATES = config.getBoolean("checkDuplicates", CHECK_DUPLICATES);
135 | CACHE_DATA = getConfig().getBoolean("cacheData", CACHE_DATA);
136 | Sender.DELAY = getConfig().getInt("sender.delay", Sender.DELAY);
137 | Sender.AMOUNT = getConfig().getInt("sender.amount", Sender.AMOUNT);
138 | Sender.ALLOW_QUEUE_BYPASS = getConfig().getBoolean("sender.allowQueueBypass", Sender.ALLOW_QUEUE_BYPASS);
139 | }
140 |
141 | /**
142 | * @return The {@link MapManager} instance
143 | */
144 | public MapManager getMapManager() {
145 | if (mapManagerInstance == null) {throw new IllegalStateException("Manager not yet initialized");}
146 | return mapManagerInstance;
147 | }
148 |
149 | /**
150 | * Helper method to find an {@link ItemFrame} by its entity ID
151 | *
152 | * @param world {@link World} the frame is located in
153 | * @param entityId the frame's entity ID
154 | * @return the {@link ItemFrame} or null
155 | */
156 | public static ItemFrame getItemFrameById(World world, int entityId) {
157 | try {
158 | if (CraftWorldFieldResolver == null) {
159 | CraftWorldFieldResolver = new FieldResolver(MapManagerPlugin.obcClassResolver.resolve("CraftWorld"));
160 | }
161 | if (WorldFieldResolver == null) {
162 | WorldFieldResolver = new FieldResolver(MapManagerPlugin.nmsClassResolver.resolve("world.level.World"));
163 | }
164 | if (WorldServerFieldResolver == null) {
165 | WorldServerFieldResolver = new FieldResolver(MapManagerPlugin.nmsClassResolver.resolve("server.level.WorldServer"));
166 | }
167 | if (EntityMethodResolver == null) {
168 | EntityMethodResolver = new MethodResolver(MapManagerPlugin.nmsClassResolver.resolve("world.entity.Entity"));
169 | }
170 | if (WorldServerMethodResolver == null) {
171 | WorldServerMethodResolver = new MethodResolver(MapManagerPlugin.nmsClassResolver.resolve("server.level.WorldServer"));
172 | }
173 |
174 | Object nmsWorld = CraftWorldFieldResolver.resolveAccessor("world").get(world);
175 |
176 | Object entity;
177 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_18_R1)) {
178 | entity = world.getEntitiesByClass(ItemFrame.class).stream().filter(i -> i.getEntityId() == entityId).findFirst().orElse(null);
179 | if (entity != null) {
180 | entity = Minecraft.getHandle(entity);
181 | }
182 | } else if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_17_R1)) {
183 | // no more entitiesById in 1.17
184 | entity = WorldServerMethodResolver.resolve(new ResolverQuery("getEntity", int.class)).invoke(nmsWorld, entityId);
185 | } else {
186 | Object entitiesById;
187 | // NOTE: this check can be false, if the v1_14_R1 doesn't exist (stupid java), i.e. in old ReflectionHelper versions
188 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_8_R1)
189 | && MinecraftVersion.VERSION.olderThan(Minecraft.Version.v1_14_R1)) { /* seriously?! between 1.8 and 1.14 entitiesyId was moved to World */
190 | entitiesById = WorldFieldResolver.resolveAccessor("entitiesById").get(nmsWorld);
191 | } else {
192 | entitiesById = WorldServerFieldResolver.resolveAccessor("entitiesById").get(nmsWorld);
193 | }
194 |
195 | if (MinecraftVersion.VERSION.olderThan(Minecraft.Version.v1_14_R1)) {// < 1.14 uses IntHashMap
196 | if (IntHashMapMethodResolver == null) {
197 | IntHashMapMethodResolver = new MethodResolver(MapManagerPlugin.nmsClassResolver.resolve("IntHashMap"));
198 | }
199 |
200 | entity = IntHashMapMethodResolver.resolve(new ResolverQuery("get", int.class)).invoke(entitiesById, entityId);
201 | } else {// > 1.14 uses Int2ObjectMap which implements Map
202 | entity = ((Map) entitiesById).get(entityId);
203 | }
204 | }
205 |
206 | if (entity == null) {
207 | return null;
208 | }
209 | Entity bukkitEntity = (Entity) EntityMethodResolver.resolve("getBukkitEntity").invoke(entity);
210 | if (bukkitEntity instanceof ItemFrame) {
211 | return (ItemFrame) bukkitEntity;
212 | }
213 |
214 | // for (ItemFrame itemFrame : world.getEntitiesByClass(ItemFrame.class)) {
215 | // if (itemFrame.getEntityId() == entityId) {
216 | // return itemFrame;
217 | // }
218 | // }
219 | // return null;
220 | } catch (Exception e) {
221 | e.printStackTrace();
222 | }
223 | return null;
224 | }
225 |
226 | }
227 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/MapSender.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager;
2 |
3 | import org.bukkit.Bukkit;
4 | import org.bukkit.entity.Player;
5 | import org.inventivetalent.mapmanager.manager.MapManager;
6 | import org.inventivetalent.reflection.minecraft.Minecraft;
7 | import org.inventivetalent.reflection.minecraft.MinecraftVersion;
8 | import org.inventivetalent.reflection.resolver.FieldResolver;
9 | import org.inventivetalent.reflection.resolver.MethodResolver;
10 |
11 | import java.lang.reflect.InvocationTargetException;
12 | import java.lang.reflect.Method;
13 | import java.util.ArrayList;
14 | import java.util.Collection;
15 | import java.util.Iterator;
16 | import java.util.List;
17 |
18 | class MapSender {
19 |
20 | private static final List sendQueue = new ArrayList<>();
21 | private static int senderID = -1;
22 |
23 | private static Class EntityPlayer;
24 | private static Class PlayerConnection;
25 |
26 | private static FieldResolver EntityPlayerFieldResolver;
27 | private static MethodResolver PlayerConnectionMethodResolver;
28 |
29 | public static void cancelIDs(int[] ids) {
30 | Iterator iterator = sendQueue.iterator();
31 | while (iterator.hasNext()) {
32 | QueuedMap next = iterator.next();
33 | id:
34 | for (int i : ids) {
35 | if (next.id == -i) {
36 | iterator.remove();
37 | break id;
38 | }
39 | }
40 | }
41 | }
42 |
43 | public static void addToQueue(final int id, final ArrayImage image, final Player receiver) {
44 | QueuedMap toSend = new QueuedMap(id, image, receiver);
45 | if (sendQueue.contains(toSend)) {return;}
46 | sendQueue.add(toSend);
47 |
48 | runSender();
49 | }
50 |
51 | protected static void runSender() {
52 | if (Bukkit.getScheduler().isQueued(senderID) || Bukkit.getScheduler().isCurrentlyRunning(senderID) || sendQueue.size() == 0) {
53 | return;
54 | }
55 |
56 | senderID = Bukkit.getScheduler().scheduleSyncRepeatingTask(MapManagerPlugin.instance, new Runnable() {
57 |
58 | @Override
59 | public void run() {
60 | if (sendQueue.isEmpty()) {return;}
61 | for (int i = 0; i < Math.min(sendQueue.size(), MapManager.Options.Sender.AMOUNT + 1); i++) {
62 | QueuedMap current = sendQueue.get(0);
63 | if (current == null) {return;}
64 | sendMap(current.id, current.image, current.player);
65 | if (!sendQueue.isEmpty()) {
66 | sendQueue.remove(0);
67 | }
68 | }
69 | }
70 | }, 0, MapManager.Options.Sender.DELAY);
71 | }
72 |
73 | protected static void sendMap(final int id0, final ArrayImage image, final Player receiver) {
74 | if (MapManager.Options.Sender.TIMINGS) {TimingsHelper.startTiming("MapManager:sender:sendMap");}
75 | if (receiver == null || !receiver.isOnline()) {
76 |
77 | List toRemove = new ArrayList<>();
78 | for (QueuedMap qMap : sendQueue) {
79 | if (qMap == null) {continue;}
80 | if (qMap.player == null || !qMap.player.isOnline()) {
81 | toRemove.add(qMap);
82 | }
83 | }
84 | Bukkit.getScheduler().cancelTask(senderID);
85 | sendQueue.removeAll(toRemove);
86 |
87 | if (MapManager.Options.Sender.TIMINGS) {TimingsHelper.stopTiming("MapManager:sender:sendMap");}
88 | return;
89 | }
90 |
91 | final int id = -id0;
92 |
93 | Bukkit.getScheduler().runTaskAsynchronously(MapManagerPlugin.instance, new Runnable() {
94 | @Override
95 | public void run() {
96 | try {
97 | Object packet = constructPacket(id, image);
98 | sendPacket(packet, receiver);
99 | } catch (Exception e) {
100 | e.printStackTrace();
101 | }
102 | }
103 | });
104 | TimingsHelper.stopTiming("MapManager:sender:sendMap");
105 | }
106 |
107 | private static Class> nmsPacketPlayOutMap;
108 | private static Class> nmsWorldMap$UpdateData;
109 |
110 | static {
111 | nmsPacketPlayOutMap = MapManagerPlugin.nmsClassResolver.resolveSilent("PacketPlayOutMap", "network.protocol.game.PacketPlayOutMap");
112 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_17_R1)) {
113 | nmsWorldMap$UpdateData = MapManagerPlugin.nmsClassResolver.resolveSilent("world.level.saveddata.maps.WorldMap$b");
114 | }
115 | }
116 |
117 | private static Object constructPacket(int id, ArrayImage data) {
118 | Object packet = null;
119 |
120 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_17_R1)) {
121 | try {
122 | packet = constructPacket_1_17(id, data);
123 | } catch (ReflectiveOperationException e) {
124 | e.printStackTrace();
125 | }
126 | } else if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_14_R1)) {
127 | try {
128 | packet = constructPacket_1_14(id, data);
129 | } catch (ReflectiveOperationException e) {
130 | e.printStackTrace();
131 | }
132 | } else if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_9_R1)) {
133 | try {
134 | packet = constructPacket_1_9(id, data);
135 | } catch (ReflectiveOperationException e) {
136 | e.printStackTrace();
137 | }
138 | } else if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_8_R1)) {
139 | try {
140 | packet = constructPacket_1_8(id, data);
141 | } catch (ReflectiveOperationException e) {
142 | e.printStackTrace();
143 | }
144 | }
145 |
146 | return packet;
147 | }
148 |
149 | private static Object constructPacket_1_8(int id, ArrayImage data) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, NoSuchFieldException {
150 | Object packet = nmsPacketPlayOutMap//
151 | .getConstructor(int.class, byte.class, Collection.class, byte[].class, int.class, int.class, int.class, int.class)//
152 | .newInstance(id,// ID
153 | (byte) 0,// Scale
154 | new ArrayList<>(),// Icons
155 | data.array,// Data
156 | data.minX,// X-position
157 | data.minY,// Y-position
158 | data.maxX,// X-Size (or 2nd X-position)
159 | data.maxY// Y-Size (or 2nd Y-position)
160 | );
161 | return packet;
162 | }
163 |
164 | private static Object constructPacket_1_9(int id, ArrayImage data) throws ReflectiveOperationException {
165 | Object packet = nmsPacketPlayOutMap//
166 | .getConstructor(int.class, byte.class, boolean.class, Collection.class, byte[].class, int.class, int.class, int.class, int.class)//
167 | .newInstance(id,//ID
168 | (byte) 0,//Scale
169 | false,//????
170 | new ArrayList<>(),//Icons
171 | data.array,//Data
172 | data.minX,// X-position
173 | data.minY,// Y-position
174 | data.maxX,// X-Size (or 2nd X-position)
175 | data.maxY// Y-Size (or 2nd Y-position)
176 | );
177 | return packet;
178 | }
179 |
180 | private static Object constructPacket_1_14(int id, ArrayImage data) throws ReflectiveOperationException {
181 | Object packet = nmsPacketPlayOutMap//
182 | .getConstructor(int.class, byte.class, boolean.class, boolean.class, Collection.class, byte[].class, int.class, int.class, int.class, int.class)//
183 | .newInstance(id,//ID
184 | (byte) 0,//Scale
185 | false,// tracking position
186 | false,// locked
187 | new ArrayList<>(),//Icons
188 | data.array,//Data
189 | data.minX,// X-position
190 | data.minY,// Y-position
191 | data.maxX,// X-Size (or 2nd X-position)
192 | data.maxY// Y-Size (or 2nd Y-position)
193 | );
194 | return packet;
195 | }
196 |
197 | private static Object constructPacket_1_17(int id, ArrayImage data) throws ReflectiveOperationException {
198 | Object updateData = nmsWorldMap$UpdateData
199 | .getConstructor(int.class, int.class, int.class, int.class, byte[].class)
200 | .newInstance(
201 | data.minX,// X-position
202 | data.minY,// Y-position
203 | data.maxX,// X-Size (or 2nd X-position)
204 | data.maxY,// Y-Size (or 2nd Y-position)
205 | data.array//Data
206 | );
207 | Object packet = nmsPacketPlayOutMap//
208 | .getConstructor(int.class, byte.class, boolean.class, Collection.class, nmsWorldMap$UpdateData)//
209 | .newInstance(
210 | id,//ID
211 | (byte) 0,//Scale
212 | false,// Show Icons
213 | new ArrayList<>(),//Icons
214 | updateData
215 | );
216 | return packet;
217 | }
218 |
219 | protected static void sendPacket(Object packet, Player p) throws IllegalArgumentException, ClassNotFoundException {
220 | if (EntityPlayer == null) {
221 | EntityPlayer = MapManagerPlugin.nmsClassResolver.resolve("EntityPlayer", "server.level.EntityPlayer");
222 | }
223 | if (PlayerConnection == null) {
224 | PlayerConnection = MapManagerPlugin.nmsClassResolver.resolve("PlayerConnection", "server.network.PlayerConnection");
225 | }
226 | if (EntityPlayerFieldResolver == null) {
227 | EntityPlayerFieldResolver = new FieldResolver(EntityPlayer);
228 | }
229 | if (PlayerConnectionMethodResolver == null) {
230 | PlayerConnectionMethodResolver = new MethodResolver(PlayerConnection);
231 | }
232 |
233 | try {
234 | Object handle = Minecraft.getHandle(p);
235 | final Object connection = EntityPlayerFieldResolver.resolveByFirstTypeAccessor(PlayerConnection).get(handle);
236 | Method sendPacket = PlayerConnectionMethodResolver.resolveSignature("void sendPacket(Packet)", "void a(Packet)");
237 | sendPacket.invoke(connection, packet);
238 | } catch (ReflectiveOperationException e) {
239 | throw new RuntimeException(e);
240 | }
241 | }
242 |
243 | static class QueuedMap {
244 |
245 | final int id;
246 | final ArrayImage image;
247 | final Player player;
248 |
249 | QueuedMap(int id, ArrayImage image, Player player) {
250 | this.id = id;
251 | this.image = image;
252 | this.player = player;
253 | }
254 |
255 | @Override
256 | public int hashCode() {
257 | final int prime = 31;
258 | int result = 1;
259 | result = prime * result + this.id;
260 | result = prime * result + (this.image == null ? 0 : this.image.hashCode());
261 | result = prime * result + (this.player == null ? 0 : this.player.hashCode());
262 | return result;
263 | }
264 |
265 | @Override
266 | public boolean equals(Object obj) {
267 | if (this == obj) {return true;}
268 | if (obj == null) {return false;}
269 | if (this.getClass() != obj.getClass()) {return false;}
270 | QueuedMap other = (QueuedMap) obj;
271 | if (this.id != other.id) {return false;}
272 | if (this.image == null) {
273 | if (other.image != null) {return false;}
274 | } else if (!this.image.equals(other.image)) {return false;}
275 | if (this.player == null) {
276 | if (other.player != null) {return false;}
277 | } else if (!this.player.equals(other.player)) {return false;}
278 | return true;
279 | }
280 |
281 | }
282 |
283 | }
284 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/MultiMapWrapper.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager;
2 |
3 | import org.bukkit.OfflinePlayer;
4 | import org.bukkit.entity.ItemFrame;
5 | import org.bukkit.entity.Player;
6 | import org.inventivetalent.mapmanager.controller.MultiMapController;
7 | import org.inventivetalent.mapmanager.wrapper.MapWrapper;
8 | import org.inventivetalent.mapmanager.wrapper.MultiWrapper;
9 |
10 | import java.awt.*;
11 | import java.awt.image.BufferedImage;
12 | import java.util.HashSet;
13 | import java.util.Set;
14 | import java.util.UUID;
15 |
16 | class MultiMapWrapper extends DefaultMapWrapper implements MapWrapper, MultiWrapper {
17 |
18 | private ArrayImage content;
19 | private MapWrapper[][] wrapperMatrix;
20 | private Set viewerIds = new HashSet<>();
21 |
22 | private MultiMapController controller = new MultiMapController() {
23 | @Override
24 | public void addViewer(final Player player) {
25 | if (!viewerIds.contains(player.getUniqueId())) {
26 | matrixIterator(new MatrixCallable() {
27 | @Override
28 | public void call(MapWrapper wrapper) {
29 | wrapper.getController().addViewer(player);
30 | }
31 | });
32 | viewerIds.add(player.getUniqueId());
33 | }
34 | }
35 |
36 | @Override
37 | public void removeViewer(final OfflinePlayer player) {
38 | matrixIterator(new MatrixCallable() {
39 | @Override
40 | public void call(MapWrapper wrapper) {
41 | wrapper.getController().removeViewer(player);
42 | }
43 | });
44 | viewerIds.remove(player.getUniqueId());
45 | }
46 |
47 | @Override
48 | public void clearViewers() {
49 | matrixIterator(new MatrixCallable() {
50 | @Override
51 | public void call(MapWrapper wrapper) {
52 | wrapper.getController().clearViewers();
53 | }
54 | });
55 | viewerIds.clear();
56 | }
57 |
58 | @Override
59 | public boolean isViewing(OfflinePlayer player) {
60 | return viewerIds.contains(player.getUniqueId());
61 | // for (int x = 0; x < wrapperMatrix.length; x++) {
62 | // for (int y = 0; y < wrapperMatrix[x].length; y++) {
63 | // if (wrapperMatrix[x][y].getController().isViewing(player)) { return true; }
64 | // }
65 | // }
66 | // return false;
67 | }
68 |
69 | @Override
70 | public int getMapId(OfflinePlayer player) {
71 | //We don't have a unique ID
72 | return -1;
73 | }
74 |
75 | @Override
76 | @Deprecated
77 | public void update(ArrayImage content) {
78 | ArrayImage[][] split = splitImage(content.toBuffered(), wrapperMatrix[0].length, wrapperMatrix.length);
79 | for (int x = 0; x < wrapperMatrix.length; x++) {
80 | for (int y = 0; y < wrapperMatrix[x].length; y++) {
81 | wrapperMatrix[x][y].getController().update(split[x][y]);
82 | }
83 | }
84 | }
85 |
86 | @Override
87 | public void update(BufferedImage content) {
88 | ArrayImage[][] split = splitImage(content, wrapperMatrix[0].length, wrapperMatrix.length);
89 | // setContent(split);
90 |
91 | for (int x = 0; x < split.length; x++) {
92 | for (int y = 0; y < split[x].length; y++) {
93 | wrapperMatrix[x][y].getController().update(split[x][y]);
94 | }
95 | }
96 | }
97 |
98 | @Override
99 | public ArrayImage getContent() {
100 | return content;
101 | }
102 |
103 | @Override
104 | public void sendContent(Player player) {
105 | sendContent(player, false);
106 | }
107 |
108 | @Override
109 | public void sendContent(final Player player, final boolean withoutQueue) {
110 | matrixIterator(new MatrixCallable() {
111 | @Override
112 | public void call(MapWrapper wrapper) {
113 | wrapper.getController().sendContent(player, withoutQueue);
114 | }
115 | });
116 | }
117 |
118 | @Override
119 | public void showInFrames(Player player, int[][] entityIdMatrix) {
120 | for (int x = 0; x < entityIdMatrix.length; x++) {
121 | for (int y = 0; y < entityIdMatrix[x].length; y++) {
122 | wrapperMatrix[y][x].getController().showInFrame(player, entityIdMatrix[x][wrapperMatrix.length - 1 - y]);
123 | }
124 | }
125 | }
126 |
127 | @Override
128 | public void showInFrames(Player player, int[][] entityIdMatrix, DebugCallable callable) {
129 | for (int x = 0; x < entityIdMatrix.length; x++) {
130 | for (int y = 0; y < entityIdMatrix[x].length; y++) {
131 | wrapperMatrix[y][x].getController().showInFrame(player, entityIdMatrix[x][wrapperMatrix.length - 1 - y], callable.call(wrapperMatrix[y][x].getController(), x, y));
132 | }
133 | }
134 | }
135 |
136 | @Override
137 | public void showInFrames(Player player, ItemFrame[][] itemFrameMatrix, boolean force) {
138 | for (int x = 0; x < itemFrameMatrix.length; x++) {
139 | for (int y = 0; y < itemFrameMatrix[x].length; y++) {
140 | wrapperMatrix[y][x].getController().showInFrame(player, itemFrameMatrix[x][wrapperMatrix.length - 1 - y], force);
141 | }
142 | }
143 | }
144 |
145 | @Override
146 | public void clearFrames(Player player, int[][] entityIdMatrix) {
147 | for (int x = 0; x < entityIdMatrix.length; x++) {
148 | for (int y = 0; y < entityIdMatrix[x].length; y++) {
149 | wrapperMatrix[y][x].getController().clearFrame(player, entityIdMatrix[x][y]);
150 | }
151 | }
152 | }
153 |
154 | @Override
155 | public void clearFrames(Player player, ItemFrame[][] itemFrameMatrix) {
156 | for (int x = 0; x < itemFrameMatrix.length; x++) {
157 | for (int y = 0; y < itemFrameMatrix[x].length; y++) {
158 | wrapperMatrix[y][x].getController().clearFrame(player, itemFrameMatrix[x][y]);
159 | }
160 | }
161 | }
162 |
163 | @Override
164 | public void showInFrames(Player player, ItemFrame[][] itemFrameMatrix) {
165 | showInFrames(player, itemFrameMatrix, false);
166 | }
167 |
168 | @Override
169 | public void showInInventory(Player player, int slot, boolean force) {
170 | throw new UnsupportedOperationException("cannot show multi-map in inventory");
171 | }
172 |
173 | @Override
174 | public void showInInventory(Player player, int slot) {
175 | throw new UnsupportedOperationException("cannot show multi-map in inventory");
176 | }
177 |
178 | @Override
179 | public void showInHand(Player player, boolean force) {
180 | throw new UnsupportedOperationException("cannot show multi-map in inventory");
181 | }
182 |
183 | @Override
184 | public void showInHand(Player player) {
185 | throw new UnsupportedOperationException("cannot show multi-map in inventory");
186 | }
187 |
188 | @Override
189 | public void showInFrame(Player player, int entityId) {
190 | throw new UnsupportedOperationException("cannot show multi-map in single frame");
191 | }
192 |
193 | @Override
194 | public void showInFrame(Player player, ItemFrame frame, boolean force) {
195 | throw new UnsupportedOperationException("cannot show multi-map in single frame");
196 | }
197 |
198 | @Override
199 | public void showInFrame(Player player, ItemFrame frame) {
200 | throw new UnsupportedOperationException("cannot show multi-map in single frame");
201 | }
202 |
203 | @Override
204 | public void showInFrame(Player player, int entityId, String debugInfo) {
205 | throw new UnsupportedOperationException("cannot show multi-map in single frame");
206 | }
207 |
208 | @Override
209 | public void clearFrame(Player player, int entityId) {
210 | throw new UnsupportedOperationException("cannot clear multi-map in single frame");
211 | }
212 |
213 | @Override
214 | public void clearFrame(Player player, ItemFrame frame) {
215 | throw new UnsupportedOperationException("cannot clear multi-map in single frame");
216 | }
217 | };
218 |
219 | public MultiMapWrapper(BufferedImage image, int rows, int columns) {
220 | this(splitImage(image, columns, rows));
221 | // this.content = new ArrayImage(image);
222 | }
223 |
224 | @Deprecated
225 | public MultiMapWrapper(ArrayImage image, int rows, int columns) {
226 | this(splitImage(image.toBuffered(), columns, rows));
227 | // this.content = image;
228 | }
229 |
230 | public MultiMapWrapper(ArrayImage[][] imageMatrix) {
231 | this((Object[][]) imageMatrix);
232 | }
233 |
234 | public MultiMapWrapper(BufferedImage[][] imageMatrix) {
235 | this((Object[][]) imageMatrix);
236 | }
237 |
238 | private MultiMapWrapper(Object[][] imageMatrix) {
239 | super(null);
240 | setContent(imageMatrix);
241 | }
242 |
243 | @Override
244 | public MultiMapController getController() {
245 | return this.controller;
246 | }
247 |
248 | @Override
249 | @Deprecated
250 | public ArrayImage getContent() {
251 | return this.content;
252 | }
253 |
254 | @Override
255 | public ArrayImage[][] getMultiContent() {
256 | ArrayImage[][] images = new ArrayImage[wrapperMatrix.length][wrapperMatrix[0].length];
257 |
258 | for (int x = 0; x < wrapperMatrix.length; x++) {
259 | if (wrapperMatrix[x].length != wrapperMatrix[0].length) { throw new IllegalArgumentException("image is not rectangular"); }
260 | for (int y = 0; y < wrapperMatrix[x].length; y++) {
261 | images[x][y] = wrapperMatrix[x][y].getContent();
262 | }
263 | }
264 |
265 | return images;
266 | }
267 |
268 | public void unwrap() {
269 | matrixIterator(new MatrixCallable() {
270 | @Override
271 | public void call(MapWrapper wrapper) {
272 | MapManagerPlugin.instance.getMapManager().unwrapImage(wrapper);
273 | }
274 | });
275 | }
276 |
277 | protected void setContent(Object[][] imageMatrix) {
278 | wrapperMatrix = new MapWrapper[imageMatrix.length][imageMatrix[0].length];
279 | for (int x = 0; x < imageMatrix.length; x++) {
280 | if (imageMatrix[x].length != imageMatrix[0].length) { throw new IllegalArgumentException("image is not rectangular"); }
281 | for (int y = 0; y < imageMatrix[x].length; y++) {
282 | Object object = imageMatrix[x][y];
283 | if (object == null) {
284 | throw new IllegalArgumentException("null element in image array");
285 | } else if (object instanceof BufferedImage) {
286 | wrapperMatrix[x][y] = MapManagerPlugin.instance.getMapManager().wrapImage((BufferedImage) object);
287 | } else if (object instanceof ArrayImage) {
288 | wrapperMatrix[x][y] = MapManagerPlugin.instance.getMapManager().wrapImage((ArrayImage) object);
289 | }
290 | }
291 | }
292 | }
293 |
294 | protected void matrixIterator(MatrixCallable callable) {
295 | for (int x = 0; x < wrapperMatrix.length; x++) {
296 | for (int y = 0; y < wrapperMatrix[x].length; y++) {
297 | callable.call(wrapperMatrix[x][y]);
298 | }
299 | }
300 | }
301 |
302 | /**
303 | * Modified Method from http://kalanir.blogspot.de/2010/02/how-to-split-image-into-chunks-java.html
304 | */
305 | static ArrayImage[][] splitImage(final BufferedImage image, final int columns, final int rows) {
306 | int chunkWidth = image.getWidth() / columns; // determines the chunk width and height
307 | int chunkHeight = image.getHeight() / rows;
308 |
309 | ArrayImage[][] images = new ArrayImage[rows][columns];
310 | for (int x = 0; x < rows; x++) {
311 | for (int y = 0; y < columns; y++) {
312 | // Initialize the image array with image chunks
313 | BufferedImage raw = new BufferedImage(chunkWidth, chunkHeight, image.getType());
314 |
315 | // draws the image chunk
316 | Graphics2D gr = raw.createGraphics();
317 | gr.drawImage(image, 0, 0, chunkWidth, chunkHeight, chunkWidth * y, chunkHeight * x, chunkWidth * y + chunkWidth, chunkHeight * x + chunkHeight, null);
318 | gr.dispose();
319 |
320 | images[x][y] = new ArrayImage(raw);
321 | raw.flush();
322 | }
323 | }
324 | return images;
325 | }
326 |
327 | @Override
328 | public boolean equals(Object obj) {
329 | return super.equals(obj);
330 | }
331 |
332 | @Override
333 | public int hashCode() {
334 | return super.hashCode();
335 | }
336 |
337 | interface MatrixCallable {
338 | void call(MapWrapper wrapper);
339 | }
340 | }
341 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/PacketListener.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager;
2 |
3 | import org.bukkit.Bukkit;
4 | import org.bukkit.inventory.ItemStack;
5 | import org.bukkit.util.Vector;
6 | import org.inventivetalent.mapmanager.event.CreativeInventoryMapUpdateEvent;
7 | import org.inventivetalent.mapmanager.event.MapCancelEvent;
8 | import org.inventivetalent.mapmanager.event.MapInteractEvent;
9 | import org.inventivetalent.mapmanager.manager.MapManager;
10 | import org.inventivetalent.packetlistener.handler.PacketHandler;
11 | import org.inventivetalent.packetlistener.handler.PacketOptions;
12 | import org.inventivetalent.packetlistener.handler.ReceivedPacket;
13 | import org.inventivetalent.packetlistener.handler.SentPacket;
14 | import org.inventivetalent.reflection.minecraft.Minecraft;
15 | import org.inventivetalent.reflection.minecraft.MinecraftVersion;
16 | import org.inventivetalent.reflection.resolver.FieldResolver;
17 | import org.inventivetalent.reflection.resolver.MethodResolver;
18 | import org.inventivetalent.reflection.resolver.ResolverQuery;
19 |
20 | import java.util.concurrent.TimeUnit;
21 |
22 | class PacketListener {
23 |
24 | private final PacketHandler packetHandler;
25 |
26 | private static Class PacketPlayInUseEntity$b = MapManagerPlugin.nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayInUseEntity$b");
27 | private static Class PacketPlayInUseEntity$d = MapManagerPlugin.nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayInUseEntity$d");
28 | private static Class PacketPlayInUseEntity$e = MapManagerPlugin.nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayInUseEntity$e");
29 |
30 | private static FieldResolver Vec3DFieldResolver = new FieldResolver(MapManagerPlugin.nmsClassResolver.resolveSilent("Vec3D", "world.phys.Vec3D"));
31 | private static FieldResolver PacketUseEntityFieldResolver = new FieldResolver(MapManagerPlugin.nmsClassResolver.resolveSilent("PacketPlayInUseEntity", "network.protocol.game.PacketPlayInUseEntity"));
32 | private static FieldResolver PacketCreativeSlotFieldResolver = new FieldResolver(MapManagerPlugin.nmsClassResolver.resolveSilent("PacketPlayInSetCreativeSlot", "network.protocol.game.PacketPlayInSetCreativeSlot"));
33 | private static FieldResolver PacketPlayInUseEntity$dFieldResolver = new FieldResolver(PacketPlayInUseEntity$d);
34 | private static FieldResolver PacketPlayInUseEntity$eFieldResolver = new FieldResolver(PacketPlayInUseEntity$e);
35 |
36 | private static MethodResolver CraftItemStackMethodResolver = new MethodResolver(MapManagerPlugin.obcClassResolver.resolveSilent("inventory.CraftItemStack"));
37 | private static MethodResolver PacketPlayInUseEntity$EnumEntityUseActionMethodResolver = new MethodResolver(MapManagerPlugin.nmsClassResolver.resolveSilent("network.protocol.game.PacketPlayInUseEntity$EnumEntityUseAction"));
38 |
39 | public PacketListener(final MapManagerPlugin plugin) {
40 | this.packetHandler = new PacketHandler(plugin) {
41 | @Override
42 | @PacketOptions(forcePlayer = true)
43 | public void onSend(SentPacket sentPacket) {
44 | if (sentPacket.hasPlayer()) {
45 | if ("PacketPlayOutMap".equals(sentPacket.getPacketName())) {
46 | int id = (int) sentPacket.getPacketValue("a");
47 |
48 | if (id < 0) {
49 | //It's one of our maps, invert the id and let it through
50 | int newId = -id;
51 | sentPacket.setPacketValue("a", newId);
52 | } else {
53 | boolean async = !plugin.getServer().isPrimaryThread();
54 | MapCancelEvent mapCancelEvent = new MapCancelEvent(sentPacket.getPlayer(), id, async);
55 | if (!MapManager.Options.ALLOW_VANILLA) {//Vanilla maps not allowed, so we can just cancel all maps
56 | mapCancelEvent.setCancelled(true);
57 | } else {
58 | boolean isPluginMap = id > MapManager.Options.FORCED_OFFSET;
59 | if (MapManager.Options.FORCED_OFFSET <= 0) {//Less efficient method: check if the ID is used by the player
60 | isPluginMap = plugin.getMapManager().isIdUsedBy(sentPacket.getPlayer(), (int) id);
61 | }
62 |
63 | if (isPluginMap) {//It's the ID of one of our maps, so cancel it for this player
64 | mapCancelEvent.setCancelled(true);
65 | }
66 | }
67 | if (mapCancelEvent.getHandlers().getRegisteredListeners().length > 0) {
68 | Bukkit.getPluginManager().callEvent(mapCancelEvent);
69 | }
70 | sentPacket.setCancelled(mapCancelEvent.isCancelled());
71 | }
72 | }
73 | }
74 | }
75 |
76 | @Override
77 | @PacketOptions(forcePlayer = true)
78 | public void onReceive(ReceivedPacket receivedPacket) {
79 | if (receivedPacket.hasPlayer()) {
80 | if ("PacketPlayInUseEntity".equals(receivedPacket.getPacketName())) {
81 | try {
82 | int a = (int) receivedPacket.getPacketValue("a");
83 | Object b = PacketUseEntityFieldResolver.resolveSilent("action", "b").get(receivedPacket.getPacket());
84 | Object c = MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_8_R1) ? receivedPacket.getPacketValue("c") : null;
85 | Object d = MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_9_R1) ? receivedPacket.getPacketValue("d") : null;
86 |
87 | Object entityUseAction = null;
88 | Object hand = null;
89 | Object pos = null;
90 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_17_R1)) {
91 | // Enum is wrapped in another object
92 | entityUseAction = PacketPlayInUseEntity$EnumEntityUseActionMethodResolver.resolve("a").invoke(b);
93 | if (PacketPlayInUseEntity$d.isInstance(b)) {
94 | hand = PacketPlayInUseEntity$dFieldResolver.resolveIndexAccessor(0).get(b);
95 | }
96 | if (PacketPlayInUseEntity$e.isInstance(b)) {
97 | hand = PacketPlayInUseEntity$eFieldResolver.resolveIndexAccessor(0).get(b);
98 | pos = PacketPlayInUseEntity$eFieldResolver.resolveIndexAccessor(1).get(b);
99 | }
100 | } else {
101 | entityUseAction = b;
102 | pos = c;
103 | hand = d;
104 | }
105 |
106 | Object finalEntityUseAction = entityUseAction;
107 | Object finalPos = pos;
108 | Object finalHand = hand;
109 | boolean cancel = Bukkit.getScheduler().callSyncMethod(getPlugin(), () -> {
110 | boolean async = !plugin.getServer().isPrimaryThread();
111 | MapInteractEvent event = new MapInteractEvent(receivedPacket.getPlayer(), a, ((Enum) finalEntityUseAction).ordinal(), finalPos == null ? null : vec3DtoVector(finalPos), finalHand == null ? 0 : ((Enum) finalHand).ordinal(), async);
112 | if (event.getItemFrame() != null) {
113 | if (event.getMapWrapper() != null) {
114 | Bukkit.getPluginManager().callEvent(event);
115 | return event.isCancelled();
116 | }
117 | }
118 | return false;
119 | }).get(1, TimeUnit.SECONDS);
120 | receivedPacket.setCancelled(cancel);
121 | } catch (Exception e) {
122 | e.printStackTrace();
123 | }
124 | }
125 | if ("PacketPlayInSetCreativeSlot".equals(receivedPacket.getPacketName())) {
126 | try {
127 | int a = (int) PacketCreativeSlotFieldResolver.resolveSilent("slot", "a").get(receivedPacket.getPacket());
128 | Object b = receivedPacket.getPacketValue("b");
129 | ItemStack itemStack = b == null ? null : (ItemStack) CraftItemStackMethodResolver.resolve(new ResolverQuery("asBukkitCopy", MapManagerPlugin.nmsClassResolver.resolve("world.item.ItemStack"))).invoke(null, b);
130 |
131 | boolean async = !plugin.getServer().isPrimaryThread();
132 | CreativeInventoryMapUpdateEvent event = new CreativeInventoryMapUpdateEvent(receivedPacket.getPlayer(), a, itemStack, async);
133 | if (event.getMapWrapper() != null) {
134 | Bukkit.getPluginManager().callEvent(event);
135 | if (event.isCancelled()) {
136 | receivedPacket.setCancelled(true);
137 | }
138 | }
139 | } catch (Exception e) {
140 | e.printStackTrace();
141 | }
142 | }
143 | }
144 | }
145 | };
146 | PacketHandler.addHandler(this.packetHandler);
147 | }
148 |
149 | protected Vector vec3DtoVector(Object vec3D) {
150 | if (vec3D == null) {return null;}
151 | if (MinecraftVersion.VERSION.newerThan(Minecraft.Version.v1_17_R1)) {
152 | double b = (double) Vec3DFieldResolver.resolveAccessor("b").get(vec3D);
153 | double c = (double) Vec3DFieldResolver.resolveAccessor("c").get(vec3D);
154 | double d = (double) Vec3DFieldResolver.resolveAccessor("c").get(vec3D);
155 | return new Vector(b, c, d);
156 | }
157 | try {
158 | double a = (double) Vec3DFieldResolver.resolveAccessor("x"/*1.9*/, "a").get(vec3D);
159 | double b = (double) Vec3DFieldResolver.resolveAccessor("y"/*1.9*/, "b").get(vec3D);
160 | double c = (double) Vec3DFieldResolver.resolveAccessor("z"/*1.9*/, "c").get(vec3D);
161 | return new Vector(a, b, c);
162 | } catch (Exception e) {
163 | e.printStackTrace();
164 | }
165 | return new Vector(0, 0, 0);
166 | }
167 |
168 | protected void disable() {
169 | PacketHandler.removeHandler(this.packetHandler);
170 | }
171 |
172 | }
173 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/TimingsHelper.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager;
2 |
3 | import org.bukkit.plugin.Plugin;
4 | import org.inventivetalent.reflection.resolver.ClassResolver;
5 | import org.spigotmc.CustomTimingsHandler;
6 |
7 | import java.util.HashMap;
8 | import java.util.Map;
9 |
10 | public class TimingsHelper {
11 |
12 | static ClassResolver classResolver = new ClassResolver();
13 |
14 | public static final boolean PAPER_SPIGOT = classResolver.resolveSilent("com.destroystokyo.paper.PaperConfig") != null ||
15 | classResolver.resolveSilent("org.github.paperspigot.PaperSpigotConfig") != null ||
16 | (MapManagerPlugin.instance != null && MapManagerPlugin.instance.getConfig().getBoolean("paperSpigot", false));
17 | static final Map HANDLER_MAP = new HashMap<>();
18 |
19 | public static void startTiming(String name) {
20 | if (!HANDLER_MAP.containsKey(name)) {
21 | HANDLER_MAP.put(name, createHandler(name));
22 | }
23 | Object handler = HANDLER_MAP.get(name);
24 | try {
25 | classResolver.resolveSilent("co.aikar.timings.Timing", "org.spigotmc.CustomTimingsHandler").getDeclaredMethod("startTiming").invoke(handler);
26 | } catch (Exception e) {
27 | throw new RuntimeException(e);
28 | }
29 | }
30 |
31 | public static void stopTiming(String name) {
32 | if (HANDLER_MAP.containsKey(name)) {
33 | Object handler = HANDLER_MAP.get(name);
34 | try {
35 | classResolver.resolveSilent("co.aikar.timings.Timing", "org.spigotmc.CustomTimingsHandler").getDeclaredMethod("stopTiming").invoke(handler);
36 | } catch (Exception e) {
37 | throw new RuntimeException(e);
38 | }
39 | }
40 | }
41 |
42 | private static Object createHandler(String name) {
43 | if (!PAPER_SPIGOT) {
44 | return new CustomTimingsHandler(name);
45 | } else {
46 | try {
47 | Class> clazz = Class.forName("co.aikar.timings.Timings");
48 | return clazz.getDeclaredMethod("of", Plugin.class, String.class).invoke(null, MapManagerPlugin.instance, name);
49 | } catch (Exception e) {
50 | throw new RuntimeException(e);
51 | }
52 | }
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/controller/MapController.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager.controller;
2 |
3 | import org.bukkit.OfflinePlayer;
4 | import org.bukkit.entity.ItemFrame;
5 | import org.bukkit.entity.Player;
6 | import org.inventivetalent.mapmanager.ArrayImage;
7 |
8 | /**
9 | * Default MapController
10 | */
11 | public interface MapController {
12 |
13 | /**
14 | * Add a viewer to this {@link org.inventivetalent.mapmanager.wrapper.MapWrapper}
15 | *
16 | * @param player {@link Player} to add
17 | */
18 | void addViewer(Player player);
19 |
20 | /**
21 | * Remove a viewer from this {@link org.inventivetalent.mapmanager.wrapper.MapWrapper}
22 | *
23 | * @param player {@link OfflinePlayer} to remove
24 | */
25 | void removeViewer(OfflinePlayer player);
26 |
27 | /**
28 | * Remove all viewers
29 | */
30 | void clearViewers();
31 |
32 | /**
33 | * Check if a player is viewing this {@link org.inventivetalent.mapmanager.wrapper.MapWrapper}
34 | *
35 | * @param player {@link OfflinePlayer} to check
36 | * @return true
if the player is viewing
37 | */
38 | boolean isViewing(OfflinePlayer player);
39 |
40 | /**
41 | * Get this {@link org.inventivetalent.mapmanager.wrapper.MapWrapper}'s map ID for a player
42 | *
43 | * @param player {@link OfflinePlayer} to get the ID for
44 | * @return the ID, or -1
if no ID exists (i.e. the player is not viewing)
45 | */
46 | int getMapId(OfflinePlayer player);
47 |
48 | /**
49 | * Update the image in this {@link org.inventivetalent.mapmanager.wrapper.MapWrapper}
50 | *
51 | * @param content new {@link ArrayImage} content
52 | */
53 | void update(ArrayImage content);
54 |
55 | ArrayImage getContent();
56 |
57 | /**
58 | * Send the content of this {@link org.inventivetalent.mapmanager.wrapper.MapWrapper} to a player
59 | *
60 | * @param player {@link Player} receiver of the content
61 | */
62 | void sendContent(Player player);
63 |
64 | /**
65 | * Send the content of this {@link org.inventivetalent.mapmanager.wrapper.MapWrapper} to a player
66 | *
67 | * @param player {@link Player} receiver of the content
68 | * @param withoutQueue if true
, the content will be sent immediately
69 | */
70 | void sendContent(Player player, boolean withoutQueue);
71 |
72 | /**
73 | * Show this {@link org.inventivetalent.mapmanager.wrapper.MapWrapper} in a player's inventory
74 | *
75 | * @param player {@link Player}
76 | * @param slot slot to show the map in
77 | * @param force if false
, the map will not be shown if the player is in creative mode
78 | * @see org.inventivetalent.mapmanager.event.CreativeInventoryMapUpdateEvent
79 | */
80 | void showInInventory(Player player, int slot, boolean force);
81 |
82 | /**
83 | * Show this {@link org.inventivetalent.mapmanager.wrapper.MapWrapper} in a player's inventory
84 | *
85 | * @param player {@link Player}
86 | * @param slot slot to show the map in
87 | */
88 | void showInInventory(Player player, int slot);
89 |
90 | /**
91 | * Show this {@link org.inventivetalent.mapmanager.wrapper.MapWrapper} in a player's hand
92 | *
93 | * @param player {@link Player}
94 | * @param force if false
, the map will not be shown if the player is not holding a map, or is in createive mode
95 | * @see #showInFrame(Player, ItemFrame, boolean)
96 | * @see org.inventivetalent.mapmanager.event.CreativeInventoryMapUpdateEvent
97 | */
98 | void showInHand(Player player, boolean force);
99 |
100 | /**
101 | * Show this {@link org.inventivetalent.mapmanager.wrapper.MapWrapper} in a player's hand
102 | *
103 | * @param player {@link Player}
104 | */
105 | void showInHand(Player player);
106 |
107 | /**
108 | * Show this {@link org.inventivetalent.mapmanager.wrapper.MapWrapper} in an {@link ItemFrame}
109 | *
110 | * @param player {@link Player} that will be able to see the map
111 | * @param frame {@link ItemFrame} to show the map in
112 | */
113 | void showInFrame(Player player, ItemFrame frame);
114 |
115 | /**
116 | * Show this {@link org.inventivetalent.mapmanager.wrapper.MapWrapper} in an {@link ItemFrame}
117 | *
118 | * @param player {@link Player} that will be able to see the map
119 | * @param frame {@link ItemFrame} to show the map in
120 | * @param force if false
, the map will not be shown if there is not Map-Item in the ItemFrame
121 | */
122 | void showInFrame(Player player, ItemFrame frame, boolean force);
123 |
124 | /**
125 | * Show this {@link org.inventivetalent.mapmanager.wrapper.MapWrapper} in an {@link ItemFrame}
126 | *
127 | * @param player {@link Player} that will be able to see the map
128 | * @param entityId Entity-ID of the {@link ItemFrame} to show the map in
129 | */
130 | void showInFrame(Player player, int entityId);
131 |
132 | /**
133 | * Show this {@link org.inventivetalent.mapmanager.wrapper.MapWrapper} in an {@link ItemFrame}
134 | *
135 | * @param player {@link Player} that will be able to see the map
136 | * @param entityId Entity-ID of the {@link ItemFrame} to show the map in
137 | * @param debugInfo {@link String} to show when a player looks at the map, or null
138 | */
139 | void showInFrame(Player player, int entityId, String debugInfo);
140 |
141 | /**
142 | * Clear a frame
143 | *
144 | * @param player {@link Player} that will be able to see the cleared frame
145 | * @param entityId Entity-ID of the {@link ItemFrame} to clear
146 | */
147 | void clearFrame(Player player, int entityId);
148 |
149 | /**
150 | * Clear a frame
151 | *
152 | * @param player {@link Player} that will be able to see the cleared frame
153 | * @param frame {@link ItemFrame} to clear
154 | */
155 | void clearFrame(Player player, ItemFrame frame);
156 |
157 | }
158 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/controller/MultiMapController.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager.controller;
2 |
3 | import org.bukkit.entity.ItemFrame;
4 | import org.bukkit.entity.Player;
5 |
6 | import java.awt.image.BufferedImage;
7 |
8 | /**
9 | * Controller for multiple/split maps
10 | *
11 | * @see org.inventivetalent.mapmanager.manager.MapManager#wrapMultiImage(BufferedImage, int, int)
12 | */
13 | public interface MultiMapController extends MapController {
14 |
15 | /**
16 | * Show this {@link MultiMapController} in {@link ItemFrame}s
17 | *
18 | * @param player {@link Player} that will be able to see the maps
19 | * @param entityIdMatrix 2D-Array of entity-IDs of the {@link ItemFrame}s (int[width][height]
)
20 | * @see MapController#showInFrame(Player, int)
21 | */
22 | void showInFrames(Player player, int[][] entityIdMatrix);
23 |
24 | /**
25 | * Show this {@link MultiMapController} in {@link ItemFrame}s
26 | *
27 | * @param player {@link Player} that will be able to see the maps
28 | * @param entityIdMatrix 2D-Array of entity-IDs of the {@link ItemFrame}s (int[width][height]
)
29 | * @param callable {@link org.inventivetalent.mapmanager.controller.MultiMapController.DebugCallable} which will be called to display debug information, or null
30 | * @see MapController#showInFrame(Player, int, String)
31 | */
32 | void showInFrames(Player player, int[][] entityIdMatrix, DebugCallable callable);
33 |
34 | /**
35 | * Show this {@link MultiMapController} in {@link ItemFrame}s
36 | *
37 | * @param player {@link Player} that will be able to see the maps
38 | * @param itemFrameMatrix 2D-Array of {@link ItemFrame}s (ItemFrame[width][height]
)
39 | * @param force if false
, the map will not be shown if there is not Map-Item in the ItemFrames
40 | * @see MapController#showInFrame(Player, ItemFrame, boolean)
41 | */
42 | void showInFrames(Player player, ItemFrame[][] itemFrameMatrix, boolean force);
43 |
44 | /**
45 | * Show this {@link MultiMapController} in {@link ItemFrame}s
46 | *
47 | * @param player {@link Player} that will be able to see the maps
48 | * @param itemFrameMatrix 2D-Array of {@link ItemFrame}s (ItemFrame[width][height]
)
49 | * @see MapController#showInFrame(Player, ItemFrame)
50 | */
51 | void showInFrames(Player player, ItemFrame[][] itemFrameMatrix);
52 |
53 | /**
54 | * Clear the frames
55 | *
56 | * @param player {@link Player} that will be able to see the cleared frames
57 | * @param entityIdMatrix 2D-Array of entity-IDs of the {@link ItemFrame}s (int[width][height]
)
58 | */
59 | void clearFrames(Player player, int[][] entityIdMatrix);
60 |
61 | /**
62 | * Clear the frames
63 | *
64 | * @param player {@link Player} that will be able to see the cleared frames
65 | * @param itemFrameMatrix 2D-Array of {@link ItemFrame}s (ItemFrame[width][height]
)
66 | */
67 | void clearFrames(Player player, ItemFrame[][] itemFrameMatrix);
68 |
69 | void update(BufferedImage content);
70 |
71 | interface DebugCallable {
72 | /**
73 | * Called to get debug information for a frame
74 | *
75 | * @param controller the {@link MapController}
76 | * @param x X-Position of the current frame
77 | * @param y Y-Position of the current frame
78 | * @return {@link String} to show when a player looks at the map, or null
79 | * @see MapController#showInFrame(Player, int, String)
80 | */
81 | String call(MapController controller, int x, int y);
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/event/CreativeInventoryMapUpdateEvent.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager.event;
2 |
3 | import org.bukkit.Bukkit;
4 | import org.bukkit.Material;
5 | import org.bukkit.entity.Player;
6 | import org.bukkit.event.Cancellable;
7 | import org.bukkit.event.Event;
8 | import org.bukkit.event.HandlerList;
9 | import org.bukkit.inventory.ItemStack;
10 | import org.inventivetalent.mapmanager.MapManagerPlugin;
11 | import org.inventivetalent.mapmanager.manager.MapManager;
12 | import org.inventivetalent.mapmanager.wrapper.MapWrapper;
13 |
14 | /**
15 | * Event called when a client sends a CreativeInventoryUpdate-Packet for a {@link MapManager} map
16 | * (usually after using {@link org.inventivetalent.mapmanager.controller.MapController#showInInventory(Player, int, boolean)})
17 | *
18 | * Cancelled by default.
19 | */
20 | public class CreativeInventoryMapUpdateEvent extends Event implements Cancellable {
21 |
22 | private Player player;
23 | private int slot;
24 | private ItemStack itemStack;
25 |
26 | private MapWrapper mapWrapper;
27 |
28 | private boolean cancelled = true;
29 |
30 | public CreativeInventoryMapUpdateEvent(Player player, int slot, ItemStack itemStack) {
31 | this.player = player;
32 | this.slot = slot;
33 | this.itemStack = itemStack;
34 | }
35 |
36 | public CreativeInventoryMapUpdateEvent(Player player, int slot, ItemStack itemStack, boolean async) {
37 | super(async);
38 | this.player = player;
39 | this.slot = slot;
40 | this.itemStack = itemStack;
41 | }
42 |
43 | /**
44 | * @return the {@link Player} that sent the update
45 | */
46 | public Player getPlayer() {
47 | return player;
48 | }
49 |
50 | /**
51 | * @return the update item slot
52 | */
53 | public int getSlot() {
54 | return slot;
55 | }
56 |
57 | /**
58 | * @return the updated {@link ItemStack}
59 | */
60 | public ItemStack getItemStack() {
61 | return itemStack;
62 | }
63 |
64 | /**
65 | * @return the {@link MapWrapper} of the item
66 | */
67 | public MapWrapper getMapWrapper() {
68 | if (this.mapWrapper != null) { return this.mapWrapper; }
69 | if (getItemStack() == null) { return null; }
70 | if (getItemStack().getType() != Material.MAP) { return null; }
71 | MapManager mapManager = ((MapManagerPlugin) Bukkit.getPluginManager().getPlugin("MapManager")).getMapManager();
72 | return this.mapWrapper = mapManager.getWrapperForId(getPlayer(), getItemStack().getDurability());
73 | }
74 |
75 | @Override
76 | public boolean isCancelled() {
77 | return cancelled;
78 | }
79 |
80 | @Override
81 | public void setCancelled(boolean b) {
82 | cancelled = b;
83 | }
84 |
85 | private static HandlerList handlerList = new HandlerList();
86 |
87 | @Override
88 | public HandlerList getHandlers() {
89 | return handlerList;
90 | }
91 |
92 | public static HandlerList getHandlerList() {
93 | return handlerList;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/event/MapCancelEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015-2016 inventivetalent. All rights reserved.
3 | *
4 | * Redistribution and use in source and binary forms, with or without modification, are
5 | * permitted provided that the following conditions are met:
6 | *
7 | * 1. Redistributions of source code must retain the above copyright notice, this list of
8 | * conditions and the following disclaimer.
9 | *
10 | * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 | * of conditions and the following disclaimer in the documentation and/or other materials
12 | * provided with the distribution.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
15 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 | * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 | *
24 | * The views and conclusions contained in the software and documentation are those of the
25 | * authors and contributors and should not be interpreted as representing official policies,
26 | * either expressed or implied, of anybody else.
27 | */
28 |
29 | package org.inventivetalent.mapmanager.event;
30 |
31 | import org.bukkit.entity.Player;
32 | import org.bukkit.event.Cancellable;
33 | import org.bukkit.event.Event;
34 | import org.bukkit.event.HandlerList;
35 | import org.inventivetalent.mapmanager.manager.MapManager;
36 |
37 | public class MapCancelEvent extends Event implements Cancellable {
38 |
39 | private Player player;
40 | private int id;
41 | private boolean cancelled;
42 |
43 | public MapCancelEvent(Player player, int id) {
44 | this.player = player;
45 | this.id = id;
46 | }
47 |
48 | public MapCancelEvent(Player player, int id, boolean async) {
49 | super(async);
50 | this.player = player;
51 | this.id = id;
52 | }
53 |
54 |
55 | public Player getPlayer() {
56 | return player;
57 | }
58 |
59 | public int getId() {
60 | return id;
61 | }
62 |
63 | public boolean isAllowVanilla() {
64 | return MapManager.Options.ALLOW_VANILLA;
65 | }
66 |
67 | @Override
68 | public boolean isCancelled() {
69 | return cancelled;
70 | }
71 |
72 | @Override
73 | public void setCancelled(boolean b) {
74 | cancelled = b;
75 | }
76 |
77 | private static HandlerList handlerList = new HandlerList();
78 |
79 | @Override
80 | public HandlerList getHandlers() {
81 | return handlerList;
82 | }
83 |
84 | public static HandlerList getHandlerList() {
85 | return handlerList;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/event/MapContentUpdateEvent.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager.event;
2 |
3 | import org.bukkit.event.Event;
4 | import org.bukkit.event.HandlerList;
5 | import org.inventivetalent.mapmanager.ArrayImage;
6 | import org.inventivetalent.mapmanager.wrapper.MapWrapper;
7 |
8 | /**
9 | * Event called when the content of a {@link MapWrapper} is updated
10 | */
11 | public class MapContentUpdateEvent extends Event {
12 |
13 | private MapWrapper mapWrapper;
14 | private ArrayImage content;
15 | private boolean sendContent;
16 |
17 | public MapContentUpdateEvent(MapWrapper mapWrapper, ArrayImage content) {
18 | this.mapWrapper = mapWrapper;
19 | this.content = content;
20 | this.sendContent = true;
21 | }
22 |
23 | public MapContentUpdateEvent(MapWrapper mapWrapper, ArrayImage content, boolean async) {
24 | super(async);
25 | this.mapWrapper = mapWrapper;
26 | this.content = content;
27 | this.sendContent = true;
28 | }
29 |
30 |
31 | /**
32 | * @return the updated {@link MapWrapper}
33 | */
34 | public MapWrapper getMapWrapper() {
35 | return mapWrapper;
36 | }
37 |
38 | /**
39 | * @return the {@link ArrayImage} content
40 | */
41 | public ArrayImage getContent() {
42 | return content;
43 | }
44 |
45 | /**
46 | * Change the updated content
47 | *
48 | * @param content new image content
49 | */
50 | public void setContent(ArrayImage content) {
51 | this.content = content;
52 | }
53 |
54 | /**
55 | * true
by default
56 | *
57 | * @return true
if the content will be sent to the {@link org.inventivetalent.mapmanager.manager.MapManager} viewers
58 | */
59 | public boolean isSendContent() {
60 | return sendContent;
61 | }
62 |
63 | /**
64 | * Change if the content is sent to the viewers
65 | *
66 | * @param sendContent if true
, the content will be sent; if false
, the content will be update without sending
67 | * @see #isSendContent()
68 | */
69 | public void setSendContent(boolean sendContent) {
70 | this.sendContent = sendContent;
71 | }
72 |
73 | private static HandlerList handlerList = new HandlerList();
74 |
75 | @Override
76 | public HandlerList getHandlers() {
77 | return handlerList;
78 | }
79 |
80 | public static HandlerList getHandlerList() {
81 | return handlerList;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/event/MapInteractEvent.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager.event;
2 |
3 | import org.bukkit.entity.ItemFrame;
4 | import org.bukkit.entity.Player;
5 | import org.bukkit.event.Cancellable;
6 | import org.bukkit.event.Event;
7 | import org.bukkit.event.HandlerList;
8 | import org.bukkit.metadata.MetadataValue;
9 | import org.bukkit.util.Vector;
10 | import org.inventivetalent.mapmanager.MapManagerPlugin;
11 | import org.inventivetalent.mapmanager.wrapper.MapWrapper;
12 |
13 | import java.util.List;
14 |
15 | /**
16 | * Event called when a player interacts with a {@link org.inventivetalent.mapmanager.manager.MapManager} map in an {@link ItemFrame}
17 | */
18 | public class MapInteractEvent extends Event implements Cancellable {
19 |
20 | private Player player;
21 | private int entityID;
22 | private int action;
23 | private Vector vector;
24 | private int hand;
25 |
26 | private ItemFrame itemFrame;
27 | private MapWrapper mapWrapper;
28 |
29 | private boolean cancelled;
30 |
31 | public MapInteractEvent(Player who, int entityID, int action, Vector vector, int hand) {
32 | this.player = who;
33 | this.entityID = entityID;
34 | this.action = action;
35 | this.vector = vector;
36 | this.hand = hand;
37 | }
38 |
39 | public MapInteractEvent(Player who, int entityID, int action, Vector vector, int hand, boolean async) {
40 | super(async);
41 | this.player = who;
42 | this.entityID = entityID;
43 | this.action = action;
44 | this.vector = vector;
45 | this.hand = hand;
46 | }
47 |
48 | /**
49 | * @return the {@link Player} that interacted
50 | */
51 | public Player getPlayer() {
52 | return player;
53 | }
54 |
55 | /**
56 | * @return the Entity-ID of the clicked ItemFrame
57 | */
58 | public int getEntityID() {
59 | return entityID;
60 | }
61 |
62 | /**
63 | * @return 0 = INTERACT; 1 = ATTACK; 2 = INTERACT_AT
64 | */
65 | public int getActionID() {
66 | return action;
67 | }
68 |
69 | /**
70 | * Only returns if {@link #getActionID()} == INTERACT_AT
71 | *
72 | * @return the {@link Vector}-Position where the player clicked, or null
if the action is not INTERACT_AT
73 | */
74 | public Vector getVector() {
75 | return vector;
76 | }
77 |
78 | public int getHandID() {
79 | return hand;
80 | }
81 |
82 | /**
83 | * @return the clicked {@link ItemFrame}
84 | */
85 | public ItemFrame getItemFrame() {
86 | if (this.itemFrame != null) { return this.itemFrame; }
87 | return this.itemFrame = MapManagerPlugin.getItemFrameById(getPlayer().getWorld(), getEntityID());
88 | }
89 |
90 | /**
91 | * @return the {@link MapWrapper} of the clicked frame
92 | */
93 | public MapWrapper getMapWrapper() {
94 | if (this.mapWrapper != null) { return this.mapWrapper; }
95 | ItemFrame itemFrame = getItemFrame();
96 | if (itemFrame != null) {
97 | if (itemFrame.hasMetadata("MAP_WRAPPER_REF")) {
98 | List metadataValues = itemFrame.getMetadata("MAP_WRAPPER_REF");
99 | for (MetadataValue value : metadataValues) {
100 | MapWrapper wrapper = (MapWrapper) value.value();
101 | if (wrapper != null) {
102 | return this.mapWrapper = wrapper;
103 | }
104 | }
105 | }
106 | }
107 | return null;
108 | }
109 |
110 | @Override
111 | public boolean isCancelled() {
112 | return cancelled;
113 | }
114 |
115 | @Override
116 | public void setCancelled(boolean b) {
117 | cancelled = b;
118 | }
119 |
120 | private static HandlerList handlerList = new HandlerList();
121 |
122 | @Override
123 | public HandlerList getHandlers() {
124 | return handlerList;
125 | }
126 |
127 | public static HandlerList getHandlerList() {
128 | return handlerList;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/manager/MapManager.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager.manager;
2 |
3 | import org.bukkit.OfflinePlayer;
4 | import org.bukkit.entity.Player;
5 | import org.inventivetalent.mapmanager.ArrayImage;
6 | import org.inventivetalent.mapmanager.MapLimitExceededException;
7 | import org.inventivetalent.mapmanager.wrapper.MapWrapper;
8 |
9 | import java.awt.image.BufferedImage;
10 | import java.util.Set;
11 |
12 | public interface MapManager {
13 |
14 | /**
15 | * Get the {@link MapWrapper} for a {@link BufferedImage}
16 | *
17 | * @param image the image to wrap
18 | * @return the wrapper of the image
19 | */
20 | MapWrapper wrapImage(BufferedImage image);
21 |
22 | /**
23 | * Get the {@link MapWrapper} for an {@link ArrayImage}
24 | *
25 | * @param image the image to wrap
26 | * @return the wrapper of the image
27 | */
28 | MapWrapper wrapImage(ArrayImage image);
29 |
30 | /**
31 | * Wrap an image and split it into multiple maps
32 | *
33 | * @param image the image to wrap
34 | * @param rows rows of the split (i.e. height)
35 | * @param columns columns of the split (i.e. width)
36 | * @return the wrapper of the image
37 | */
38 | MapWrapper wrapMultiImage(BufferedImage image, int rows, int columns);
39 |
40 | /**
41 | * Wrap an image and split it into multiple maps
42 | *
43 | * @param image the image to wrap
44 | * @param rows rows of the split (i.e. height)
45 | * @param columns columns of the split (i.e. width)
46 | * @return the wrapper of the image
47 | */
48 | @Deprecated
49 | MapWrapper wrapMultiImage(ArrayImage image, int rows, int columns);
50 |
51 | /**
52 | * Wrap multiple images
53 | *
54 | * @param images the images to wrap
55 | * @return the wrapper of the image
56 | */
57 | MapWrapper wrapMultiImage(ArrayImage[][] images);
58 |
59 | /**
60 | * Remove a wrapper
61 | *
62 | * @param wrapper the {@link MapWrapper} to remove
63 | */
64 | void unwrapImage(MapWrapper wrapper);
65 |
66 | /**
67 | * Get all {@link MapWrapper}s visible to a player
68 | *
69 | * @param player {@link OfflinePlayer} to check
70 | * @return a set of visible maps
71 | */
72 | Set getMapsVisibleTo(OfflinePlayer player);
73 |
74 | /**
75 | * Get the MapWrapper for a {@link OfflinePlayer} and a map ID
76 | *
77 | * @param player {@link OfflinePlayer} to get the wrapper for
78 | * @param id ID of the map
79 | * @return the {@link MapWrapper} or null
80 | */
81 | MapWrapper getWrapperForId(OfflinePlayer player, int id);
82 |
83 | /**
84 | * Registers an occupied ID (which will not be used as a map ID)
85 | *
86 | * @param id the ID to register
87 | */
88 | void registerOccupiedID(int id);
89 |
90 | /**
91 | * Unregisters an occupied ID
92 | *
93 | * @param id the ID to unregister
94 | * @see #registerOccupiedID(int)
95 | */
96 | void unregisterOccupiedID(int id);
97 |
98 | /**
99 | * Get the IDs which are used for a player
100 | *
101 | * @param player the {@link OfflinePlayer} to get the IDs for
102 | * @return Set of IDs
103 | */
104 | Set getOccupiedIdsFor(OfflinePlayer player);
105 |
106 | /**
107 | * Check if an map ID is used by a player
108 | *
109 | * @param player {@link OfflinePlayer} to check
110 | * @param id Map ID to check
111 | * @return true
if the ID is used
112 | */
113 | boolean isIdUsedBy(OfflinePlayer player, int id);
114 |
115 | /**
116 | * Get the next available (non-occupied) map ID for a player
117 | *
118 | * @param player {@link Player} to get the ID for
119 | * @return the next available ID
120 | * @throws MapLimitExceededException if there are no more IDs available (i.e. all IDs up to {@link Short#MAX_VALUE} are occupied by the player)
121 | */
122 | int getNextFreeIdFor(Player player) throws MapLimitExceededException;
123 |
124 | /**
125 | * Removes all {@link MapWrapper}s for a player
126 | *
127 | * @param player {@link OfflinePlayer} to clear the maps for
128 | */
129 | void clearAllMapsFor(OfflinePlayer player);
130 |
131 | /**
132 | * MapManger Options
133 | */
134 | class Options {
135 |
136 | /**
137 | * If vanilla maps should be allowed to be sent to the players (less efficient, since we need to check the id of every sent map)
138 | */
139 | public static boolean ALLOW_VANILLA = false;
140 |
141 | /**
142 | * Offset for new map IDs
143 | */
144 | public static int FORCED_OFFSET = 0;
145 |
146 | /**
147 | * If the plugin checks for duplicate images before creating a new one (Less efficient when first creating a image, but more efficient overall)
148 | */
149 | public static boolean CHECK_DUPLICATES = true;
150 |
151 | /**
152 | * Cache the packet data in the image object (less CPU intensive for a lot of players, but probably a bit more memory intensive depending on the image size)
153 | */
154 | public static boolean CACHE_DATA = true;
155 |
156 | public static boolean TIMINGS = false;
157 |
158 | /**
159 | * Options for Map-sending
160 | */
161 | public static class Sender {
162 |
163 | /**
164 | * Delay between map packets (ticks)
165 | */
166 | public static int DELAY = 2;
167 |
168 | /**
169 | * Maximum amount of map packets sent at once
170 | */
171 | public static int AMOUNT = 10;
172 |
173 | /**
174 | * Allow immediate sending of map data
175 | */
176 | public static boolean ALLOW_QUEUE_BYPASS = true;
177 |
178 | public static boolean TIMINGS = false;
179 |
180 | }
181 |
182 | }
183 |
184 | }
185 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/metrics/Metrics.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager.metrics;
2 |
3 | import org.bukkit.Bukkit;
4 | import org.bukkit.configuration.file.YamlConfiguration;
5 | import org.bukkit.plugin.ServicePriority;
6 | import org.bukkit.plugin.java.JavaPlugin;
7 | import org.json.simple.JSONArray;
8 | import org.json.simple.JSONObject;
9 |
10 | import javax.net.ssl.HttpsURLConnection;
11 | import java.io.ByteArrayOutputStream;
12 | import java.io.DataOutputStream;
13 | import java.io.File;
14 | import java.io.IOException;
15 | import java.lang.reflect.InvocationTargetException;
16 | import java.net.URL;
17 | import java.util.*;
18 | import java.util.logging.Level;
19 | import java.util.zip.GZIPOutputStream;
20 |
21 | /**
22 | * bStats collects some data for plugin authors.
23 | *
24 | * Check out https://bStats.org/ to learn more about bStats!
25 | */
26 | public class Metrics {
27 |
28 | // The version of this bStats class
29 | public static final int B_STATS_VERSION = 1;
30 |
31 | // The url to which the data is sent
32 | private static final String URL = "https://bStats.org/submitData";
33 |
34 | // Should failed requests be logged?
35 | private static boolean logFailedRequests;
36 |
37 | // The uuid of the server
38 | private static String serverUUID;
39 |
40 | // The plugin
41 | private final JavaPlugin plugin;
42 |
43 | // A list with all custom charts
44 | private final List charts = new ArrayList<>();
45 |
46 | /**
47 | * Class constructor.
48 | *
49 | * @param plugin The plugin which stats should be submitted.
50 | */
51 | public Metrics(JavaPlugin plugin) {
52 | if (plugin == null) {
53 | throw new IllegalArgumentException("Plugin cannot be null!");
54 | }
55 | this.plugin = plugin;
56 |
57 | // Get the config file
58 | File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
59 | File configFile = new File(bStatsFolder, "config.yml");
60 | YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
61 |
62 | // Check if the config file exists
63 | if (!config.isSet("serverUuid")) {
64 |
65 | // Add default values
66 | config.addDefault("enabled", true);
67 | // Every server gets it's unique random id.
68 | config.addDefault("serverUuid", UUID.randomUUID().toString());
69 | // Should failed request be logged?
70 | config.addDefault("logFailedRequests", false);
71 |
72 | // Inform the server owners about bStats
73 | config.options().header(
74 | "bStats collects some data for plugin authors like how many servers are using their plugins.\n" +
75 | "To honor their work, you should not disable it.\n" +
76 | "This has nearly no effect on the server performance!\n" +
77 | "Check out https://bStats.org/ to learn more :)"
78 | ).copyDefaults(true);
79 | try {
80 | config.save(configFile);
81 | } catch (IOException ignored) { }
82 | }
83 |
84 | // Load the data
85 | serverUUID = config.getString("serverUuid");
86 | logFailedRequests = config.getBoolean("logFailedRequests", false);
87 | if (config.getBoolean("enabled", true)) {
88 | boolean found = false;
89 | // Search for all other bStats Metrics classes to see if we are the first one
90 | for (Class> service : Bukkit.getServicesManager().getKnownServices()) {
91 | try {
92 | service.getField("B_STATS_VERSION"); // Our identifier :)
93 | found = true; // We aren't the first
94 | break;
95 | } catch (NoSuchFieldException ignored) { }
96 | }
97 | // Register our service
98 | Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal);
99 | if (!found) {
100 | // We are the first!
101 | startSubmitting();
102 | }
103 | }
104 | }
105 |
106 | /**
107 | * Adds a custom chart.
108 | *
109 | * @param chart The chart to add.
110 | */
111 | public void addCustomChart(CustomChart chart) {
112 | if (chart == null) {
113 | throw new IllegalArgumentException("Chart cannot be null!");
114 | }
115 | charts.add(chart);
116 | }
117 |
118 | /**
119 | * Starts the Scheduler which submits our data every 30 minutes.
120 | */
121 | private void startSubmitting() {
122 | final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
123 | timer.scheduleAtFixedRate(new TimerTask() {
124 | @Override
125 | public void run() {
126 | if (!plugin.isEnabled()) { // Plugin was disabled
127 | timer.cancel();
128 | return;
129 | }
130 | // Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler
131 | // Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;)
132 | Bukkit.getScheduler().runTask(plugin, new Runnable() {
133 | @Override
134 | public void run() {
135 | submitData();
136 | }
137 | });
138 | }
139 | }, 1000 * 60 * 5, 1000 * 60 * 30);
140 | // Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start
141 | // WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
142 | // WARNING: Just don't do it!
143 | }
144 |
145 | /**
146 | * Gets the plugin specific data.
147 | * This method is called using Reflection.
148 | *
149 | * @return The plugin specific data.
150 | */
151 | public JSONObject getPluginData() {
152 | JSONObject data = new JSONObject();
153 |
154 | String pluginName = plugin.getDescription().getName();
155 | String pluginVersion = plugin.getDescription().getVersion();
156 |
157 | data.put("pluginName", pluginName); // Append the name of the plugin
158 | data.put("pluginVersion", pluginVersion); // Append the version of the plugin
159 | JSONArray customCharts = new JSONArray();
160 | for (CustomChart customChart : charts) {
161 | // Add the data of the custom charts
162 | JSONObject chart = customChart.getRequestJsonObject();
163 | if (chart == null) { // If the chart is null, we skip it
164 | continue;
165 | }
166 | customCharts.add(chart);
167 | }
168 | data.put("customCharts", customCharts);
169 |
170 | return data;
171 | }
172 |
173 | /**
174 | * Gets the server specific data.
175 | *
176 | * @return The server specific data.
177 | */
178 | private JSONObject getServerData() {
179 | // Minecraft specific data
180 | int playerAmount = Bukkit.getOnlinePlayers().size();
181 | int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
182 | String bukkitVersion = org.bukkit.Bukkit.getVersion();
183 | bukkitVersion = bukkitVersion.substring(bukkitVersion.indexOf("MC: ") + 4, bukkitVersion.length() - 1);
184 |
185 | // OS/Java specific data
186 | String javaVersion = System.getProperty("java.version");
187 | String osName = System.getProperty("os.name");
188 | String osArch = System.getProperty("os.arch");
189 | String osVersion = System.getProperty("os.version");
190 | int coreCount = Runtime.getRuntime().availableProcessors();
191 |
192 | JSONObject data = new JSONObject();
193 |
194 | data.put("serverUUID", serverUUID);
195 |
196 | data.put("playerAmount", playerAmount);
197 | data.put("onlineMode", onlineMode);
198 | data.put("bukkitVersion", bukkitVersion);
199 |
200 | data.put("javaVersion", javaVersion);
201 | data.put("osName", osName);
202 | data.put("osArch", osArch);
203 | data.put("osVersion", osVersion);
204 | data.put("coreCount", coreCount);
205 |
206 | return data;
207 | }
208 |
209 | /**
210 | * Collects the data and sends it afterwards.
211 | */
212 | private void submitData() {
213 | final JSONObject data = getServerData();
214 |
215 | JSONArray pluginData = new JSONArray();
216 | // Search for all other bStats Metrics classes to get their plugin data
217 | for (Class> service : Bukkit.getServicesManager().getKnownServices()) {
218 | try {
219 | service.getField("B_STATS_VERSION"); // Our identifier :)
220 | } catch (NoSuchFieldException ignored) {
221 | continue; // Continue "searching"
222 | }
223 | // Found one!
224 | try {
225 | pluginData.add(service.getMethod("getPluginData").invoke(Bukkit.getServicesManager().load(service)));
226 | } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { }
227 | }
228 |
229 | data.put("plugins", pluginData);
230 |
231 | // Create a new thread for the connection to the bStats server
232 | new Thread(new Runnable() {
233 | @Override
234 | public void run() {
235 | try {
236 | // Send the data
237 | sendData(data);
238 | } catch (Exception e) {
239 | // Something went wrong! :(
240 | if (logFailedRequests) {
241 | plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e);
242 | }
243 | }
244 | }
245 | }).start();
246 |
247 | }
248 |
249 | /**
250 | * Sends the data to the bStats server.
251 | *
252 | * @param data The data to send.
253 | * @throws Exception If the request failed.
254 | */
255 | private static void sendData(JSONObject data) throws Exception {
256 | if (data == null) {
257 | throw new IllegalArgumentException("Data cannot be null!");
258 | }
259 | if (Bukkit.isPrimaryThread()) {
260 | throw new IllegalAccessException("This method must not be called from the main thread!");
261 | }
262 | HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
263 |
264 | // Compress the data to save bandwidth
265 | byte[] compressedData = compress(data.toString());
266 |
267 | // Add headers
268 | connection.setRequestMethod("POST");
269 | connection.addRequestProperty("Accept", "application/json");
270 | connection.addRequestProperty("Connection", "close");
271 | connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
272 | connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
273 | connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
274 | connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
275 |
276 | // Send data
277 | connection.setDoOutput(true);
278 | DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
279 | outputStream.write(compressedData);
280 | outputStream.flush();
281 | outputStream.close();
282 |
283 | connection.getInputStream().close(); // We don't care about the response - Just send our data :)
284 | }
285 |
286 | /**
287 | * Gzips the given String.
288 | *
289 | * @param str The string to gzip.
290 | * @return The gzipped String.
291 | * @throws IOException If the compression failed.
292 | */
293 | private static byte[] compress(final String str) throws IOException {
294 | if (str == null) {
295 | return null;
296 | }
297 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
298 | GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
299 | gzip.write(str.getBytes("UTF-8"));
300 | gzip.close();
301 | return outputStream.toByteArray();
302 | }
303 |
304 | /**
305 | * Represents a custom chart.
306 | */
307 | public static abstract class CustomChart {
308 |
309 | // The id of the chart
310 | protected final String chartId;
311 |
312 | /**
313 | * Class constructor.
314 | *
315 | * @param chartId The id of the chart.
316 | */
317 | public CustomChart(String chartId) {
318 | if (chartId == null || chartId.isEmpty()) {
319 | throw new IllegalArgumentException("ChartId cannot be null or empty!");
320 | }
321 | this.chartId = chartId;
322 | }
323 |
324 | protected JSONObject getRequestJsonObject() {
325 | JSONObject chart = new JSONObject();
326 | chart.put("chartId", chartId);
327 | try {
328 | JSONObject data = getChartData();
329 | if (data == null) {
330 | // If the data is null we don't send the chart.
331 | return null;
332 | }
333 | chart.put("data", data);
334 | } catch (Throwable t) {
335 | if (logFailedRequests) {
336 | Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t);
337 | }
338 | return null;
339 | }
340 | return chart;
341 | }
342 |
343 | protected abstract JSONObject getChartData();
344 |
345 | }
346 |
347 | /**
348 | * Represents a custom simple pie.
349 | */
350 | public static abstract class SimplePie extends CustomChart {
351 |
352 | /**
353 | * Class constructor.
354 | *
355 | * @param chartId The id of the chart.
356 | */
357 | public SimplePie(String chartId) {
358 | super(chartId);
359 | }
360 |
361 | /**
362 | * Gets the value of the pie.
363 | *
364 | * @return The value of the pie.
365 | */
366 | public abstract String getValue();
367 |
368 | @Override
369 | protected JSONObject getChartData() {
370 | JSONObject data = new JSONObject();
371 | String value = getValue();
372 | if (value == null || value.isEmpty()) {
373 | // Null = skip the chart
374 | return null;
375 | }
376 | data.put("value", value);
377 | return data;
378 | }
379 | }
380 |
381 | /**
382 | * Represents a custom advanced pie.
383 | */
384 | public static abstract class AdvancedPie extends CustomChart {
385 |
386 | /**
387 | * Class constructor.
388 | *
389 | * @param chartId The id of the chart.
390 | */
391 | public AdvancedPie(String chartId) {
392 | super(chartId);
393 | }
394 |
395 | /**
396 | * Gets the values of the pie.
397 | *
398 | * @param valueMap Just an empty map. The only reason it exists is to make your life easier. You don't have to create a map yourself!
399 | * @return The values of the pie.
400 | */
401 | public abstract HashMap getValues(HashMap valueMap);
402 |
403 | @Override
404 | protected JSONObject getChartData() {
405 | JSONObject data = new JSONObject();
406 | JSONObject values = new JSONObject();
407 | HashMap map = getValues(new HashMap());
408 | if (map == null || map.isEmpty()) {
409 | // Null = skip the chart
410 | return null;
411 | }
412 | boolean allSkipped = true;
413 | for (Map.Entry entry : map.entrySet()) {
414 | if (entry.getValue() == 0) {
415 | continue; // Skip this invalid
416 | }
417 | allSkipped = false;
418 | values.put(entry.getKey(), entry.getValue());
419 | }
420 | if (allSkipped) {
421 | // Null = skip the chart
422 | return null;
423 | }
424 | data.put("values", values);
425 | return data;
426 | }
427 | }
428 |
429 | /**
430 | * Represents a custom single line chart.
431 | */
432 | public static abstract class SingleLineChart extends CustomChart {
433 |
434 | /**
435 | * Class constructor.
436 | *
437 | * @param chartId The id of the chart.
438 | */
439 | public SingleLineChart(String chartId) {
440 | super(chartId);
441 | }
442 |
443 | /**
444 | * Gets the value of the chart.
445 | *
446 | * @return The value of the chart.
447 | */
448 | public abstract int getValue();
449 |
450 | @Override
451 | protected JSONObject getChartData() {
452 | JSONObject data = new JSONObject();
453 | int value = getValue();
454 | if (value == 0) {
455 | // Null = skip the chart
456 | return null;
457 | }
458 | data.put("value", value);
459 | return data;
460 | }
461 |
462 | }
463 |
464 | /**
465 | * Represents a custom multi line chart.
466 | */
467 | public static abstract class MultiLineChart extends CustomChart {
468 |
469 | /**
470 | * Class constructor.
471 | *
472 | * @param chartId The id of the chart.
473 | */
474 | public MultiLineChart(String chartId) {
475 | super(chartId);
476 | }
477 |
478 | /**
479 | * Gets the values of the chart.
480 | *
481 | * @param valueMap Just an empty map. The only reason it exists is to make your life easier. You don't have to create a map yourself!
482 | * @return The values of the chart.
483 | */
484 | public abstract HashMap getValues(HashMap valueMap);
485 |
486 | @Override
487 | protected JSONObject getChartData() {
488 | JSONObject data = new JSONObject();
489 | JSONObject values = new JSONObject();
490 | HashMap map = getValues(new HashMap());
491 | if (map == null || map.isEmpty()) {
492 | // Null = skip the chart
493 | return null;
494 | }
495 | boolean allSkipped = true;
496 | for (Map.Entry entry : map.entrySet()) {
497 | if (entry.getValue() == 0) {
498 | continue; // Skip this invalid
499 | }
500 | allSkipped = false;
501 | values.put(entry.getKey(), entry.getValue());
502 | }
503 | if (allSkipped) {
504 | // Null = skip the chart
505 | return null;
506 | }
507 | data.put("values", values);
508 | return data;
509 | }
510 |
511 | }
512 |
513 | /**
514 | * Represents a custom simple bar chart.
515 | */
516 | public static abstract class SimpleBarChart extends CustomChart {
517 |
518 | /**
519 | * Class constructor.
520 | *
521 | * @param chartId The id of the chart.
522 | */
523 | public SimpleBarChart(String chartId) {
524 | super(chartId);
525 | }
526 |
527 | /**
528 | * Gets the value of the chart.
529 | *
530 | * @param valueMap Just an empty map. The only reason it exists is to make your life easier. You don't have to create a map yourself!
531 | * @return The value of the chart.
532 | */
533 | public abstract HashMap getValues(HashMap valueMap);
534 |
535 | @Override
536 | protected JSONObject getChartData() {
537 | JSONObject data = new JSONObject();
538 | JSONObject values = new JSONObject();
539 | HashMap map = getValues(new HashMap());
540 | if (map == null || map.isEmpty()) {
541 | // Null = skip the chart
542 | return null;
543 | }
544 | for (Map.Entry entry : map.entrySet()) {
545 | JSONArray categoryValues = new JSONArray();
546 | categoryValues.add(entry.getValue());
547 | values.put(entry.getKey(), categoryValues);
548 | }
549 | data.put("values", values);
550 | return data;
551 | }
552 |
553 | }
554 |
555 | /**
556 | * Represents a custom advanced bar chart.
557 | */
558 | public static abstract class AdvancedBarChart extends CustomChart {
559 |
560 | /**
561 | * Class constructor.
562 | *
563 | * @param chartId The id of the chart.
564 | */
565 | public AdvancedBarChart(String chartId) {
566 | super(chartId);
567 | }
568 |
569 | /**
570 | * Gets the value of the chart.
571 | *
572 | * @param valueMap Just an empty map. The only reason it exists is to make your life easier. You don't have to create a map yourself!
573 | * @return The value of the chart.
574 | */
575 | public abstract HashMap getValues(HashMap valueMap);
576 |
577 | @Override
578 | protected JSONObject getChartData() {
579 | JSONObject data = new JSONObject();
580 | JSONObject values = new JSONObject();
581 | HashMap map = getValues(new HashMap());
582 | if (map == null || map.isEmpty()) {
583 | // Null = skip the chart
584 | return null;
585 | }
586 | boolean allSkipped = true;
587 | for (Map.Entry entry : map.entrySet()) {
588 | if (entry.getValue().length == 0) {
589 | continue; // Skip this invalid
590 | }
591 | allSkipped = false;
592 | JSONArray categoryValues = new JSONArray();
593 | for (int categoryValue : entry.getValue()) {
594 | categoryValues.add(categoryValue);
595 | }
596 | values.put(entry.getKey(), categoryValues);
597 | }
598 | if (allSkipped) {
599 | // Null = skip the chart
600 | return null;
601 | }
602 | data.put("values", values);
603 | return data;
604 | }
605 |
606 | }
607 |
608 | /**
609 | * Represents a custom simple map chart.
610 | */
611 | public static abstract class SimpleMapChart extends CustomChart {
612 |
613 | /**
614 | * Class constructor.
615 | *
616 | * @param chartId The id of the chart.
617 | */
618 | public SimpleMapChart(String chartId) {
619 | super(chartId);
620 | }
621 |
622 | /**
623 | * Gets the value of the chart.
624 | *
625 | * @return The value of the chart.
626 | */
627 | public abstract Country getValue();
628 |
629 | @Override
630 | protected JSONObject getChartData() {
631 | JSONObject data = new JSONObject();
632 | Country value = getValue();
633 |
634 | if (value == null) {
635 | // Null = skip the chart
636 | return null;
637 | }
638 | data.put("value", value.getCountryIsoTag());
639 | return data;
640 | }
641 |
642 | }
643 |
644 | /**
645 | * Represents a custom advanced map chart.
646 | */
647 | public static abstract class AdvancedMapChart extends CustomChart {
648 |
649 | /**
650 | * Class constructor.
651 | *
652 | * @param chartId The id of the chart.
653 | */
654 | public AdvancedMapChart(String chartId) {
655 | super(chartId);
656 | }
657 |
658 | /**
659 | * Gets the value of the chart.
660 | *
661 | * @param valueMap Just an empty map. The only reason it exists is to make your life easier. You don't have to create a map yourself!
662 | * @return The value of the chart.
663 | */
664 | public abstract HashMap getValues(HashMap valueMap);
665 |
666 | @Override
667 | protected JSONObject getChartData() {
668 | JSONObject data = new JSONObject();
669 | JSONObject values = new JSONObject();
670 | HashMap map = getValues(new HashMap());
671 | if (map == null || map.isEmpty()) {
672 | // Null = skip the chart
673 | return null;
674 | }
675 | boolean allSkipped = true;
676 | for (Map.Entry entry : map.entrySet()) {
677 | if (entry.getValue() == 0) {
678 | continue; // Skip this invalid
679 | }
680 | allSkipped = false;
681 | values.put(entry.getKey().getCountryIsoTag(), entry.getValue());
682 | }
683 | if (allSkipped) {
684 | // Null = skip the chart
685 | return null;
686 | }
687 | data.put("values", values);
688 | return data;
689 | }
690 |
691 | }
692 |
693 | /**
694 | * A enum which is used for custom maps.
695 | */
696 | public enum Country {
697 |
698 | /**
699 | * bStats will use the country of the server.
700 | */
701 | AUTO_DETECT("AUTO", "Auto Detected"),
702 |
703 | ANDORRA("AD", "Andorra"),
704 | UNITED_ARAB_EMIRATES("AE", "United Arab Emirates"),
705 | AFGHANISTAN("AF", "Afghanistan"),
706 | ANTIGUA_AND_BARBUDA("AG", "Antigua and Barbuda"),
707 | ANGUILLA("AI", "Anguilla"),
708 | ALBANIA("AL", "Albania"),
709 | ARMENIA("AM", "Armenia"),
710 | NETHERLANDS_ANTILLES("AN", "Netherlands Antilles"),
711 | ANGOLA("AO", "Angola"),
712 | ANTARCTICA("AQ", "Antarctica"),
713 | ARGENTINA("AR", "Argentina"),
714 | AMERICAN_SAMOA("AS", "American Samoa"),
715 | AUSTRIA("AT", "Austria"),
716 | AUSTRALIA("AU", "Australia"),
717 | ARUBA("AW", "Aruba"),
718 | ÅLAND_ISLANDS("AX", "Åland Islands"),
719 | AZERBAIJAN("AZ", "Azerbaijan"),
720 | BOSNIA_AND_HERZEGOVINA("BA", "Bosnia and Herzegovina"),
721 | BARBADOS("BB", "Barbados"),
722 | BANGLADESH("BD", "Bangladesh"),
723 | BELGIUM("BE", "Belgium"),
724 | BURKINA_FASO("BF", "Burkina Faso"),
725 | BULGARIA("BG", "Bulgaria"),
726 | BAHRAIN("BH", "Bahrain"),
727 | BURUNDI("BI", "Burundi"),
728 | BENIN("BJ", "Benin"),
729 | SAINT_BARTHÉLEMY("BL", "Saint Barthélemy"),
730 | BERMUDA("BM", "Bermuda"),
731 | BRUNEI("BN", "Brunei"),
732 | BOLIVIA("BO", "Bolivia"),
733 | BONAIRE_SINT_EUSTATIUS_AND_SABA("BQ", "Bonaire, Sint Eustatius and Saba"),
734 | BRAZIL("BR", "Brazil"),
735 | BAHAMAS("BS", "Bahamas"),
736 | BHUTAN("BT", "Bhutan"),
737 | BOUVET_ISLAND("BV", "Bouvet Island"),
738 | BOTSWANA("BW", "Botswana"),
739 | BELARUS("BY", "Belarus"),
740 | BELIZE("BZ", "Belize"),
741 | CANADA("CA", "Canada"),
742 | COCOS_ISLANDS("CC", "Cocos Islands"),
743 | THE_DEMOCRATIC_REPUBLIC_OF_CONGO("CD", "The Democratic Republic Of Congo"),
744 | CENTRAL_AFRICAN_REPUBLIC("CF", "Central African Republic"),
745 | CONGO("CG", "Congo"),
746 | SWITZERLAND("CH", "Switzerland"),
747 | CÔTE_D_IVOIRE("CI", "Côte d'Ivoire"),
748 | COOK_ISLANDS("CK", "Cook Islands"),
749 | CHILE("CL", "Chile"),
750 | CAMEROON("CM", "Cameroon"),
751 | CHINA("CN", "China"),
752 | COLOMBIA("CO", "Colombia"),
753 | COSTA_RICA("CR", "Costa Rica"),
754 | CUBA("CU", "Cuba"),
755 | CAPE_VERDE("CV", "Cape Verde"),
756 | CURAÇAO("CW", "Curaçao"),
757 | CHRISTMAS_ISLAND("CX", "Christmas Island"),
758 | CYPRUS("CY", "Cyprus"),
759 | CZECH_REPUBLIC("CZ", "Czech Republic"),
760 | GERMANY("DE", "Germany"),
761 | DJIBOUTI("DJ", "Djibouti"),
762 | DENMARK("DK", "Denmark"),
763 | DOMINICA("DM", "Dominica"),
764 | DOMINICAN_REPUBLIC("DO", "Dominican Republic"),
765 | ALGERIA("DZ", "Algeria"),
766 | ECUADOR("EC", "Ecuador"),
767 | ESTONIA("EE", "Estonia"),
768 | EGYPT("EG", "Egypt"),
769 | WESTERN_SAHARA("EH", "Western Sahara"),
770 | ERITREA("ER", "Eritrea"),
771 | SPAIN("ES", "Spain"),
772 | ETHIOPIA("ET", "Ethiopia"),
773 | FINLAND("FI", "Finland"),
774 | FIJI("FJ", "Fiji"),
775 | FALKLAND_ISLANDS("FK", "Falkland Islands"),
776 | MICRONESIA("FM", "Micronesia"),
777 | FAROE_ISLANDS("FO", "Faroe Islands"),
778 | FRANCE("FR", "France"),
779 | GABON("GA", "Gabon"),
780 | UNITED_KINGDOM("GB", "United Kingdom"),
781 | GRENADA("GD", "Grenada"),
782 | GEORGIA("GE", "Georgia"),
783 | FRENCH_GUIANA("GF", "French Guiana"),
784 | GUERNSEY("GG", "Guernsey"),
785 | GHANA("GH", "Ghana"),
786 | GIBRALTAR("GI", "Gibraltar"),
787 | GREENLAND("GL", "Greenland"),
788 | GAMBIA("GM", "Gambia"),
789 | GUINEA("GN", "Guinea"),
790 | GUADELOUPE("GP", "Guadeloupe"),
791 | EQUATORIAL_GUINEA("GQ", "Equatorial Guinea"),
792 | GREECE("GR", "Greece"),
793 | SOUTH_GEORGIA_AND_THE_SOUTH_SANDWICH_ISLANDS("GS", "South Georgia And The South Sandwich Islands"),
794 | GUATEMALA("GT", "Guatemala"),
795 | GUAM("GU", "Guam"),
796 | GUINEA_BISSAU("GW", "Guinea-Bissau"),
797 | GUYANA("GY", "Guyana"),
798 | HONG_KONG("HK", "Hong Kong"),
799 | HEARD_ISLAND_AND_MCDONALD_ISLANDS("HM", "Heard Island And McDonald Islands"),
800 | HONDURAS("HN", "Honduras"),
801 | CROATIA("HR", "Croatia"),
802 | HAITI("HT", "Haiti"),
803 | HUNGARY("HU", "Hungary"),
804 | INDONESIA("ID", "Indonesia"),
805 | IRELAND("IE", "Ireland"),
806 | ISRAEL("IL", "Israel"),
807 | ISLE_OF_MAN("IM", "Isle Of Man"),
808 | INDIA("IN", "India"),
809 | BRITISH_INDIAN_OCEAN_TERRITORY("IO", "British Indian Ocean Territory"),
810 | IRAQ("IQ", "Iraq"),
811 | IRAN("IR", "Iran"),
812 | ICELAND("IS", "Iceland"),
813 | ITALY("IT", "Italy"),
814 | JERSEY("JE", "Jersey"),
815 | JAMAICA("JM", "Jamaica"),
816 | JORDAN("JO", "Jordan"),
817 | JAPAN("JP", "Japan"),
818 | KENYA("KE", "Kenya"),
819 | KYRGYZSTAN("KG", "Kyrgyzstan"),
820 | CAMBODIA("KH", "Cambodia"),
821 | KIRIBATI("KI", "Kiribati"),
822 | COMOROS("KM", "Comoros"),
823 | SAINT_KITTS_AND_NEVIS("KN", "Saint Kitts And Nevis"),
824 | NORTH_KOREA("KP", "North Korea"),
825 | SOUTH_KOREA("KR", "South Korea"),
826 | KUWAIT("KW", "Kuwait"),
827 | CAYMAN_ISLANDS("KY", "Cayman Islands"),
828 | KAZAKHSTAN("KZ", "Kazakhstan"),
829 | LAOS("LA", "Laos"),
830 | LEBANON("LB", "Lebanon"),
831 | SAINT_LUCIA("LC", "Saint Lucia"),
832 | LIECHTENSTEIN("LI", "Liechtenstein"),
833 | SRI_LANKA("LK", "Sri Lanka"),
834 | LIBERIA("LR", "Liberia"),
835 | LESOTHO("LS", "Lesotho"),
836 | LITHUANIA("LT", "Lithuania"),
837 | LUXEMBOURG("LU", "Luxembourg"),
838 | LATVIA("LV", "Latvia"),
839 | LIBYA("LY", "Libya"),
840 | MOROCCO("MA", "Morocco"),
841 | MONACO("MC", "Monaco"),
842 | MOLDOVA("MD", "Moldova"),
843 | MONTENEGRO("ME", "Montenegro"),
844 | SAINT_MARTIN("MF", "Saint Martin"),
845 | MADAGASCAR("MG", "Madagascar"),
846 | MARSHALL_ISLANDS("MH", "Marshall Islands"),
847 | MACEDONIA("MK", "Macedonia"),
848 | MALI("ML", "Mali"),
849 | MYANMAR("MM", "Myanmar"),
850 | MONGOLIA("MN", "Mongolia"),
851 | MACAO("MO", "Macao"),
852 | NORTHERN_MARIANA_ISLANDS("MP", "Northern Mariana Islands"),
853 | MARTINIQUE("MQ", "Martinique"),
854 | MAURITANIA("MR", "Mauritania"),
855 | MONTSERRAT("MS", "Montserrat"),
856 | MALTA("MT", "Malta"),
857 | MAURITIUS("MU", "Mauritius"),
858 | MALDIVES("MV", "Maldives"),
859 | MALAWI("MW", "Malawi"),
860 | MEXICO("MX", "Mexico"),
861 | MALAYSIA("MY", "Malaysia"),
862 | MOZAMBIQUE("MZ", "Mozambique"),
863 | NAMIBIA("NA", "Namibia"),
864 | NEW_CALEDONIA("NC", "New Caledonia"),
865 | NIGER("NE", "Niger"),
866 | NORFOLK_ISLAND("NF", "Norfolk Island"),
867 | NIGERIA("NG", "Nigeria"),
868 | NICARAGUA("NI", "Nicaragua"),
869 | NETHERLANDS("NL", "Netherlands"),
870 | NORWAY("NO", "Norway"),
871 | NEPAL("NP", "Nepal"),
872 | NAURU("NR", "Nauru"),
873 | NIUE("NU", "Niue"),
874 | NEW_ZEALAND("NZ", "New Zealand"),
875 | OMAN("OM", "Oman"),
876 | PANAMA("PA", "Panama"),
877 | PERU("PE", "Peru"),
878 | FRENCH_POLYNESIA("PF", "French Polynesia"),
879 | PAPUA_NEW_GUINEA("PG", "Papua New Guinea"),
880 | PHILIPPINES("PH", "Philippines"),
881 | PAKISTAN("PK", "Pakistan"),
882 | POLAND("PL", "Poland"),
883 | SAINT_PIERRE_AND_MIQUELON("PM", "Saint Pierre And Miquelon"),
884 | PITCAIRN("PN", "Pitcairn"),
885 | PUERTO_RICO("PR", "Puerto Rico"),
886 | PALESTINE("PS", "Palestine"),
887 | PORTUGAL("PT", "Portugal"),
888 | PALAU("PW", "Palau"),
889 | PARAGUAY("PY", "Paraguay"),
890 | QATAR("QA", "Qatar"),
891 | REUNION("RE", "Reunion"),
892 | ROMANIA("RO", "Romania"),
893 | SERBIA("RS", "Serbia"),
894 | RUSSIA("RU", "Russia"),
895 | RWANDA("RW", "Rwanda"),
896 | SAUDI_ARABIA("SA", "Saudi Arabia"),
897 | SOLOMON_ISLANDS("SB", "Solomon Islands"),
898 | SEYCHELLES("SC", "Seychelles"),
899 | SUDAN("SD", "Sudan"),
900 | SWEDEN("SE", "Sweden"),
901 | SINGAPORE("SG", "Singapore"),
902 | SAINT_HELENA("SH", "Saint Helena"),
903 | SLOVENIA("SI", "Slovenia"),
904 | SVALBARD_AND_JAN_MAYEN("SJ", "Svalbard And Jan Mayen"),
905 | SLOVAKIA("SK", "Slovakia"),
906 | SIERRA_LEONE("SL", "Sierra Leone"),
907 | SAN_MARINO("SM", "San Marino"),
908 | SENEGAL("SN", "Senegal"),
909 | SOMALIA("SO", "Somalia"),
910 | SURINAME("SR", "Suriname"),
911 | SOUTH_SUDAN("SS", "South Sudan"),
912 | SAO_TOME_AND_PRINCIPE("ST", "Sao Tome And Principe"),
913 | EL_SALVADOR("SV", "El Salvador"),
914 | SINT_MAARTEN_DUTCH_PART("SX", "Sint Maarten (Dutch part)"),
915 | SYRIA("SY", "Syria"),
916 | SWAZILAND("SZ", "Swaziland"),
917 | TURKS_AND_CAICOS_ISLANDS("TC", "Turks And Caicos Islands"),
918 | CHAD("TD", "Chad"),
919 | FRENCH_SOUTHERN_TERRITORIES("TF", "French Southern Territories"),
920 | TOGO("TG", "Togo"),
921 | THAILAND("TH", "Thailand"),
922 | TAJIKISTAN("TJ", "Tajikistan"),
923 | TOKELAU("TK", "Tokelau"),
924 | TIMOR_LESTE("TL", "Timor-Leste"),
925 | TURKMENISTAN("TM", "Turkmenistan"),
926 | TUNISIA("TN", "Tunisia"),
927 | TONGA("TO", "Tonga"),
928 | TURKEY("TR", "Turkey"),
929 | TRINIDAD_AND_TOBAGO("TT", "Trinidad and Tobago"),
930 | TUVALU("TV", "Tuvalu"),
931 | TAIWAN("TW", "Taiwan"),
932 | TANZANIA("TZ", "Tanzania"),
933 | UKRAINE("UA", "Ukraine"),
934 | UGANDA("UG", "Uganda"),
935 | UNITED_STATES_MINOR_OUTLYING_ISLANDS("UM", "United States Minor Outlying Islands"),
936 | UNITED_STATES("US", "United States"),
937 | URUGUAY("UY", "Uruguay"),
938 | UZBEKISTAN("UZ", "Uzbekistan"),
939 | VATICAN("VA", "Vatican"),
940 | SAINT_VINCENT_AND_THE_GRENADINES("VC", "Saint Vincent And The Grenadines"),
941 | VENEZUELA("VE", "Venezuela"),
942 | BRITISH_VIRGIN_ISLANDS("VG", "British Virgin Islands"),
943 | U_S__VIRGIN_ISLANDS("VI", "U.S. Virgin Islands"),
944 | VIETNAM("VN", "Vietnam"),
945 | VANUATU("VU", "Vanuatu"),
946 | WALLIS_AND_FUTUNA("WF", "Wallis And Futuna"),
947 | SAMOA("WS", "Samoa"),
948 | YEMEN("YE", "Yemen"),
949 | MAYOTTE("YT", "Mayotte"),
950 | SOUTH_AFRICA("ZA", "South Africa"),
951 | ZAMBIA("ZM", "Zambia"),
952 | ZIMBABWE("ZW", "Zimbabwe");
953 |
954 | private String isoTag;
955 | private String name;
956 |
957 | Country(String isoTag, String name) {
958 | this.isoTag = isoTag;
959 | this.name = name;
960 | }
961 |
962 | /**
963 | * Gets the name of the country.
964 | *
965 | * @return The name of the country.
966 | */
967 | public String getCountryName() {
968 | return name;
969 | }
970 |
971 | /**
972 | * Gets the iso tag of the country.
973 | *
974 | * @return The iso tag of the country.
975 | */
976 | public String getCountryIsoTag() {
977 | return isoTag;
978 | }
979 |
980 | /**
981 | * Gets a country by it's iso tag.
982 | *
983 | * @param isoTag The iso tag of the county.
984 | * @return The country with the given iso tag or null
if unknown.
985 | */
986 | public static Country byIsoTag(String isoTag) {
987 | for (Country country : Country.values()) {
988 | if (country.getCountryIsoTag().equals(isoTag)) {
989 | return country;
990 | }
991 | }
992 | return null;
993 | }
994 |
995 | /**
996 | * Gets a country by a locale.
997 | *
998 | * @param locale The locale.
999 | * @return The country from the giben locale or null
if unknown country or if the locale does not contain a country.
1000 | */
1001 | public static Country byLocale(Locale locale) {
1002 | return byIsoTag(locale.getCountry());
1003 | }
1004 |
1005 | }
1006 |
1007 | }
1008 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/util/Converter.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager.util;
2 |
3 | import java.awt.*;
4 | import java.awt.image.BufferedImage;
5 |
6 | public class Converter {
7 |
8 | public static byte[] imageToBytes(Image image) {
9 |
10 | BufferedImage temp = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
11 | Graphics2D graphics = temp.createGraphics();
12 | graphics.drawImage(image, 0, 0, null);
13 | graphics.dispose();
14 |
15 | int[] pixels = new int[temp.getWidth() * temp.getHeight()];
16 | temp.getRGB(0, 0, temp.getWidth(), temp.getHeight(), pixels, 0, temp.getWidth());
17 |
18 | byte[] result = new byte[temp.getWidth() * temp.getHeight()];
19 | for (int i = 0; i < pixels.length; i++) {
20 | result[i] = MapColorPalette.getColor(new Color(pixels[i], true));
21 | }
22 |
23 | return result;
24 |
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/util/MapColorPalette.java:
--------------------------------------------------------------------------------
1 | // From https://github.com/bergerhealer/BKCommonLib, modified by Joiubaxas#4650
2 | package org.inventivetalent.mapmanager.util;
3 |
4 | import org.bukkit.Bukkit;
5 | import org.inventivetalent.mapmanager.util.mcsd.MCSDBubbleFormat;
6 | import org.inventivetalent.mapmanager.util.mcsd.MCSDGenBukkit;
7 |
8 | import java.awt.*;
9 | import java.io.InputStream;
10 | import java.util.Arrays;
11 |
12 | public class MapColorPalette extends MapColorSpaceData {
13 | private static final MapColorSpaceData COLOR_MAP_DATA = new MapColorSpaceData();
14 | public static final byte[] COLOR_MAP_AVERAGE = new byte[0x10000];
15 | public static final byte[] COLOR_MAP_ADD = new byte[0x10000];
16 | public static final byte[] COLOR_MAP_SUBTRACT = new byte[0x10000];
17 | public static final byte[] COLOR_MAP_MULTIPLY = new byte[0x10000];
18 | public static final byte[] COLOR_MAP_SPECULAR = new byte[0x10000];
19 |
20 | public static final byte COLOR_TRANSPARENT = 0;
21 |
22 | static {
23 | {
24 | MCSDBubbleFormat bubbleData = new MCSDBubbleFormat();
25 | boolean success = false;
26 | try {
27 | String bub_path = "/org/inventivetalent/mapmanager/util/map/";
28 |
29 | if (Bukkit.getVersion().contains("1.12") || Bukkit.getVersion().contains("1.13") || Bukkit.getVersion().contains("1.14")) {
30 | bub_path += "map_1_12.ab";
31 | } else {
32 | bub_path += "map_1_8_8.ab";
33 | }
34 |
35 | InputStream input = MapColorPalette.class.getResourceAsStream(bub_path);
36 | if (input == null) {
37 | System.err.println("Missing data file " + bub_path);
38 | } else {
39 | bubbleData.readFrom(input);
40 | success = true;
41 | }
42 | } catch (Throwable t) {
43 | t.printStackTrace();
44 | }
45 | if (success) {
46 | COLOR_MAP_DATA.readFrom(bubbleData);
47 | } else {
48 | MCSDGenBukkit bukkitGen = new MCSDGenBukkit();
49 | bukkitGen.generate();
50 | COLOR_MAP_DATA.readFrom(bukkitGen);
51 | }
52 | }
53 |
54 | for (int a = 0; a < 256; a++) {
55 | int index = (a * 256);
56 | Color color_a = getRealColor((byte) a);
57 | if (color_a.getAlpha() < 128) {
58 | Arrays.fill(COLOR_MAP_SPECULAR, index, index + 256, COLOR_TRANSPARENT);
59 | } else {
60 | for (int b = 0; b < 256; b++) {
61 | float f = (float) b / 128.0f;
62 | int sr = (int) ((float) color_a.getRed() * f);
63 | int sg = (int) ((float) color_a.getGreen() * f);
64 | int sb = (int) ((float) color_a.getBlue() * f);
65 | COLOR_MAP_SPECULAR[index++] = getColor(sr, sg, sb);
66 | }
67 | }
68 | }
69 |
70 | for (int c1 = 0; c1 < 256; c1++) {
71 | for (int c2 = 0; c2 < 256; c2++) {
72 | initTable((byte) c1, (byte) c2);
73 | }
74 | }
75 | }
76 |
77 |
78 | private static void initTable(byte color1, byte color2) {
79 | int index = getMapIndex(color1, color2);
80 | if (isTransparent(color1) || isTransparent(color2)) {
81 | initTransparent(index, color1, color2);
82 | } else {
83 | Color c1 = getRealColor(color1);
84 | Color c2 = getRealColor(color2);
85 | initColor(
86 | index,
87 | c1.getRed(), c1.getGreen(), c1.getBlue(),
88 | c2.getRed(), c2.getGreen(), c2.getBlue()
89 | );
90 | }
91 | }
92 |
93 | private static void initTransparent(int index, byte color1, byte color2) {
94 | COLOR_MAP_AVERAGE[index] = color2;
95 | COLOR_MAP_ADD[index] = color2;
96 | COLOR_MAP_SUBTRACT[index] = color2;
97 | COLOR_MAP_MULTIPLY[index] = (byte) 0;
98 | }
99 |
100 | private static void initColor(int index, int r1, int g1, int b1, int r2, int g2, int b2) {
101 | initArray(COLOR_MAP_AVERAGE, index, (r1 + r2) >> 1, (g1 + g2) >> 1, (b1 + b2) >> 1);
102 | initArray(COLOR_MAP_ADD, index, (r1 + r2), (g1 + g2), (b1 + b2));
103 | initArray(COLOR_MAP_SUBTRACT, index, (r2 - r1), (g2 - g1), (b2 - b1));
104 | initArray(COLOR_MAP_MULTIPLY, index, (r1 * r2) / 255, (g1 * g2) / 255, (b1 * b2) / 255);
105 | }
106 |
107 | private static void initArray(byte[] array, int index, int r, int g, int b) {
108 | if (r < 0x00) r = 0x00;
109 | if (r > 0xFF) r = 0xFF;
110 | if (g < 0x00) g = 0x00;
111 | if (g > 0xFF) g = 0xFF;
112 | if (b < 0x00) b = 0x00;
113 | if (b > 0xFF) b = 0xFF;
114 | array[index] = getColor(r, g, b);
115 | }
116 |
117 |
118 | public static boolean isTransparent(byte color) {
119 | return (color & 0xFF) < 0x4;
120 | }
121 |
122 | public static byte getColor(Color color) {
123 | if ((color.getAlpha() & 0x80) == 0) {
124 | return COLOR_TRANSPARENT;
125 | } else {
126 | return COLOR_MAP_DATA.get(color.getRed(), color.getGreen(), color.getBlue());
127 | }
128 | }
129 |
130 |
131 | public static byte getColor(int r, int g, int b) {
132 | if (r < 0)
133 | r = 0;
134 | else if (r > 255)
135 | r = 255;
136 | if (g < 0)
137 | g = 0;
138 | else if (g > 255)
139 | g = 255;
140 | if (b < 0)
141 | b = 0;
142 | else if (b > 255)
143 | b = 255;
144 |
145 | return COLOR_MAP_DATA.get(r, g, b);
146 | }
147 |
148 | public static final int getMapIndex(byte color_a, byte color_b) {
149 | return (color_a & 0xFF) | ((color_b & 0xFF) << 8);
150 | }
151 |
152 | public static final Color getRealColor(byte color) {
153 | return COLOR_MAP_DATA.getColor(color);
154 | }
155 |
156 | }
157 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/util/MapColorSpaceData.java:
--------------------------------------------------------------------------------
1 | // From https://github.com/bergerhealer/BKCommonLib, modified by Joiubaxas#4650
2 | package org.inventivetalent.mapmanager.util;
3 |
4 | import java.awt.Color;
5 | import java.util.Arrays;
6 |
7 | public class MapColorSpaceData implements Cloneable {
8 | private final Color[] colors = new Color[256];
9 | private final byte[] data = new byte[1 << 24];
10 |
11 | public MapColorSpaceData() {
12 | Arrays.fill(this.colors, new Color(0, 0, 0, 0));
13 | }
14 |
15 |
16 | public final void clearRGBData() {
17 | Arrays.fill(this.data, (byte) 0);
18 | }
19 |
20 |
21 | public final void clear() {
22 | Arrays.fill(this.colors, new Color(0, 0, 0, 0));
23 | Arrays.fill(this.data, (byte) 0);
24 | }
25 |
26 |
27 | public void readFrom(MapColorSpaceData data) {
28 | System.arraycopy(data.data, 0, this.data, 0, this.data.length);
29 | System.arraycopy(data.colors, 0, this.colors, 0, this.colors.length);
30 | }
31 |
32 |
33 | public final void setColor(byte code, Color color) {
34 | this.colors[code & 0xFF] = color;
35 | }
36 |
37 | public final Color getColor(byte code) {
38 | return this.colors[code & 0xFF];
39 | }
40 |
41 | public final void set(int r, int g, int b, byte code) {
42 | this.data[getDataIndex(r, g, b)] = code;
43 | }
44 |
45 | public final byte get(int r, int g, int b) {
46 | return this.data[getDataIndex(r, g, b)];
47 | }
48 |
49 | public final void set(int index, byte code) {
50 | this.data[index] = code;
51 | }
52 |
53 | public final byte get(int index) {
54 | return this.data[index];
55 | }
56 |
57 | @Override
58 | public MapColorSpaceData clone() {
59 | MapColorSpaceData clone = new MapColorSpaceData();
60 | System.arraycopy(this.colors, 0, clone.colors, 0, this.colors.length);
61 | System.arraycopy(this.data, 0, clone.data, 0, this.data.length);
62 | return clone;
63 | }
64 |
65 | private static final int getDataIndex(int r, int g, int b) {
66 | return (r & 0xFF) + ((g & 0xFF) << 8) + ((b & 0xFF) << 16);
67 | }
68 | }
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/util/bit/BitInputStream.java:
--------------------------------------------------------------------------------
1 | // From https://github.com/bergerhealer/BKCommonLib, modified by Joiubaxas#4650
2 | package org.inventivetalent.mapmanager.util.bit;
3 |
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 |
7 | public class BitInputStream extends InputStream {
8 | private int bitbuff = 0;
9 | private int bitbuff_len = 0;
10 | private boolean eos = false;
11 | private boolean closed = false;
12 | private final InputStream input;
13 | private final boolean closeInput;
14 |
15 | public BitInputStream(InputStream inputStream) {
16 | this(inputStream, true);
17 | }
18 |
19 | public BitInputStream(InputStream inputStream, boolean closeInputStream) {
20 | this.input = inputStream;
21 | this.closeInput = closeInputStream;
22 | }
23 |
24 | @Override
25 | public int available() throws IOException {
26 | if (this.closed) {
27 | throw new IOException("Stream is closed");
28 | }
29 | return this.input.available();
30 | }
31 |
32 | @Override
33 | public int read() throws IOException {
34 | return readBits(8);
35 | }
36 |
37 | public int readBits(int nBits) throws IOException {
38 | if (this.closed) {
39 | throw new IOException("Stream is closed");
40 | }
41 | while (this.bitbuff_len < nBits) {
42 | int readByte = -1;
43 | try {
44 | readByte = this.input.read();
45 | } catch (IOException ex) {}
46 | if (readByte == -1) {
47 | this.eos = true;
48 | return -1;
49 | }
50 | this.bitbuff |= (readByte << this.bitbuff_len);
51 | this.bitbuff_len += 8;
52 | }
53 | int result = bitbuff & ((1 << nBits) - 1);
54 | this.bitbuff >>= nBits;
55 | this.bitbuff_len -= nBits;
56 | return result;
57 | }
58 |
59 | @Override
60 | public void close() throws IOException {
61 | if (!this.closed) {
62 | this.closed = true;
63 | if (this.closeInput) {
64 | this.input.close();
65 | }
66 | }
67 | }
68 | }
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/util/bit/BitPacket.java:
--------------------------------------------------------------------------------
1 | // From https://github.com/bergerhealer/BKCommonLib, modified by Joiubaxas#4650
2 | package org.inventivetalent.mapmanager.util.bit;
3 |
4 | public class BitPacket implements Cloneable {
5 | public int data, bits;
6 |
7 | public BitPacket() {
8 | this.data = 0;
9 | this.bits = 0;
10 | }
11 |
12 | public BitPacket(int data, int bits) {
13 | this.data = data;
14 | this.bits = bits;
15 | }
16 |
17 |
18 | @Override
19 | public boolean equals(Object o) {
20 | if (o == this) {
21 | return true;
22 | } else if (o instanceof BitPacket) {
23 | BitPacket other = (BitPacket) o;
24 | if (other.bits == bits) {
25 | int mask = ((1 << bits) - 1);
26 | return (data & mask) == (other.data & mask);
27 | } else {
28 | return false;
29 | }
30 | } else {
31 | return false;
32 | }
33 | }
34 |
35 | @Override
36 | public BitPacket clone() {
37 | return new BitPacket(this.data, this.bits);
38 | }
39 |
40 | @Override
41 | public String toString() {
42 | String str = Integer.toBinaryString(data & ((1 << bits) - 1));
43 | while (str.length() < this.bits) {
44 | str = "0" + str;
45 | }
46 | return str;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/util/map/map_1_12.ab:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/InventivetalentDev/MapManager/c038c00e36fd43b01dd78ef7b6389fc8ce89ae27/src/org/inventivetalent/mapmanager/util/map/map_1_12.ab
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/util/map/map_1_8_8.ab:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/InventivetalentDev/MapManager/c038c00e36fd43b01dd78ef7b6389fc8ce89ae27/src/org/inventivetalent/mapmanager/util/map/map_1_8_8.ab
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/util/mcsd/MCSDBubbleFormat.java:
--------------------------------------------------------------------------------
1 | // From https://github.com/bergerhealer/BKCommonLib, modified by Joiubaxas#4650
2 | package org.inventivetalent.mapmanager.util.mcsd;
3 |
4 | import org.inventivetalent.mapmanager.util.MapColorSpaceData;
5 | import org.inventivetalent.mapmanager.util.bit.BitInputStream;
6 |
7 | import java.awt.Color;
8 | import java.io.IOException;
9 | import java.io.InputStream;
10 | import java.util.ArrayList;
11 | import java.util.Arrays;
12 | import java.util.zip.GZIPInputStream;
13 |
14 |
15 |
16 | public class MCSDBubbleFormat extends MapColorSpaceData {
17 | public final boolean[][] strands = new boolean[256][256 * 256];
18 | public final ArrayList bubbles = new ArrayList<>();
19 |
20 |
21 | public void readFrom(InputStream stream) throws IOException {
22 | BitInputStream bitStream = new BitInputStream(new GZIPInputStream(stream));
23 | try {
24 |
25 | for (int i = 0; i < 256; i++) {
26 | int r = bitStream.read();
27 | int g = bitStream.read();
28 | int b = bitStream.read();
29 | int a = bitStream.read();
30 | this.setColor((byte) i, new Color(r, g, b, a));
31 | }
32 |
33 | while (true) {
34 | Bubble bubble = new Bubble();
35 | bubble.color = (byte) bitStream.read();
36 | if (bubble.color == 0) {
37 | break;
38 | }
39 | bubble.x = bitStream.read();
40 | bubble.y = bitStream.read();
41 | bubble.z_min = bitStream.read();
42 | bubble.z_max = bubble.z_min + bitStream.read();
43 | this.bubbles.add(bubble);
44 | }
45 |
46 | MCSDWebbingCodec codec = new MCSDWebbingCodec();
47 | for (int z = 0; z < 256; z++) {
48 | Arrays.fill(this.strands[z], false);
49 | codec.reset(strands[z], false);
50 | while (codec.readNext(bitStream));
51 | }
52 |
53 | this.initColors();
54 |
55 | for (int i = 0; i < (1 << 24); i++) {
56 | if (this.get(i) == 0) {
57 | if (bitStream.readBits(1) == 0) {
58 | this.set(i, this.get(i - 1));
59 | } else {
60 | int mode = bitStream.readBits(2);
61 | if (mode == 0) {
62 | this.set(i, this.get(i - 256));
63 | } else if (mode == 1) {
64 | this.set(i, this.get(i + 1));
65 | } else if (mode == 2) {
66 | this.set(i, this.get(i + 256));
67 | } else {
68 | this.set(i, (byte) bitStream.readBits(8));
69 | }
70 | }
71 | }
72 | }
73 | } finally {
74 | bitStream.close();
75 | }
76 | }
77 |
78 |
79 | private void initColors() {
80 |
81 | this.clearRGBData();
82 | for (MCSDBubbleFormat.Bubble cell : bubbles) {
83 | for (int z = cell.z_min; z <= cell.z_max; z++) {
84 | this.set(cell.x, cell.y, z, cell.color);
85 | }
86 | }
87 | spreadColors();
88 | }
89 |
90 | private void spreadColors() {
91 | final boolean[] all_strands = new boolean[1 << 24];
92 | for (int z = 0; z < 256; z++) {
93 | System.arraycopy(this.strands[z], 0, all_strands, z << 16, 1 << 16);
94 | }
95 |
96 | boolean mode = false;
97 | boolean hasChanges;
98 | do {
99 | hasChanges = false;
100 |
101 | final int index_end, index_delta;
102 | int index;
103 | byte color;
104 | if (mode = !mode) {
105 | index_delta = 1;
106 | index = 0;
107 | index_end = (1 << 24);
108 | } else {
109 | index_delta = -1;
110 | index = (1 << 24) - 1;
111 | index_end = 0;
112 | }
113 | do {
114 | if (!all_strands[index]) {
115 | all_strands[index] = true;
116 |
117 | if ((index & 0xFF) < 0xFF) {
118 | if ((color = this.get(index + 1)) != 0) {
119 | this.set(index, color);
120 | hasChanges = true;
121 | } else if ((color = this.get(index)) != 0) {
122 | this.set(index + 1, color);
123 | hasChanges = true;
124 | } else {
125 | all_strands[index] = false;
126 | }
127 | }
128 |
129 | if ((index & 0xFF00) < 0xFF00) {
130 | if ((color = this.get(index + 256)) != 0) {
131 | this.set(index, color);
132 | hasChanges = true;
133 | } else if ((color = this.get(index)) != 0) {
134 | this.set(index + 256, color);
135 | hasChanges = true;
136 | } else {
137 | all_strands[index] = false;
138 | }
139 | }
140 | }
141 | } while ((index += index_delta) != index_end);
142 | } while (hasChanges);
143 | }
144 |
145 |
146 | @Override
147 | public boolean equals(Object o) {
148 | if (o == this) {
149 | return true;
150 | } else if (o instanceof MCSDBubbleFormat) {
151 | MCSDBubbleFormat other = (MCSDBubbleFormat) o;
152 | for (int i = 0; i < strands.length; i++) {
153 | if (other.strands[i] != this.strands[i]) {
154 | return false;
155 | }
156 | }
157 | if (bubbles.size() != other.bubbles.size()) {
158 | return false;
159 | }
160 | for (int i = 0; i < bubbles.size(); i++) {
161 | if (!bubbles.get(i).equals(other.bubbles.get(i))) {
162 | return false;
163 | }
164 | }
165 | return true;
166 | } else {
167 | return false;
168 | }
169 | }
170 |
171 | public static class Bubble {
172 | public int x, y;
173 | public int z_min;
174 | public int z_max;
175 | public byte color;
176 |
177 | @Override
178 | public boolean equals(Object o) {
179 | if (o == this) {
180 | return true;
181 | } else if (o instanceof Bubble) {
182 | Bubble other = (Bubble) o;
183 | return other.x == x && other.y == y &&
184 | other.z_min == z_min && other.z_max == z_max &&
185 | other.color == color;
186 | } else {
187 | return false;
188 | }
189 | }
190 |
191 | @Override
192 | public String toString() {
193 | return "cell{x="+x+", y="+y+", zmin="+z_min+", zmax="+z_max+", color="+(color & 0xFF)+"}";
194 | }
195 | }
196 |
197 | }
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/util/mcsd/MCSDGenBukkit.java:
--------------------------------------------------------------------------------
1 | // From https://github.com/bergerhealer/BKCommonLib, modified by Joiubaxas#4650
2 | package org.inventivetalent.mapmanager.util.mcsd;
3 |
4 | import org.inventivetalent.mapmanager.util.MapColorSpaceData;
5 | import org.bukkit.map.MapPalette;
6 |
7 | public class MCSDGenBukkit extends MapColorSpaceData {
8 |
9 | public void generate() {
10 | this.clear();
11 | for (int i = 0; i < 256; i++) {
12 | try {
13 | setColor((byte) i, MapPalette.getColor((byte) i));
14 | } catch (Throwable t) {}
15 | }
16 | for (int r = 0; r < 256; r++) {
17 | for (int g = 0; g < 256; g++) {
18 | for (int b = 0; b < 256; b++) {
19 | set(r, g, b, MapPalette.matchColor(r, g, b));
20 | }
21 | }
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/util/mcsd/MCSDWebbingCodec.java:
--------------------------------------------------------------------------------
1 | // From https://github.com/bergerhealer/BKCommonLib, modified by Joiubaxas#4650
2 | package org.inventivetalent.mapmanager.util.mcsd;
3 |
4 | import org.inventivetalent.mapmanager.util.bit.BitInputStream;
5 | import org.inventivetalent.mapmanager.util.bit.BitPacket;
6 |
7 | import java.io.IOException;
8 |
9 | public class MCSDWebbingCodec {
10 | private int written_cells;
11 | private int last_x, last_y;
12 | private int last_dx, last_dy;
13 | public boolean[] strands = new boolean[1 << 16];
14 | private BitPacket[] packets = new BitPacket[1024];
15 | private int packets_count = 0;
16 |
17 | public MCSDWebbingCodec() {
18 | for (int i = 0; i < this.packets.length; i++) {
19 | this.packets[i] = new BitPacket();
20 | }
21 | }
22 |
23 |
24 | public void reset(boolean[] cells, boolean copyCells) {
25 | if (copyCells) {
26 | System.arraycopy(cells, 0, this.strands, 0, cells.length);
27 | } else {
28 | this.strands = cells;
29 | }
30 | this.written_cells = 0;
31 | this.last_x = -1000;
32 | this.last_y = -1000;
33 | this.last_dx = 1;
34 | this.last_dy = 1;
35 | this.packets_count = 0;
36 | }
37 |
38 |
39 | public boolean readNext(BitInputStream stream) throws IOException {
40 | int op = stream.readBits(2);
41 | if (op == 0b11) {
42 | if (stream.readBits(1) == 1) {
43 | // Set DX/DY increment/decrement
44 | int sub = stream.readBits(2);
45 | if (sub == 0b00) {
46 | last_dx = -1;
47 | } else if (sub == 0b01) {
48 | last_dx = 1;
49 | } else if (sub == 0b10) {
50 | last_dy = -1;
51 | } else if (sub == 0b11) {
52 | last_dy = 1;
53 | }
54 | } else {
55 | // Command codes
56 | if (stream.readBits(1) == 1) {
57 | // End of slice
58 | return false;
59 | } else {
60 | // Reset position
61 | last_x = stream.readBits(8);
62 | last_y = stream.readBits(8);
63 | strands[last_x | (last_y << 8)] = true;
64 | }
65 | }
66 | } else {
67 | // Write next pixel
68 | if (op == 0b00) {
69 | last_x += last_dx;
70 | } else if (op == 0b01) {
71 | last_y += last_dy;
72 | } else if (op == 0b10) {
73 | last_x += last_dx;
74 | last_y += last_dy;
75 | }
76 | strands[last_x | (last_y << 8)] = true;
77 | }
78 | return true;
79 | }
80 |
81 | }
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/wrapper/MapWrapper.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager.wrapper;
2 |
3 | import org.inventivetalent.mapmanager.ArrayImage;
4 | import org.inventivetalent.mapmanager.controller.MapController;
5 |
6 | public interface MapWrapper {
7 |
8 | /**
9 | * Get this {@link MapWrapper}'s {@link MapController}
10 | *
11 | * @return the {@link MapController}
12 | */
13 | MapController getController();
14 |
15 | /**
16 | * Get the content of this wrapper
17 | *
18 | * @return the {@link ArrayImage} content
19 | */
20 | ArrayImage getContent();
21 |
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/src/org/inventivetalent/mapmanager/wrapper/MultiWrapper.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mapmanager.wrapper;
2 |
3 | import org.inventivetalent.mapmanager.ArrayImage;
4 |
5 | public interface MultiWrapper {
6 |
7 | ArrayImage[][] getMultiContent();
8 |
9 | }
10 |
--------------------------------------------------------------------------------