Skip to content

Commit 1f533f7

Browse files
committed
Improve the map logic for legacy versions
1 parent e419ef8 commit 1f533f7

File tree

6 files changed

+208
-62
lines changed

6 files changed

+208
-62
lines changed

Item Creator/src/main/java/org/broken/arrow/library/itemcreator/ItemCreator.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,18 @@
88
import org.broken.arrow.library.logging.Validate;
99
import org.broken.arrow.library.logging.Validate.ValidateExceptions;
1010
import org.broken.arrow.library.nbt.RegisterNbtAPI;
11+
import org.bukkit.Bukkit;
1112
import org.bukkit.Material;
1213
import org.bukkit.NamespacedKey;
1314
import org.bukkit.enchantments.Enchantment;
1415
import org.bukkit.inventory.ItemStack;
16+
import org.bukkit.map.MapView;
1517
import org.bukkit.plugin.Plugin;
1618

1719
import javax.annotation.Nonnull;
1820
import javax.annotation.Nullable;
21+
import java.lang.reflect.InvocationTargetException;
22+
import java.lang.reflect.Method;
1923
import java.util.Arrays;
2024
import java.util.List;
2125

@@ -367,6 +371,25 @@ public static Enchantment getEnchantment(@Nonnull final NamespacedKey key) {
367371
return getEnchantment(key, null);
368372
}
369373

374+
/**
375+
* Retrieves a {@link MapView} by ID in a version-independent way.
376+
*
377+
* @param id the map ID to retrieve
378+
* @return the {@link MapView}, or null if it doesn't exist
379+
*/
380+
@Nullable
381+
public static MapView getMapById(int id) {
382+
if (getServerVersion() < 13.0F) {
383+
try {
384+
Method getMap = Bukkit.class.getMethod("getMap", short.class);
385+
return (MapView) getMap.invoke(null, (short) id);
386+
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
387+
throw new RuntimeException(e);
388+
}
389+
} else {
390+
return Bukkit.getMap(id);
391+
}
392+
}
370393

371394
/**
372395
* Sets the server version based on the plugin's server version.

Item Creator/src/main/java/org/broken/arrow/library/itemcreator/meta/MapWrapperMeta.java

Lines changed: 99 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
package org.broken.arrow.library.itemcreator.meta;
22

3+
import org.broken.arrow.library.itemcreator.ItemCreator;
34
import org.broken.arrow.library.itemcreator.meta.map.BuildMapView;
5+
import org.bukkit.Bukkit;
46
import org.bukkit.World;
7+
import org.bukkit.inventory.ItemStack;
58
import org.bukkit.inventory.meta.ItemMeta;
69
import org.bukkit.inventory.meta.MapMeta;
710
import org.bukkit.map.MapView;
811

912
import javax.annotation.Nonnull;
1013
import javax.annotation.Nullable;
14+
import java.util.function.Consumer;
1115

1216
/**
1317
* A wrapper class for managing a {@link BuildMapView} and applying it to item metadata.
@@ -25,45 +29,133 @@ public class MapWrapperMeta {
2529
*/
2630
@Nullable
2731
public MapView getMapView() {
32+
if (this.mapView == null)
33+
return null;
2834
return this.mapView.build();
2935
}
3036

37+
/**
38+
* Returns the {@link BuildMapView} that also populate the {@link MapView}.
39+
*
40+
* @return The built {@link BuildMapView}, or null if no {@code BuildMapView} is set.
41+
*/
42+
@Nullable
43+
public BuildMapView getMapViewBuilder() {
44+
this.getMapView();
45+
return this.mapView;
46+
}
47+
3148
/**
3249
* Creates and sets a new {@link BuildMapView} instance based on the given world.
3350
*
3451
* @param world the {@link World} from which to create a new map view (non-null)
3552
* @return the newly created {@link BuildMapView} instance.
3653
*/
3754
public BuildMapView createMapView(@Nonnull final World world) {
38-
this.mapView = new BuildMapView(world);
39-
return this.mapView;
55+
return this.createOrRetrieveMapView(world, -1, (view) -> {
56+
});
4057
}
4158

