Repository: runelite/runelite (java)

Issue:
gpu: unrelated map in waterbirth dungeon is not hidden
### Type

Incorrect behavior

### Operating System

None

### OS Version

_No response_

### Bug description

The region should probably be added to regions.txt as it looks quite out of place

![image](https://github.com/runelite/runelite/assets/7929021/4e31944a-24ce-4ca3-b910-2067d24e821a)

![image](https://github.com/runelite/runelite/assets/7929021/f4d84124-b3a6-423c-8f81-6f73f85dc663)

It looks to be the canoe zone.
![image](https://github.com/runelite/runelite/assets/7929021/ca52afe3-bf84-4ec1-a68d-c21e6d2e720e)

### Screenshots or videos

_No response_

### RuneLite version

1.10.29-snapshot (master, commit 14d96670)

### Logs

_No response_

Pull request description:
gpu: regions: add waterbirth dungeon
Adds Waterbirth dungeon to GPU regions. Closes #17819. All testing by Yves. 

Patch:
diff --git a/cache/src/main/java/net/runelite/cache/definitions/WorldMapDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/WorldMapDefinition.java
new file mode 100644
index 000000000..9f0f80c75
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/definitions/WorldMapDefinition.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2017, Adam <Adam@sigterm.info>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.cache.definitions;
+
+import java.util.List;
+import lombok.Data;
+import net.runelite.cache.region.Position;
+
+@Data
+public class WorldMapDefinition
+{
+	public String safeName;
+	public String name;
+	public int emptyTileColor;
+	public int backgroundColor;
+	public int defaultZoom;
+	public int fileId;
+	public boolean isSurface;
+	public List<WorldMapTypeBase> regionList;
+	public Position position;
+}
diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapLoader.java
new file mode 100644
index 000000000..118476a4c
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapLoader.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2017, Adam <Adam@sigterm.info>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.cache.definitions.loaders;
+
+import java.util.LinkedList;
+import net.runelite.cache.definitions.WorldMapDefinition;
+import net.runelite.cache.definitions.WorldMapType0;
+import net.runelite.cache.definitions.WorldMapType1;
+import net.runelite.cache.definitions.WorldMapType2;
+import net.runelite.cache.definitions.WorldMapType3;
+import net.runelite.cache.definitions.WorldMapTypeBase;
+import net.runelite.cache.io.InputStream;
+import net.runelite.cache.region.Position;
+
+public class WorldMapLoader
+{
+	public WorldMapDefinition load(byte[] b, int fileId)
+	{
+		WorldMapDefinition def = new WorldMapDefinition();
+		InputStream in = new InputStream(b);
+
+		def.fileId = fileId;
+		def.safeName = in.readString();
+		def.name = in.readString();
+
+		int packedPos = in.readInt();
+		def.position = Position.fromPacked(packedPos);
+
+		def.emptyTileColor = in.readInt();
+		def.backgroundColor = in.readInt();
+		in.readUnsignedByte();
+		def.isSurface = in.readUnsignedByte() == 1;
+		def.defaultZoom = in.readUnsignedByte();
+		int var3 = in.readUnsignedByte();
+		def.regionList = new LinkedList<>();
+
+		for (int var4 = 0; var4 < var3; ++var4)
+		{
+			def.regionList.add(this.loadType(in));
+		}
+
+		return def;
+	}
+
+	private WorldMapTypeBase loadType(InputStream var1)
+	{
+		int var2 = var1.readUnsignedByte();
+		WorldMapTypeBase base;
+		switch (var2)
+		{
+			case 0:
+				// type 1
+				base = load1(var1);
+				break;
+			case 1:
+				// type 2
+				base = load2(var1);
+				break;
+			case 2:
+				// type 3
+				base = load3(var1);
+				break;
+			case 3:
+				// type 0
+				base = load0(var1);
+				break;
+			default:
+				throw new IllegalStateException();
+		}
+		return base;
+	}
+
+	private WorldMapTypeBase load0(InputStream in)
+	{
+		WorldMapType0 wm = new WorldMapType0();
+
+		wm.plane = in.readUnsignedByte();
+		wm.numberOfPlanes = in.readUnsignedByte();
+		wm.xLow = in.readUnsignedShort();
+		wm.chunk_xLow = in.readUnsignedByte();
+		wm.yLow = in.readUnsignedShort();
+		wm.chunk_yLow = in.readUnsignedByte();
+		wm.xHigh = in.readUnsignedShort();
+		wm.chunk_xHigh = in.readUnsignedByte();
+		wm.yHigh = in.readUnsignedShort();
+		wm.chunk_yHigh = in.readUnsignedByte();
+
+		return wm;
+	}
+
+	private WorldMapTypeBase load1(InputStream in)
+	{
+		WorldMapType1 wm = new WorldMapType1();
+
+		wm.plane = in.readUnsignedByte();
+		wm.numberOfPlanes = in.readUnsignedByte();
+		wm.xLowerLeft = in.readUnsignedShort();
+		wm.yLowerLeft = in.readUnsignedShort();
+		wm.xLowerRight = in.readUnsignedShort();
+		wm.yUpperLeft = in.readUnsignedShort();
+		wm.xUpperLeft = in.readUnsignedShort();
+		wm.yLowerRight = in.readUnsignedShort();
+		wm.xUpperRight = in.readUnsignedShort();
+		wm.yUpperRight = in.readUnsignedShort();
+
+		return wm;
+	}
+
+	private WorldMapTypeBase load2(InputStream in)
+	{
+		WorldMapType2 wm = new WorldMapType2();
+
+		wm.plane = in.readUnsignedByte();
+		wm.numberOfPlanes = in.readUnsignedByte();
+		wm.xLow = in.readUnsignedShort();
+		wm.yLow = in.readUnsignedShort();
+		wm.xHigh = in.readUnsignedShort();
+		wm.yHigh = in.readUnsignedShort();
+
+		return wm;
+	}
+
+	private WorldMapTypeBase load3(InputStream in)
+	{
+		WorldMapType3 wm = new WorldMapType3();
+
+		wm.oldPlane = in.readUnsignedByte();
+		wm.numberOfPlanes = in.readUnsignedByte();
+		wm.oldX = in.readUnsignedShort();
+		wm.chunk_oldXLow = in.readUnsignedByte();
+		wm.chunk_oldXHigh = in.readUnsignedByte();
+		wm.oldY = in.readUnsignedShort();
+		wm.chunk_oldYLow = in.readUnsignedByte();
+		wm.chunk_oldYHigh = in.readUnsignedByte();
+		wm.newX = in.readUnsignedShort();
+		wm.chunk_newXLow = in.readUnsignedByte();
+		wm.chunk_newXHigh = in.readUnsignedByte();
+		wm.newY = in.readUnsignedShort();
+		wm.chunk_newYLow = in.readUnsignedByte();
+		wm.chunk_newYHigh = in.readUnsignedByte();
+
+		return wm;
+	}
+}
diff --git a/runelite-api/src/main/java/net/runelite/api/WorldType.java b/runelite-api/src/main/java/net/runelite/api/WorldType.java
index 5cd26c4b6..b4ca3b3dc 100644
--- a/runelite-api/src/main/java/net/runelite/api/WorldType.java
+++ b/runelite-api/src/main/java/net/runelite/api/WorldType.java
@@ -70,6 +70,8 @@ public enum WorldType
 	 * Beta world.
 	 */
 	BETA_WORLD(1 << 16),
+	LEGACY_ONLY(1 << 22),
+	EOC_ONLY(1 << 23),
 	/**
 	 * Beta worlds without profiles that are saved.
 	 */
diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml
index c73318eca..12b876a52 100644
--- a/runelite-client/pom.xml
+++ b/runelite-client/pom.xml
@@ -253,7 +253,7 @@
 		<dependency>
 			<groupId>net.runelite.arn</groupId>
 			<artifactId>http-api</artifactId>
-			<version>1.2.12</version>
+			<version>1.2.16</version>
 		</dependency>
 		<dependency>
 			<groupId>net.runelite</groupId>
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterPlugin.java
index c93c0dd24..b5da6850a 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterPlugin.java
@@ -216,6 +216,7 @@ public class ChatFilterPlugin extends Plugin
 				break;
 			case GAMEMESSAGE:
 			case ENGINE:
+			case FRIENDSCHATNOTIFICATION:
 			case ITEM_EXAMINE:
 			case NPC_EXAMINE:
 			case OBJECT_EXAMINE:
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java
index 2021b9fe0..6f0ab3708 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java
@@ -95,7 +95,6 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
 	static final int MAX_TRIANGLE = 6144;
 	static final int SMALL_TRIANGLE_COUNT = 512;
 	private static final int FLAG_SCENE_BUFFER = Integer.MIN_VALUE;
-	private static final int DEFAULT_DISTANCE = 25;
 	static final int MAX_DISTANCE = 184;
 	static final int MAX_FOG_DEPTH = 100;
 	static final int SCENE_OFFSET = (Constants.EXTENDED_SCENE_SIZE - Constants.SCENE_SIZE) / 2; // offset for sxy -> msxy
@@ -1845,8 +1844,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
 
 	private int getDrawDistance()
 	{
-		final int limit = computeMode != ComputeMode.NONE ? MAX_DISTANCE : DEFAULT_DISTANCE;
-		return Ints.constrainToRange(config.drawDistance(), 0, limit);
+		return Ints.constrainToRange(config.drawDistance(), 0, MAX_DISTANCE);
 	}
 
 	private void updateBuffer(@Nonnull GLBuffer glBuffer, int target, @Nonnull IntBuffer data, int usage, long clFlags)
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java
index aa373ab98..5a13eaebf 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java
@@ -635,7 +635,7 @@ public class LootTrackerPlugin extends Plugin
 		final LootTrackerItem[] entries = buildEntries(stack(items));
 		SwingUtilities.invokeLater(() -> panel.add(name, type, combatLevel, entries, amount));
 
-		LootRecord lootRecord = new LootRecord(name, type, metadata, toGameItems(items), Instant.now(), getLootWorldId());
+		LootRecord lootRecord = new LootRecord(name, type, metadata, toGameItems(items), Instant.now(), getLootWorldId(), amount, null);
 		synchronized (queuedLoots)
 		{
 			queuedLoots.add(lootRecord);
@@ -1319,7 +1319,7 @@ public class LootTrackerPlugin extends Plugin
 		{
 			ConfigLoot key = new ConfigLoot(record.getType(), record.getEventId());
 			ConfigLoot loot = map.computeIfAbsent(key, k -> key);
-			loot.kills++;
+			loot.kills += record.getAmount();
 			for (GameItem item : record.getDrops())
 			{
 				loot.add(item.getId(), item.getQty());
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableRow.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableRow.java
index aa63a28e8..ec4ea6088 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableRow.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableRow.java
@@ -269,7 +269,8 @@ class WorldTableRow extends JPanel
 		{
 			activityField.setForeground(SEASONAL_WORLD);
 		}
-		else if (types.contains(WorldType.NOSAVE_MODE) || types.contains(WorldType.BETA_WORLD))
+		else if (types.contains(WorldType.NOSAVE_MODE) || types.contains(WorldType.BETA_WORLD)
+			|| types.contains(WorldType.LEGACY_ONLY)) // sometimes used for arbitrarily restricted worlds
 		{
 			activityField.setForeground(BETA_WORLD);
 		}
diff --git a/runelite-client/src/main/java/net/runelite/client/util/WorldUtil.java b/runelite-client/src/main/java/net/runelite/client/util/WorldUtil.java
index 0841fe07b..bdb1abdd3 100644
--- a/runelite-client/src/main/java/net/runelite/client/util/WorldUtil.java
+++ b/runelite-client/src/main/java/net/runelite/client/util/WorldUtil.java
@@ -73,6 +73,10 @@ public class WorldUtil
 				return WorldType.LAST_MAN_STANDING;
 			case BETA_WORLD:
 				return WorldType.BETA_WORLD;
+			case LEGACY_ONLY:
+				return WorldType.LEGACY_ONLY;
+			case EOC_ONLY:
+				return WorldType.EOC_ONLY;
 			case NOSAVE_MODE:
 				return WorldType.NOSAVE_MODE;
 			case TOURNAMENT:
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/regions/regions.txt b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/regions/regions.txt
index 68de1a27b..640669501 100644
--- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/regions/regions.txt
+++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/regions/regions.txt
@@ -238,4 +238,8 @@ r 45 71
 
 // pyramid plunder
 n
-r 30 69
\ No newline at end of file
+r 30 69
+
+// waterbirth dungeon
+n
+R 28 68 30 68
\ No newline at end of file


Test patch:
diff --git a/cache/src/test/java/net/runelite/cache/WorldMapDumperTest.java b/cache/src/test/java/net/runelite/cache/WorldMapDumperTest.java
new file mode 100644
index 000000000..a3f25a3e6
--- /dev/null
+++ b/cache/src/test/java/net/runelite/cache/WorldMapDumperTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2017, Adam <Adam@sigterm.info>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.cache;
+
+import com.google.common.io.Files;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import net.runelite.cache.definitions.WorldMapDefinition;
+import net.runelite.cache.definitions.loaders.WorldMapLoader;
+import net.runelite.cache.fs.Archive;
+import net.runelite.cache.fs.ArchiveFiles;
+import net.runelite.cache.fs.FSFile;
+import net.runelite.cache.fs.Index;
+import net.runelite.cache.fs.Storage;
+import net.runelite.cache.fs.Store;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class WorldMapDumperTest
+{
+	private static final Logger logger = LoggerFactory.getLogger(WorldMapDumperTest.class);
+
+	@Rule
+	public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
+
+	private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+	@Test
+	@Ignore
+	public void extract() throws IOException
+	{
+		File base = StoreLocation.LOCATION,
+			outDir = folder.newFolder();
+
+		int count = 0;
+
+		try (Store store = new Store(base))
+		{
+			store.load();
+
+			Storage storage = store.getStorage();
+			Index index = store.getIndex(IndexType.WORLDMAP);
+			Archive archive = index.findArchiveByName("details");
+
+			byte[] archiveData = storage.loadArchive(archive);
+			ArchiveFiles files = archive.getFiles(archiveData);
+
+			for (FSFile file : files.getFiles())
+			{
+				WorldMapLoader loader = new WorldMapLoader();
+				WorldMapDefinition def = loader.load(file.getContents(), file.getFileId());
+
+				Files.asCharSink(new File(outDir, file.getFileId() + ".json"), Charset.defaultCharset()).write(gson.toJson(def));
+				++count;
+			}
+		}
+
+		logger.info("Dumped {} world map data to {}", count, outDir);
+	}
+}