--- a/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
+++ b/net/minecraft/world/level/saveddata/maps/MapItemSavedData.java
@@ -50,7 +_,7 @@
     private static final String FRAME_PREFIX = "frame-";
     public static final Codec<MapItemSavedData> CODEC = RecordCodecBuilder.create(
         instance -> instance.group(
-                Level.RESOURCE_KEY_CODEC.fieldOf("dimension").forGetter(mapItemSavedData -> mapItemSavedData.dimension),
+                createUUIDBackedDimensionKeyCodec().forGetter(MapItemSavedData::packUUIDBackedDimension), // Paper - store target world by uuid in addition to dimension
                 Codec.INT.fieldOf("xCenter").forGetter(mapItemSavedData -> mapItemSavedData.centerX),
                 Codec.INT.fieldOf("zCenter").forGetter(mapItemSavedData -> mapItemSavedData.centerZ),
                 Codec.BYTE.optionalFieldOf("scale", (byte)0).forGetter(mapItemSavedData -> mapItemSavedData.scale),
@@ -66,21 +_,29 @@
             )
             .apply(instance, MapItemSavedData::new)
     );
-    public final int centerX;
-    public final int centerZ;
-    public final ResourceKey<Level> dimension;
-    private final boolean trackingPosition;
-    private final boolean unlimitedTracking;
-    public final byte scale;
+    public int centerX;
+    public int centerZ;
+    public ResourceKey<Level> dimension;
+    public boolean trackingPosition;
+    public boolean unlimitedTracking;
+    public byte scale;
     public byte[] colors = new byte[16384];
-    public final boolean locked;
-    private final List<MapItemSavedData.HoldingPlayer> carriedBy = Lists.newArrayList();
-    private final Map<Player, MapItemSavedData.HoldingPlayer> carriedByPlayers = Maps.newHashMap();
+    public boolean locked;
+    private final org.bukkit.craftbukkit.map.RenderData vanillaRender = new org.bukkit.craftbukkit.map.RenderData(); // Paper - Use Vanilla map renderer when possible
+    public final List<MapItemSavedData.HoldingPlayer> carriedBy = Lists.newArrayList();
+    public final Map<Player, MapItemSavedData.HoldingPlayer> carriedByPlayers = Maps.newHashMap();
     private final Map<String, MapBanner> bannerMarkers = Maps.newHashMap();
-    final Map<String, MapDecoration> decorations = Maps.newLinkedHashMap();
+    public final Map<String, MapDecoration> decorations = Maps.newLinkedHashMap();
     private final Map<String, MapFrame> frameMarkers = Maps.newHashMap();
     private int trackedDecorationCount;
 
+    // CraftBukkit start
+    public final org.bukkit.craftbukkit.map.CraftMapView mapView;
+    private final org.bukkit.craftbukkit.CraftServer server;
+    public java.util.UUID uniqueId;
+    public MapId id;
+    // CraftBukkit end
+
     public static SavedDataType<MapItemSavedData> type(MapId mapId) {
         return new SavedDataType<>(mapId.key(), () -> {
             throw new IllegalStateException("Should never create an empty map saved data");
@@ -95,7 +_,29 @@
         this.trackingPosition = trackingPosition;
         this.unlimitedTracking = unlimitedTracking;
         this.locked = locked;
-    }
+        // CraftBukkit start
+        this.mapView = new org.bukkit.craftbukkit.map.CraftMapView(this);
+        this.server = (org.bukkit.craftbukkit.CraftServer) org.bukkit.Bukkit.getServer();
+        this.vanillaRender.buffer = colors; // Paper - Use Vanilla map renderer when possible
+        // CraftBukkit end
+    }
+
+    // Paper start - store target world by uuid in addition to dimension
+    private MapItemSavedData(
+        UUIDBackedDimension dimension,
+        int x,
+        int z,
+        byte scale,
+        ByteBuffer colors,
+        boolean trackingPosition,
+        boolean unlimitedTracking,
+        boolean locked,
+        List<MapBanner> banners,
+        List<MapFrame> frames
+    ) {
+        this(dimension.resolveOrThrow(), x, z, scale, colors, trackingPosition, unlimitedTracking, locked, banners, frames);
+    }
+    // Paper end - store target world by uuid in addition to dimension
 
     private MapItemSavedData(
         ResourceKey<Level> dimension,
@@ -127,6 +_,8 @@
                 MapDecorationTypes.FRAME, null, getFrameKey(mapFrame.entityId()), mapFrame.pos().getX(), mapFrame.pos().getZ(), mapFrame.rotation(), null
             );
         }