4259
/**
43-
* Creates and sets a new {@link BuildMapView} instance by wrapping the provided {@link MapView}.
60+
* Creates and sets a new {@link BuildMapView} instance, allowing configuration via a lambda.
61+
* <p>
62+
* This method always creates a new map using the provided {@link World}.
63+
* </p>
4464
*
45-
* @param mapView the existing {@link MapView} to wrap (non-null)
46-
* @return the newly created {@link BuildMapView} instance wrapping the given map view.
65+
* @param world the world to associate the map with. This does not affect the rendering of the map.
66+
* @param action a consumer to configure the resulting {@link BuildMapView}.
67+
* @return the created {@link BuildMapView}.
68+
*/
69+
public BuildMapView createMapView(@Nonnull final World world, @Nonnull final Consumer<BuildMapView> action) {
70+
return this.createOrRetrieveMapView(world, -1, action);
71+
}
72+
73+
/**
74+
* Attempts to retrieve an existing map by its ID and wraps it in a {@link BuildMapView}.
75+
* <p>
76+
* Unlike {@link #createOrRetrieveMapView(World, int, Consumer)} and {@link #createMapView(World, Consumer)},
77+
* the difference is that the first method always creates a new map when one cannot be found, while the second
78+
* always creates a new map regardless. This method, however, does not create a new map if the ID is missing,
79+
* it simply returns {@code null}.
80+
* </p>
81+
*
82+
* @param id the map ID to retrieve can't be below zero.
83+
* @param action a consumer to configure the resulting {@link BuildMapView}, if found.
84+
* @return the retrieved {@link BuildMapView}, or {@code null} if no map exists for the given ID.
85+
*/
86+
@Nullable
87+
public BuildMapView getExistingMapView(final int id, @Nonnull final Consumer<BuildMapView> action) {
88+
return this.createOrRetrieveMapView(null, id, action);
89+
}
90+
91+
/**
92+
* Creates or retrieves a {@link BuildMapView} instance, allowing configuration via a lambda.
93+
* <p>
94+
* If {@code id >= 0}, this method attempts to retrieve an existing map with that ID.
95+
* If no such map exists or {@code id < 0}, a new map is created using the provided {@link World}.
96+
* </p>
97+
*
98+
* @param world the world to associate the map with. This does not affect the rendering of the map.
99+
* May be {@code null} only when {@code id} is provided and exists.
100+
* @param id the map ID to retrieve, or -1 to create a new one.
101+
* @param action a consumer to configure the resulting {@link BuildMapView}.
102+
* @return the created or retrieved {@link BuildMapView}, or {@code null} if retrieval failed and no world was provided.
47103
*/
48-
public BuildMapView createMapView(@Nonnull final MapView mapView) {
104+
@Nullable
105+
public BuildMapView createOrRetrieveMapView(@Nullable final World world, final int id, @Nonnull final Consumer<BuildMapView> action) {
106+
MapView mapView = null;
107+
108+
if (id >= 0) {
109+
mapView = ItemCreator.getMapById(id);
110+
}
111+
if (mapView == null) {
112+
if (world == null) return null;
113+
mapView = Bukkit.createMap(world);
114+
}
115+
49116
this.mapView = new BuildMapView(mapView);
117+
action.accept(this.mapView);
50118
return this.mapView;
51119
}
52120

121+
/**
122+
* It is using your crated {@link BuildMapView} instance by wrapping the provided {@link MapView}.
123+
*
124+
* @param buildMapView new instance of {@link BuildMapView} to wrap (non-null)
125+
* @return the newly created {@link BuildMapView} instance wrapping the given map view.
126+
*/
127+
public BuildMapView createMapView(@Nonnull final BuildMapView buildMapView) {
128+
this.mapView = buildMapView;
129+
return buildMapView;
130+
}
131+
53132
/**
54133
* Applies the stored {@link BuildMapView} to the given {@link ItemMeta} if it is a {@link MapMeta}.
55134
*
56135
* <p>If the {@code itemMeta} is not an instance of {@link MapMeta}, this method does nothing.
57136
* Otherwise, it builds the {@link MapView} from the stored {@link BuildMapView} and sets it on the {@link MapMeta}.</p>
58137
*
138+
* @param item the itemStack to apply the data.
59139
* @param itemMeta The {@link ItemMeta} to apply the map view to.
60140
*/
61-
public void applyMapMeta(@Nonnull final ItemMeta itemMeta) {
141+
public void applyMapMeta(final ItemStack item, @Nonnull final ItemMeta itemMeta) {
62142
if (!(itemMeta instanceof MapMeta)) return;
63143
final MapMeta mapMeta = (MapMeta) itemMeta;
144+
145+
if (ItemCreator.getServerVersion() < 13.0F) {
146+
final BuildMapView mapViewBuilder = getMapViewBuilder();
147+
short durability = mapViewBuilder == null ? -1 : (short) mapViewBuilder.getId();
148+
if (durability >= 0) {
149+
item.setDurability(durability);
150+
}
151+
return;
152+
}
153+
64154
if (mapView != null) {
65155
MapView builtMap = mapView.build();
66156
mapMeta.setMapView(builtMap);
67157
}
68158
}
159+
160+
69161
}

Item Creator/src/main/java/org/broken/arrow/library/itemcreator/meta/MetaHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ public void applyMeta(@Nonnull final ItemStack itemStack, @Nullable final ItemMe
295295
shieldMeta.applyShieldBanner(itemMeta);
296296
}
297297
if (mapMeta != null) {
298-
mapMeta.applyMapMeta(itemMeta);
298+
mapMeta.applyMapMeta(itemStack, itemMeta);
299299
}
300300
if(this.bookMeta != null)
301301
this.bookMeta.applyBookMenta(itemMeta);

Item Creator/src/main/java/org/broken/arrow/library/itemcreator/meta/map/BuildMapView.java

Lines changed: 48 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.broken.arrow.library.itemcreator.meta.map;
22

33

4+
import org.broken.arrow.library.itemcreator.ItemCreator;
45
import org.bukkit.Bukkit;
56
import org.bukkit.World;
67
import org.bukkit.inventory.meta.MapMeta;
@@ -9,6 +10,7 @@
910

1011
import javax.annotation.Nonnull;
1112
import javax.annotation.Nullable;
13+
import java.lang.reflect.Method;
1214
import java.util.ArrayList;
1315
import java.util.HashSet;
1416
import java.util.List;
@@ -30,16 +32,13 @@ public class BuildMapView {
3032
private final World world;
3133
private final Set<MapRendererData> renderers = new HashSet<>();
3234

33-
private MapView.Scale scale = MapView.Scale.NORMAL;
34-
3535
private boolean locked;
3636
private boolean unlimited;
3737
private boolean trackingPosition;
3838
private boolean virtual;
3939

4040
private int mapId;
41-
private int z;
42-
private int x;
41+
4342

4443
/**
4544
* Constructs a new {@code BuildMapView} for the given {@link World},
@@ -50,11 +49,7 @@ public class BuildMapView {
5049
public BuildMapView(@Nonnull final World world) {
5150
this.mapView = Bukkit.createMap(world);
5251
this.virtual = mapView.isVirtual();
53-
try {
54-
this.mapId = mapView.getId();
55-
} catch (NoSuchMethodError e) {
56-
e.printStackTrace();
57-
}
52+
this.mapId = retrieveMapId(mapView);
5853
this.world = mapView.getWorld();
5954
}
6055

@@ -67,7 +62,7 @@ public BuildMapView(@Nonnull final World world) {
6762
public BuildMapView(@Nonnull final MapView mapView) {
6863
this.mapView = mapView;
6964
this.virtual = mapView.isVirtual();
70-
this.world = mapView.getWorld(); // nullable, but can be used
65+
this.world = mapView.getWorld();
7166
}
7267

7368
/**
@@ -96,7 +91,10 @@ public boolean isVirtual() {
9691
*/
9792
@Nonnull
9893
public MapView.Scale getScale() {
99-
return this.scale;
94+
final MapView.Scale mapViewScale = mapView.getScale();
95+
if (mapViewScale == null)
96+
return MapView.Scale.NORMAL;
97+
return mapViewScale;
10098
}
10199

102100
/**
@@ -105,7 +103,7 @@ public MapView.Scale getScale() {
105103
* @param scale The scale to set.
106104
*/
107105
public void setScale(@Nonnull MapView.Scale scale) {
108-
this.scale = scale;
106+
this.mapView.setScale(scale);
109107
}
110108

111109
/**
@@ -114,7 +112,7 @@ public void setScale(@Nonnull MapView.Scale scale) {
114112
* @return The center X position.
115113
*/
116114
public int getCenterX() {
117-
return this.x;
115+
return this.mapView.getCenterX();
118116
}
119117

120118
/**
@@ -123,7 +121,7 @@ public int getCenterX() {
123121
* @return The center Z position.
124122
*/
125123
public int getCenterZ() {
126-
return this.z;
124+
return this.mapView.getCenterZ();
127125
}
128126

129127
/**
@@ -132,7 +130,7 @@ public int getCenterZ() {
132130
* @param x The center X position.
133131
*/
134132
public void setCenterX(int x) {
135-
this.x = x;
133+
this.mapView.setCenterX(x);
136134
}
137135

138136
/**
@@ -141,7 +139,7 @@ public void setCenterX(int x) {
141139
* @param z The center Z position.
142140
*/
143141
public void setCenterZ(int z) {
144-
this.z = z;
142+
this.mapView.setCenterZ(z);
145143
}
146144

147145
/**
@@ -153,7 +151,7 @@ public void setCenterZ(int z) {
153151
*/
154152
@Nullable
155153
public World getWorld() {
156-
return this.world;
154+
return this.mapView.getWorld();
157155
}
158156

159157
/**
@@ -286,24 +284,17 @@ public MapView build() {
286284
if (world == null)
287285
throw new IllegalStateException("World must be set before building MapView.");
288286

289-
mapView.setCenterX(x);
290-
mapView.setCenterZ(z);
291-
mapView.setScale(scale);
292-
try {
287+
if (mapView.getScale() == null)
288+
mapView.setScale(this.getScale());
289+
290+
if (ItemCreator.getServerVersion() > 13.2F) {
293291
mapView.setTrackingPosition(trackingPosition);
294-
} catch (NoSuchMethodError e) {
295-
e.printStackTrace();
292+
mapView.setLocked(locked);
296293
}
297-
try {
294+
295+
if (ItemCreator.getServerVersion() > 10.2F)
298296
mapView.setUnlimitedTracking(unlimited);
299-
} catch (NoSuchMethodError e) {
300-
e.printStackTrace();
301-
}
302-
try {
303-
mapView.setLocked(locked);
304-
} catch (NoSuchMethodError e) {
305-
e.printStackTrace();
306-
}
297+
307298
for (MapRenderer renderer : mapView.getRenderers()) {
308299
mapView.removeRenderer(renderer);
309300
}
@@ -316,5 +307,30 @@ public MapView build() {
316307
}
317308
return mapView;
318309
}
310+
311+
/**
312+
* Retrieves the map ID from a {@link MapView}, handling version differences between
313+
* legacy (≤ 1.12.2) and modern (≥ 1.13) Spigot APIs.
314+
* <p>
315+
* In versions 1.12.2 and below, {@code MapView#getId()} returns a {@code short}.
316+
* In versions 1.13 and above, it returns an {@code int}. This method ensures compatibility
317+
* across both by using reflection only when necessary.
318+
* </p>
319+
*
320+
* @param mapView the {@link MapView} instance from which to retrieve the map ID
321+
* @return the map ID as an {@code int}, or {@code -1} if retrieval failed
322+
*/
323+
public static int retrieveMapId(@Nonnull final MapView mapView) {
324+
if (ItemCreator.getServerVersion() > 12.2F)
325+
return mapView.getId();
326+
try {
327+
Method getId = mapView.getClass().getMethod("getId");
328+
Object result = getId.invoke(mapView);
329+
return ((Number) result).intValue();
330+
} catch (Exception e) {
331+
e.printStackTrace();
332+
return -1;
333+
}
334+
}
319335
}
320336

0 commit comments

Comments
 (0)