+
+        this.vanillaRender.buffer = colors.array(); // Paper - Use Vanilla map renderer when possible
     }
 
     public static MapItemSavedData createFresh(
@@ -204,6 +_,7 @@
             }
 
             MapFrame mapFrame1 = new MapFrame(pos, frame.getDirection().get2DDataValue() * 90, frame.getId());
+            if (this.decorations.size() < player.level().paperConfig().maps.itemFrameCursorLimit) { // Paper - Limit item frame cursors on maps
             this.addDecoration(
                 MapDecorationTypes.FRAME, player.level(), getFrameKey(frame.getId()), pos.getX(), pos.getZ(), frame.getDirection().get2DDataValue() * 90, null
             );
@@ -211,6 +_,7 @@
             if (!mapFrame1.equals(mapFrame2)) {
                 this.setDirty();
             }
+            } // Paper - Limit item frame cursors on maps
         }
 
         MapDecorations mapDecorations = mapStack.getOrDefault(DataComponents.MAP_DECORATIONS, MapDecorations.EMPTY);
@@ -241,7 +_,7 @@
             this.trackedDecorationCount--;
         }
 
-        this.setDecorationsDirty();
+        if (mapDecoration != null) this.setDecorationsDirty(); // Paper - only mark dirty if a change occurs
     }
 
     public static void addTargetDecoration(ItemStack stack, BlockPos pos, String type, Holder<MapDecorationType> mapDecorationType) {
@@ -353,15 +_,20 @@
         return holdingPlayer == null ? null : holdingPlayer.nextUpdatePacket(mapId);
     }
 
-    private void setColorsDirty(int x, int z) {
-        this.setDirty();
+    public void setColorsDirty(int x, int z) {
+    // Paper start - Fix unnecessary map data saves
+        this.setColorsDirty(x, z, true);
+    }
+    public void setColorsDirty(int x, int z, boolean markFileDirty) {
+        if (markFileDirty) this.setDirty();
+    // Paper end - Fix unnecessary map data saves
 
         for (MapItemSavedData.HoldingPlayer holdingPlayer : this.carriedBy) {
             holdingPlayer.markColorsDirty(x, z);
         }
     }
 
-    private void setDecorationsDirty() {
+    public void setDecorationsDirty() {
         this.carriedBy.forEach(MapItemSavedData.HoldingPlayer::markDecorationsDirty);
     }
 
@@ -395,7 +_,7 @@
                 return true;
             }
 
-            if (!this.isTrackedCountOverLimit(256)) {
+            if (!this.isTrackedCountOverLimit(((Level) accessor).paperConfig().maps.itemFrameCursorLimit)) { // Paper - Limit item frame cursors on maps
                 this.bannerMarkers.put(mapBanner.getId(), mapBanner);
                 this.addDecoration(mapBanner.getDecoration(), accessor, mapBanner.getId(), d, d1, 180.0, mapBanner.name().orElse(null));
                 this.setDirty();
@@ -497,7 +_,7 @@
             this.player = player;
         }
 
-        private MapItemSavedData.MapPatch createPatch() {
+        private MapItemSavedData.MapPatch createPatch(byte[] buffer) { // CraftBukkit
             int i = this.minDirtyX;
             int i1 = this.minDirtyY;
             int i2 = this.maxDirtyX + 1 - this.minDirtyX;
@@ -506,7 +_,7 @@
 
             for (int i4 = 0; i4 < i2; i4++) {
                 for (int i5 = 0; i5 < i3; i5++) {
-                    bytes[i4 + i5 * i2] = MapItemSavedData.this.colors[i + i4 + (i1 + i5) * 128];
+                    bytes[i4 + i5 * i2] = buffer[i + i4 + (i1 + i5) * 128]; // CraftBukkit
                 }
             }
 
@@ -516,17 +_,38 @@
         @Nullable
         Packet<?> nextUpdatePacket(MapId mapId) {
             MapItemSavedData.MapPatch mapPatch;
+            // Paper start
+            if (!this.dirtyData && this.tick % 5 != 0) {
+                // this won't end up sending, so don't render it!
+                this.tick++;
+                return null;
+            }
+
+            final boolean vanillaMaps = this.shouldUseVanillaMap();
+            // Use Vanilla map renderer when possible - much simpler/faster than the CB rendering
+            org.bukkit.craftbukkit.map.RenderData render = !vanillaMaps ? MapItemSavedData.this.mapView.render((org.bukkit.craftbukkit.entity.CraftPlayer) this.player.getBukkitEntity()) : MapItemSavedData.this.vanillaRender;
+            // Paper end
             if (this.dirtyData) {
                 this.dirtyData = false;
-                mapPatch = this.createPatch();
+                mapPatch = this.createPatch(render.buffer); // CraftBukkit
             } else {
                 mapPatch = null;
             }
 
             Collection<MapDecoration> collection;
-            if (this.dirtyDecorations && this.tick++ % 5 == 0) {
+            if ((!vanillaMaps || this.dirtyDecorations) && this.tick++ % 5 == 0) { // Paper - bypass dirtyDecorations for custom maps
                 this.dirtyDecorations = false;
-                collection = MapItemSavedData.this.decorations.values();
+                // CraftBukkit start
+                Collection<MapDecoration> icons = new java.util.ArrayList<>();
+                if (vanillaMaps) this.addSeenPlayers(icons); // Paper
+
+                for (org.bukkit.map.MapCursor cursor : render.cursors) {
+                    if (cursor.isVisible()) {
+                        icons.add(new MapDecoration(org.bukkit.craftbukkit.map.CraftMapCursor.CraftType.bukkitToMinecraftHolder(cursor.getType()), cursor.getX(), cursor.getY(), cursor.getDirection(), Optional.ofNullable(io.papermc.paper.adventure.PaperAdventure.asVanilla(cursor.caption()))));
+                    }
+                }
+                collection = icons;
+                // CraftBukkit end
             } else {
                 collection = null;
             }
@@ -554,6 +_,23 @@
         private void markDecorationsDirty() {
             this.dirtyDecorations = true;
         }
+
+        // Paper start
+        private void addSeenPlayers(java.util.Collection<MapDecoration> icons) {
+            org.bukkit.entity.Player player = (org.bukkit.entity.Player) this.player.getBukkitEntity();
+            MapItemSavedData.this.decorations.forEach((name, mapIcon) -> {
+                // If this cursor is for a player check visibility with vanish system
+                org.bukkit.entity.Player other = org.bukkit.Bukkit.getPlayerExact(name); // Spigot
+                if (other == null || player.canSee(other)) {
+                    icons.add(mapIcon);
+                }
+            });
+        }
+
+        private boolean shouldUseVanillaMap() {
+            return mapView.getRenderers().size() == 1 && mapView.getRenderers().getFirst().getClass() == org.bukkit.craftbukkit.map.CraftMapRenderer.class;
+        }
+        // Paper end
     }
 
     record MapDecorationLocation(Holder<MapDecorationType> type, byte x, byte y, byte rot) {
@@ -598,4 +_,71 @@
             }
         }
     }
+
+    // Paper start - store target world by uuid in addition to dimension
+    record UUIDAndError(java.util.UUID uuid, String faultyDimension) {
+
+    }
+    record UUIDBackedDimension(@Nullable ResourceKey<Level> resourceKey, @Nullable UUIDAndError uuid) {
+        public UUIDBackedDimension(final @org.jetbrains.annotations.NotNull ResourceKey<Level> resourceKey) {
+            this(resourceKey, null);
+        }
+        public UUIDBackedDimension {
+            com.google.common.base.Preconditions.checkArgument(resourceKey != null || uuid != null, "Created uuid backed dimension with null level and uuid. This is a bug");
+        }
+
+        public @org.jetbrains.annotations.NotNull ResourceKey<Level> resolveOrThrow() {
+            if (resourceKey != null) return resourceKey;
+
+            final org.bukkit.World worldByUUID = org.bukkit.Bukkit.getWorld(uuid.uuid());
+            if (worldByUUID != null) return ((org.bukkit.craftbukkit.CraftWorld) worldByUUID).getHandle().dimension();
+
+            throw new IllegalArgumentException("Invalid dimension " + uuid.faultyDimension() + " and unknown world uuid " + uuid.uuid);
+        }
+    }
+
+    private UUIDBackedDimension packUUIDBackedDimension() {
+        final net.minecraft.server.level.ServerLevel mappedLevel = net.minecraft.server.MinecraftServer.getServer().getLevel(this.dimension);
+        return new UUIDBackedDimension(this.dimension, mappedLevel == null ? null : new UUIDAndError(mappedLevel.uuid, ""));
+    }
+
+    private static com.mojang.serialization.MapCodec<UUIDBackedDimension> createUUIDBackedDimensionKeyCodec() {
+        return new com.mojang.serialization.MapCodec<>() {
+            @Override
+            public <T> java.util.stream.Stream<T> keys(final com.mojang.serialization.DynamicOps<T> ops) {
+                return java.util.stream.Stream.of("dimension", "UUIDLeast", "UUIDMost").map(ops::createString);
+            }
+
+            @Override
+            public <T> com.mojang.serialization.DataResult<UUIDBackedDimension> decode(final com.mojang.serialization.DynamicOps<T> ops,
+                                                                                       final com.mojang.serialization.MapLike<T> input) {
+                final com.mojang.serialization.DataResult<UUIDBackedDimension> foundDimension = Level.RESOURCE_KEY_CODEC.decode(ops, input.get("dimension"))
+                    .map(Pair::getFirst)
+                    .map(UUIDBackedDimension::new); // Do not pack uuid when reading as the level itself might reference an unloaded world. UUID lookup would be faulty + should be re-generated when written.
+                if (foundDimension.isSuccess()) return foundDimension;
+
+                // Fallback attempt at parsing the uuid
+                final com.mojang.serialization.DataResult<UUIDBackedDimension> fromUUID = Codec.LONG.decode(ops, input.get("UUIDMost")).map(Pair::getFirst).apply2(
+                    java.util.UUID::new,
+                    Codec.LONG.decode(ops, input.get("UUIDLeast")).map(Pair::getFirst)
+                ).map(uuid -> new UUIDBackedDimension(null, new UUIDAndError(uuid, String.valueOf(input.get("dimension")))));
+                if (fromUUID.isSuccess()) return fromUUID;
+
+                return foundDimension; // Return the found dimension instead, it's error is more "accurate" over the bukkit added uuids.
+            }
+
+            @Override
+            public <T> com.mojang.serialization.RecordBuilder<T> encode(final UUIDBackedDimension input,
+                                                                        final com.mojang.serialization.DynamicOps<T> ops,
+                                                                        final com.mojang.serialization.RecordBuilder<T> prefix) {
+                prefix.add("dimension", input.resourceKey(), Level.RESOURCE_KEY_CODEC);
+                if (input.uuid != null) {
+                    prefix.add("UUIDMost", input.uuid.uuid().getMostSignificantBits(), Codec.LONG);
+                    prefix.add("UUIDLeast", input.uuid.uuid().getLeastSignificantBits(), Codec.LONG);
+                }
+                return prefix;
+            }
+        };
+    }
+    // Paper end - store target world by uuid in addition to dimension
 }
