From f13bf2c9bd390b6933706d081292926eb53d5550 Mon Sep 17 00:00:00 2001
From: Tadeas Drab <tadeas.drab@student.tuke.sk>
Date: Sat, 16 Dec 2023 17:05:25 +0100
Subject: [PATCH 1/5] Issue 2393 - Added configuration and updated getHomeLimit
 for user incorporating world limit homes and world group home limits

---
 .../com/earth2me/essentials/ISettings.java    | 14 +++
 .../com/earth2me/essentials/Settings.java     | 87 ++++++++++++++++++-
 Essentials/src/main/resources/config.yml      | 32 +++++++
 3 files changed, 130 insertions(+), 3 deletions(-)

diff --git a/Essentials/src/main/java/com/earth2me/essentials/ISettings.java b/Essentials/src/main/java/com/earth2me/essentials/ISettings.java
index ae7814153c8..749edc19897 100644
--- a/Essentials/src/main/java/com/earth2me/essentials/ISettings.java
+++ b/Essentials/src/main/java/com/earth2me/essentials/ISettings.java
@@ -105,8 +105,22 @@ public interface ISettings extends IConf {
 
     Set getMultipleHomes();
 
+    Set getHomesPerWorld();
+
+    Set getHomesPerWorldGroup();
+
     int getHomeLimit(String set);
 
+    int getWorldHomeLimit(String set);
+
+    int getWorldGroupHomeLimit(String set);
+
+    Set getWorldGroupHomeList(String set);
+
+    boolean isHomeLimitPerWorldEnabled();
+
+    boolean isHomeLimitPerWorldGroupEnabled();
+
     int getHomeLimit(User user);
 
     int getSpawnMobLimit();
diff --git a/Essentials/src/main/java/com/earth2me/essentials/Settings.java b/Essentials/src/main/java/com/earth2me/essentials/Settings.java
index 628271bc969..7c81ab210f7 100644
--- a/Essentials/src/main/java/com/earth2me/essentials/Settings.java
+++ b/Essentials/src/main/java/com/earth2me/essentials/Settings.java
@@ -35,13 +35,15 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
+import java.util.Objects;
+import java.util.Map.Entry;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Predicate;
 import java.util.logging.Level;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
+import java.util.stream.Collectors;
 
 import static com.earth2me.essentials.I18n.tl;
 
@@ -169,13 +171,28 @@ public Set<String> getMultipleHomes() {
         return section == null ? null : ConfigurateUtil.getKeys(section);
     }
 
+    @Override
+    public Set<String> getHomesPerWorld() {
+        final CommentedConfigurationNode section = config.getSection("homes-per-world");
+        return section == null ? null : ConfigurateUtil.getKeys(section);
+    }
+
+    @Override
+    public Set<String> getHomesPerWorldGroup() {
+        final CommentedConfigurationNode section = config.getSection("homes-per-world-group");
+        return section == null ? null : ConfigurateUtil.getKeys(section);
+    }
+
     @Override
     public int getHomeLimit(final User user) {
         int limit = 1;
-        if (user.isAuthorized("essentials.sethome.multiple")) {
-            limit = getHomeLimit("default");
+        final boolean hasMultipleHomeAuthorization = user.isAuthorized("essentials.sethome.multiple");
+        if (!hasMultipleHomeAuthorization) {
+            return limit;
         }
 
+        limit = getHomeLimit("default");
+
         final Set<String> homeList = getMultipleHomes();
         if (homeList != null) {
             for (final String set : homeList) {
@@ -184,14 +201,78 @@ public int getHomeLimit(final User user) {
                 }
             }
         }
+
+        final boolean homesPerWorldEnabled = isHomeLimitPerWorldEnabled();
+        final boolean homesPerWorldGroupEnabled = isHomeLimitPerWorldGroupEnabled();
+
+        if(!homesPerWorldEnabled)
+            return limit;
+
+        if(homesPerWorldGroupEnabled) {
+            final Set<String> homesPerWorldGroup = getHomesPerWorldGroup();
+            if (homesPerWorldGroup != null) {
+                for (final String set : homesPerWorldGroup) {
+                    if (limit < getWorldGroupHomeLimit(set) && isUserInWorldGroup(user, set)) {
+                        limit = getWorldGroupHomeLimit(set);
+                    }
+                }
+            }
+        }else{
+            final Set<String> homesPerWorld = getHomesPerWorld();
+            if (homesPerWorld != null) {
+                for (final String set : homesPerWorld) {
+                    if (limit < getWorldHomeLimit(set) && isUserInWorld(user, set)) {
+                        limit = getWorldHomeLimit(set);
+                    }
+                }
+            }
+        }
+
         return limit;
     }
 
+    private boolean isUserInWorld(User user, String worldName) {
+        return Objects.requireNonNull(user.getLocation().getWorld()).getName().equalsIgnoreCase(worldName);
+    }
+
+    private boolean isUserInWorldGroup(User user, String worldGroup) {
+        return getWorldGroupHomeList(worldGroup).stream().anyMatch(worldName -> isUserInWorld(user, worldName));
+    }
+
     @Override
     public int getHomeLimit(final String set) {
         return config.getInt("sethome-multiple." + set, config.getInt("sethome-multiple.default", 3));
     }
 
+    @Override
+    public int getWorldHomeLimit(String set) {
+        return config.getInt("homes-per-world." + set, config.getInt("sethome-multiple.default", 3));
+    }
+
+    @Override
+    public int getWorldGroupHomeLimit(String set) {
+        return config.getInt("homes-per-world-group." + set + ".home-limit", config.getInt("sethome-multiple.default", 3));
+    }
+
+    @Override
+    public Set<String> getWorldGroupHomeList(String set) {
+        final String worlds = config.getString("homes-per-world-group." + set + ".worlds", null);
+        if(worlds == null) {
+            return new HashSet<>();
+        }
+        return new HashSet<>(Arrays.stream(worlds.split(",")).collect(Collectors.toList()));
+    }
+
+    @Override
+    public boolean isHomeLimitPerWorldEnabled() {
+        return config.getBoolean("home-limit-per-world", false);
+    }
+
+    @Override
+    public boolean isHomeLimitPerWorldGroupEnabled() {
+        return config.getBoolean("home-limit-per-world-group", false);
+    }
+
     private int _getChatRadius() {
         return config.getInt("chat.radius", config.getInt("chat-radius", 0));
     }
diff --git a/Essentials/src/main/resources/config.yml b/Essentials/src/main/resources/config.yml
index 9ce30622d36..2f41d18f9c8 100644
--- a/Essentials/src/main/resources/config.yml
+++ b/Essentials/src/main/resources/config.yml
@@ -785,6 +785,38 @@ spawn-if-no-home: true
 # Should players be asked to provide confirmation for homes which they attempt to overwrite?
 confirm-home-overwrite: false
 
+# This setting enables to have for each world limit of homes
+# e.g. when enabled, user can have 3 homes in world, 3 in world_nether and 3 in world_the_end.
+home-limit-per-world: false
+
+# When home-limit-per-world is enabled, with configuration sethome-multiple whichever setting has a number higher,
+# it then will be used
+# e.g. if sethome-multiple.vip is 5 and user has permission for this, and user is in world named world_the_end which
+# limit for home is 2, then user will have limit of higher number, therefore 5 homes in that world.
+# To not follow this logic, please remove user permission for that 'home rank'.
+# If world is not present in this configuration, configuration sethome-multiple.default
+# will be used as a home limit for that world.
+homes-per-world:
+  world: 5
+  world_the_end: 2
+
+# For this configuration, previous configuration home-limit-per-world needs to be enabled.
+# This allows you to create 'world groups' which can consist of how many worlds you want
+# and set limit for these worlds, then they will act as one world with their limit.
+# e.g. 'world group' named player-worlds which has worlds 'world' and 'world_nether'
+# which home limit is set to 4. User will have maximum of 4 homes in these worlds total.
+# If world is not present in this configuration, configuration sethome-multiple.default
+# will be used as a home limit for that world.
+home-limit-per-world-group: false
+
+# Name of the world group can be any english letter with spaces that are -
+# Worlds are split by a colon without spaces, as shown in the example: world,world_nether .
+# Worlds in a 'world group' act as one world.
+homes-per-world-group:
+  player-worlds:
+    worlds: world,world_nether
+    home-limit: 4
+
 ############################################################
 # +------------------------------------------------------+ #
 # |                       Economy                        | #

From a85aadb78edfeaeaf6993db37126535f4609c283 Mon Sep 17 00:00:00 2001
From: Tadeas Drab <tadeas.drab@student.tuke.sk>
Date: Sat, 16 Dec 2023 20:02:25 +0100
Subject: [PATCH 2/5] Issue 2393 - Added logic to home teleportation, new
 message

---
 .../com/earth2me/essentials/ISettings.java    |  6 +++---
 .../com/earth2me/essentials/Settings.java     |  2 +-
 .../com/earth2me/essentials/UserData.java     |  7 +++++++
 .../essentials/commands/Commandhome.java      | 19 ++++++++++++++++---
 .../essentials/commands/Commandsethome.java   |  6 +++++-
 .../src/main/resources/messages.properties    |  1 +
 6 files changed, 33 insertions(+), 8 deletions(-)

diff --git a/Essentials/src/main/java/com/earth2me/essentials/ISettings.java b/Essentials/src/main/java/com/earth2me/essentials/ISettings.java
index 749edc19897..1d856adcfd3 100644
--- a/Essentials/src/main/java/com/earth2me/essentials/ISettings.java
+++ b/Essentials/src/main/java/com/earth2me/essentials/ISettings.java
@@ -105,9 +105,9 @@ public interface ISettings extends IConf {
 
     Set getMultipleHomes();
 
-    Set getHomesPerWorld();
+    Set<String>  getHomesPerWorld();
 
-    Set getHomesPerWorldGroup();
+    Set<String> getHomesPerWorldGroup();
 
     int getHomeLimit(String set);
 
@@ -115,7 +115,7 @@ public interface ISettings extends IConf {
 
     int getWorldGroupHomeLimit(String set);
 
-    Set getWorldGroupHomeList(String set);
+    Set<String>  getWorldGroupHomeList(String set);
 
     boolean isHomeLimitPerWorldEnabled();
 
diff --git a/Essentials/src/main/java/com/earth2me/essentials/Settings.java b/Essentials/src/main/java/com/earth2me/essentials/Settings.java
index 7c81ab210f7..ff0243263f2 100644
--- a/Essentials/src/main/java/com/earth2me/essentials/Settings.java
+++ b/Essentials/src/main/java/com/earth2me/essentials/Settings.java
@@ -260,7 +260,7 @@ public Set<String> getWorldGroupHomeList(String set) {
         if(worlds == null) {
             return new HashSet<>();
         }
-        return new HashSet<>(Arrays.stream(worlds.split(",")).collect(Collectors.toList()));
+        return Arrays.stream(worlds.split(",")).collect(Collectors.toSet());
     }
 
     @Override
diff --git a/Essentials/src/main/java/com/earth2me/essentials/UserData.java b/Essentials/src/main/java/com/earth2me/essentials/UserData.java
index c7f1f90ca73..0ae4b87ba48 100644
--- a/Essentials/src/main/java/com/earth2me/essentials/UserData.java
+++ b/Essentials/src/main/java/com/earth2me/essentials/UserData.java
@@ -30,6 +30,7 @@
 import java.util.UUID;
 import java.util.logging.Level;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 import static com.earth2me.essentials.I18n.tl;
 
@@ -178,6 +179,12 @@ public List<String> getHomes() {
         return new ArrayList<>(holder.homes().keySet());
     }
 
+    public List<String> getHomesPerWorld(String worldName) {
+        return holder.homes().entrySet().stream()
+                .filter(homeLocation -> homeLocation.getValue().worldName().equalsIgnoreCase(worldName))
+                .map(Map.Entry::getKey).collect(Collectors.toList());
+    }
+
     public void setHome(String name, final Location loc) {
         //Invalid names will corrupt the yaml
         name = StringUtil.safeString(name);
diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java
index b1754d1e1ab..0dc62988484 100644
--- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java
+++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java
@@ -10,9 +10,7 @@
 import org.bukkit.Server;
 import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
 
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
+import java.util.*;
 import java.util.concurrent.CompletableFuture;
 
 import static com.earth2me.essentials.I18n.tl;
@@ -131,6 +129,9 @@ private void goHome(final User user, final User player, final String home, final
         if (user.getWorld() != loc.getWorld() && ess.getSettings().isWorldHomePermissions() && !user.isAuthorized("essentials.worlds." + loc.getWorld().getName())) {
             throw new Exception(tl("noPerm", "essentials.worlds." + loc.getWorld().getName()));
         }
+        if(!isUserHomeInWorldGroupWorld(user.getWorld().getName(), Objects.requireNonNull(loc.getWorld()).getName())) {
+            throw new Exception(tl("teleportNotPossible"));
+        }
         final UserTeleportHomeEvent event = new UserTeleportHomeEvent(user, home, loc, UserTeleportHomeEvent.HomeType.HOME);
         user.getServer().getPluginManager().callEvent(event);
         if (!event.isCancelled()) {
@@ -143,6 +144,18 @@ private void goHome(final User user, final User player, final String home, final
         }
     }
 
+    private boolean isUserHomeInWorldGroupWorld(String worldFrom, String worldTo) {
+        Set<String> worldGroups = ess.getSettings().getHomesPerWorldGroup();
+
+        for(String wGroup : worldGroups) {
+            Set<String> worldsPerWG = ess.getSettings().getWorldGroupHomeList(wGroup);
+
+            if(worldsPerWG.contains(worldFrom) && worldsPerWG.contains(worldTo))
+                return true;
+        }
+        return false;
+    }
+
     @Override
     protected List<String> getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) {
         final boolean canVisitOthers = user.isAuthorized("essentials.home.others");
diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandsethome.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandsethome.java
index 5033b276233..459ea6a7ba3 100644
--- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandsethome.java
+++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandsethome.java
@@ -12,6 +12,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
+import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
 import static com.earth2me.essentials.I18n.tl;
@@ -90,7 +91,10 @@ public void run(final Server server, final User user, final String commandLabel,
     private boolean checkHomeLimit(final User user, final User usersHome, final String name) throws Exception {
         if (!user.isAuthorized("essentials.sethome.multiple.unlimited")) {
             final int limit = ess.getSettings().getHomeLimit(user);
-            if (usersHome.getHomes().size() >= limit) {
+            List<String> homes = usersHome.isReachable() ?
+                    usersHome.getHomesPerWorld(Objects.requireNonNull(usersHome.getLocation().getWorld()).getName())
+                    : usersHome.getHomes();
+            if (homes.size() >= limit) {
                 if (usersHome.getHomes().contains(name)) {
                     return false;
                 }
diff --git a/Essentials/src/main/resources/messages.properties b/Essentials/src/main/resources/messages.properties
index 1e347af755d..f4acf883fa5 100644
--- a/Essentials/src/main/resources/messages.properties
+++ b/Essentials/src/main/resources/messages.properties
@@ -1272,6 +1272,7 @@ teleportDisabled=\u00a7c{0} \u00a74has teleportation disabled.
 teleportHereRequest=\u00a7c{0}\u00a76 has requested that you teleport to them.
 teleportHome=\u00a76Teleporting to \u00a7c{0}\u00a76.
 teleporting=\u00a76Teleporting...
+teleportNotPossible=\u00a7cTeleportation to that location is not possible from this world
 teleportInvalidLocation=Value of coordinates cannot be over 30000000
 teleportNewPlayerError=\u00a74Failed to teleport new player\!
 teleportNoAcceptPermission=\u00a7c{0} \u00a74does not have permission to accept teleport requests.

From 98f1dfe6ce6ce97c28736311f8f5abc469f7fa6f Mon Sep 17 00:00:00 2001
From: Tadeas Drab <tadeas.drab@student.tuke.sk>
Date: Sat, 16 Dec 2023 22:33:57 +0100
Subject: [PATCH 3/5] Issue 2393 - Added tests, fixed mistakes

---
 .../com/earth2me/essentials/ISettings.java    |   4 +-
 .../com/earth2me/essentials/Settings.java     |   6 +-
 .../essentials/commands/Commandhome.java      |  17 +-
 .../essentials/commands/Commandsethome.java   |   2 +-
 .../com/earth2me/essentials/HomeTest.java     | 166 ++++++++++++++++++
 5 files changed, 183 insertions(+), 12 deletions(-)
 create mode 100644 Essentials/src/test/java/com/earth2me/essentials/HomeTest.java

diff --git a/Essentials/src/main/java/com/earth2me/essentials/ISettings.java b/Essentials/src/main/java/com/earth2me/essentials/ISettings.java
index 1d856adcfd3..75ec1c54349 100644
--- a/Essentials/src/main/java/com/earth2me/essentials/ISettings.java
+++ b/Essentials/src/main/java/com/earth2me/essentials/ISettings.java
@@ -105,7 +105,7 @@ public interface ISettings extends IConf {
 
     Set getMultipleHomes();
 
-    Set<String>  getHomesPerWorld();
+    Set<String> getHomesPerWorld();
 
     Set<String> getHomesPerWorldGroup();
 
@@ -115,7 +115,7 @@ public interface ISettings extends IConf {
 
     int getWorldGroupHomeLimit(String set);
 
-    Set<String>  getWorldGroupHomeList(String set);
+    Set<String> getWorldGroupHomeList(String set);
 
     boolean isHomeLimitPerWorldEnabled();
 
diff --git a/Essentials/src/main/java/com/earth2me/essentials/Settings.java b/Essentials/src/main/java/com/earth2me/essentials/Settings.java
index ff0243263f2..6a39a5494a1 100644
--- a/Essentials/src/main/java/com/earth2me/essentials/Settings.java
+++ b/Essentials/src/main/java/com/earth2me/essentials/Settings.java
@@ -231,11 +231,11 @@ public int getHomeLimit(final User user) {
         return limit;
     }
 
-    private boolean isUserInWorld(User user, String worldName) {
-        return Objects.requireNonNull(user.getLocation().getWorld()).getName().equalsIgnoreCase(worldName);
+    public boolean isUserInWorld(User user, String worldName) {
+        return Objects.requireNonNull(user.getWorld()).getName().equalsIgnoreCase(worldName);
     }
 
-    private boolean isUserInWorldGroup(User user, String worldGroup) {
+    public boolean isUserInWorldGroup(User user, String worldGroup) {
         return getWorldGroupHomeList(worldGroup).stream().anyMatch(worldName -> isUserInWorld(user, worldName));
     }
 
diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java
index 0dc62988484..d6e23736cfa 100644
--- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java
+++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java
@@ -10,8 +10,13 @@
 import org.bukkit.Server;
 import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
 
-import java.util.*;
+import java.util.Locale;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.Collections;
 import java.util.concurrent.CompletableFuture;
+import java.util.logging.Level;
 
 import static com.earth2me.essentials.I18n.tl;
 
@@ -118,8 +123,8 @@ private String getHomeLimit(final User player) {
         return Integer.toString(ess.getSettings().getHomeLimit(player));
     }
 
-    private void goHome(final User user, final User player, final String home, final Trade charge, final CompletableFuture<Boolean> future) throws Exception {
-        if (home.length() < 1) {
+    public void goHome(final User user, final User player, final String home, final Trade charge, final CompletableFuture<Boolean> future) throws Exception {
+        if (home.isEmpty()) {
             throw new NotEnoughArgumentsException();
         }
         final Location loc = player.getHome(home);
@@ -144,11 +149,11 @@ private void goHome(final User user, final User player, final String home, final
         }
     }
 
-    private boolean isUserHomeInWorldGroupWorld(String worldFrom, String worldTo) {
-        Set<String> worldGroups = ess.getSettings().getHomesPerWorldGroup();
+    public boolean isUserHomeInWorldGroupWorld(String worldFrom, String worldTo) {
+        final Set<String> worldGroups = ess.getSettings().getHomesPerWorldGroup();
 
         for(String wGroup : worldGroups) {
-            Set<String> worldsPerWG = ess.getSettings().getWorldGroupHomeList(wGroup);
+            final Set<String> worldsPerWG = ess.getSettings().getWorldGroupHomeList(wGroup);
 
             if(worldsPerWG.contains(worldFrom) && worldsPerWG.contains(worldTo))
                 return true;
diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandsethome.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandsethome.java
index 459ea6a7ba3..5022816f3a8 100644
--- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandsethome.java
+++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandsethome.java
@@ -91,7 +91,7 @@ public void run(final Server server, final User user, final String commandLabel,
     private boolean checkHomeLimit(final User user, final User usersHome, final String name) throws Exception {
         if (!user.isAuthorized("essentials.sethome.multiple.unlimited")) {
             final int limit = ess.getSettings().getHomeLimit(user);
-            List<String> homes = usersHome.isReachable() ?
+            final List<String> homes = usersHome.isReachable() ?
                     usersHome.getHomesPerWorld(Objects.requireNonNull(usersHome.getLocation().getWorld()).getName())
                     : usersHome.getHomes();
             if (homes.size() >= limit) {
diff --git a/Essentials/src/test/java/com/earth2me/essentials/HomeTest.java b/Essentials/src/test/java/com/earth2me/essentials/HomeTest.java
new file mode 100644
index 00000000000..46a056f7557
--- /dev/null
+++ b/Essentials/src/test/java/com/earth2me/essentials/HomeTest.java
@@ -0,0 +1,166 @@
+package com.earth2me.essentials;
+
+import com.earth2me.essentials.commands.Commandhome;
+import com.earth2me.essentials.commands.EssentialsCommand;
+import org.bukkit.World;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.internal.util.reflection.FieldSetter;
+
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+
+import static org.junit.Assert.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class HomeTest {
+
+    @Mock
+    Settings settings;
+    @Mock
+    Commandhome commandhome;
+    @InjectMocks
+    net.ess3.api.IEssentials essentials;
+
+    @Before
+    public void before() throws Exception {
+        settings = mock(Settings.class);
+        commandhome = mock(Commandhome.class);
+        essentials = mock(net.ess3.api.IEssentials.class);
+
+        when(settings.getWorldGroupHomeLimit("test-wg")).thenReturn(7);
+        when(settings.getWorldHomeLimit("world_the_end")).thenReturn(6);
+        when(settings.getWorldGroupHomeList("test-wg")).thenReturn(new HashSet<>(Arrays.asList("world", "world_nether")));
+        when(settings.getHomeLimit(any(User.class))).thenCallRealMethod();
+        when(settings.getHomeLimit("default")).thenReturn(3);
+        when(settings.isHomeLimitPerWorldEnabled()).thenReturn(false);
+        when(settings.getMultipleHomes()).thenReturn(new HashSet<>(Collections.singletonList("vip")));
+        when(settings.getHomeLimit("vip")).thenReturn(5);
+        when(settings.getHomesPerWorldGroup()).thenReturn(new HashSet<>(Collections.singletonList("test-wg")));
+        when(settings.getHomesPerWorld()).thenReturn(new HashSet<>(Collections.singletonList("world_the_end")));
+        when(settings.isUserInWorld(any(User.class), any(String.class))).thenCallRealMethod();
+        when(settings.isUserInWorldGroup(any(User.class), any(String.class))).thenCallRealMethod();
+
+        when(essentials.getSettings()).thenReturn(settings);
+        commandhome.setEssentials(essentials);
+        when(commandhome.isUserHomeInWorldGroupWorld(any(String.class), any(String.class))).thenCallRealMethod();
+    }
+
+    @Test
+    public void test_home_limit_without_multiple_perm() {
+        final User user = mock(User.class);
+
+        when(user.isAuthorized("essentials.sethome.multiple")).thenReturn(false);
+
+        assertEquals(1, settings.getHomeLimit(user));
+    }
+
+    @Test
+    public void test_home_limit_without_world_home_limit() {
+        final User user = mock(User.class);
+
+        when(settings.isHomeLimitPerWorldEnabled()).thenReturn(false);
+
+        when(user.isAuthorized("essentials.sethome.multiple")).thenReturn(true);
+
+        assertEquals(3, settings.getHomeLimit(user));
+    }
+
+    @Test
+    public void test_home_limit_without_world_home_limit_vip() {
+        final User user = mock(User.class);
+
+        when(settings.isHomeLimitPerWorldEnabled()).thenReturn(false);
+
+        when(user.isAuthorized("essentials.sethome.multiple")).thenReturn(true);
+        when(user.isAuthorized("essentials.sethome.multiple.vip")).thenReturn(true);
+
+        assertEquals(5, settings.getHomeLimit(user));
+    }
+
+    @Test
+    public void test_home_limit_with_world_home_limit() {
+        final User user = mock(User.class);
+        final World world = mock(World.class);
+
+        when(settings.isHomeLimitPerWorldEnabled()).thenReturn(true);
+
+        when(user.isAuthorized("essentials.sethome.multiple")).thenReturn(true);
+        when(user.isAuthorized("essentials.sethome.multiple.vip")).thenReturn(false);
+        when(user.getWorld()).thenReturn(world);
+        when(world.getName()).thenReturn("world_the_end");
+
+        assertEquals(6, settings.getHomeLimit(user));
+    }
+
+    @Test
+    public void test_home_limit_with_world_home_limit_default() {
+        final User user = mock(User.class);
+        final World world = mock(World.class);
+
+        when(settings.isHomeLimitPerWorldEnabled()).thenReturn(true);
+
+        when(user.isAuthorized("essentials.sethome.multiple")).thenReturn(true);
+        when(user.isAuthorized("essentials.sethome.multiple.vip")).thenReturn(false);
+        when(user.getWorld()).thenReturn(world);
+        when(world.getName()).thenReturn("world");
+
+        assertEquals(3, settings.getHomeLimit(user));
+    }
+
+    @Test
+    public void test_home_limit_with_world_group_home_limit() {
+        final User user = mock(User.class);
+        final World world = mock(World.class);
+
+        when(settings.isHomeLimitPerWorldEnabled()).thenReturn(true);
+        when(settings.isHomeLimitPerWorldGroupEnabled()).thenReturn(true);
+
+        when(user.isAuthorized("essentials.sethome.multiple")).thenReturn(true);
+        when(user.isAuthorized("essentials.sethome.multiple.vip")).thenReturn(false);
+        when(user.getWorld()).thenReturn(world);
+        when(world.getName()).thenReturn("world");
+
+        assertEquals(7, settings.getHomeLimit(user));
+    }
+
+    @Test
+    public void test_home_limit_with_world_group_home_limit_outside_default() {
+        final User user = mock(User.class);
+        final World world = mock(World.class);
+
+        when(settings.isHomeLimitPerWorldEnabled()).thenReturn(true);
+        when(settings.isHomeLimitPerWorldGroupEnabled()).thenReturn(true);
+
+        when(user.isAuthorized("essentials.sethome.multiple")).thenReturn(true);
+        when(user.isAuthorized("essentials.sethome.multiple.vip")).thenReturn(false);
+        when(user.getWorld()).thenReturn(world);
+        when(world.getName()).thenReturn("world_another");
+
+        assertEquals(3, settings.getHomeLimit(user));
+    }
+
+    @Test
+    public void test_home_in_wg() throws NoSuchFieldException {
+        Field field = EssentialsCommand.class.getDeclaredField("ess");
+        field.setAccessible(true);
+        FieldSetter.setField(commandhome, field, essentials);
+
+        assertTrue(commandhome.isUserHomeInWorldGroupWorld("world", "world_nether"));
+    }
+
+    @Test
+    public void test_home_not_in_wg() throws NoSuchFieldException {
+        Field field = EssentialsCommand.class.getDeclaredField("ess");
+        field.setAccessible(true);
+        FieldSetter.setField(commandhome, field, essentials);
+
+        assertFalse(commandhome.isUserHomeInWorldGroupWorld("world", "world_the_end"));
+    }
+}
\ No newline at end of file

From 756fb995ba2cd59ac94ab8cc7d4c73eb3f86888e Mon Sep 17 00:00:00 2001
From: Tadeas Drab <tadeas.drab@student.tuke.sk>
Date: Sat, 16 Dec 2023 22:41:47 +0100
Subject: [PATCH 4/5] Issue 2393 - Fixed styling mistakes

---
 .../essentials/commands/Commandhome.java      |  1 -
 .../com/earth2me/essentials/HomeTest.java     | 28 ++++++++++---------
 2 files changed, 15 insertions(+), 14 deletions(-)

diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java
index d6e23736cfa..8087f286536 100644
--- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java
+++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java
@@ -16,7 +16,6 @@
 import java.util.Set;
 import java.util.Collections;
 import java.util.concurrent.CompletableFuture;
-import java.util.logging.Level;
 
 import static com.earth2me.essentials.I18n.tl;
 
diff --git a/Essentials/src/test/java/com/earth2me/essentials/HomeTest.java b/Essentials/src/test/java/com/earth2me/essentials/HomeTest.java
index 46a056f7557..3a09da4cf67 100644
--- a/Essentials/src/test/java/com/earth2me/essentials/HomeTest.java
+++ b/Essentials/src/test/java/com/earth2me/essentials/HomeTest.java
@@ -14,7 +14,9 @@
 import java.util.Collections;
 import java.util.HashSet;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -53,7 +55,7 @@ public void before() throws Exception {
     }
 
     @Test
-    public void test_home_limit_without_multiple_perm() {
+    public void testHomeLimitWithoutMultiplePerm() {
         final User user = mock(User.class);
 
         when(user.isAuthorized("essentials.sethome.multiple")).thenReturn(false);
@@ -62,7 +64,7 @@ public void test_home_limit_without_multiple_perm() {
     }
 
     @Test
-    public void test_home_limit_without_world_home_limit() {
+    public void testHomeLimitWithoutWorldHomeLimit() {
         final User user = mock(User.class);
 
         when(settings.isHomeLimitPerWorldEnabled()).thenReturn(false);
@@ -73,7 +75,7 @@ public void test_home_limit_without_world_home_limit() {
     }
 
     @Test
-    public void test_home_limit_without_world_home_limit_vip() {
+    public void testHomeLimitWithoutWorldHomeLimitVip() {
         final User user = mock(User.class);
 
         when(settings.isHomeLimitPerWorldEnabled()).thenReturn(false);
@@ -85,7 +87,7 @@ public void test_home_limit_without_world_home_limit_vip() {
     }
 
     @Test
-    public void test_home_limit_with_world_home_limit() {
+    public void testHomeLimitWithWorldHomeLimit() {
         final User user = mock(User.class);
         final World world = mock(World.class);
 
@@ -100,7 +102,7 @@ public void test_home_limit_with_world_home_limit() {
     }
 
     @Test
-    public void test_home_limit_with_world_home_limit_default() {
+    public void testHomeLimitWithWorldHomeLimitDefault() {
         final User user = mock(User.class);
         final World world = mock(World.class);
 
@@ -115,7 +117,7 @@ public void test_home_limit_with_world_home_limit_default() {
     }
 
     @Test
-    public void test_home_limit_with_world_group_home_limit() {
+    public void testHomeLimitWithWorldGroupHomeLimit() {
         final User user = mock(User.class);
         final World world = mock(World.class);
 
@@ -131,7 +133,7 @@ public void test_home_limit_with_world_group_home_limit() {
     }
 
     @Test
-    public void test_home_limit_with_world_group_home_limit_outside_default() {
+    public void testHomeLimitWithWorldGroupHomeLimitOutsideDefault() {
         final User user = mock(User.class);
         final World world = mock(World.class);
 
@@ -147,8 +149,8 @@ public void test_home_limit_with_world_group_home_limit_outside_default() {
     }
 
     @Test
-    public void test_home_in_wg() throws NoSuchFieldException {
-        Field field = EssentialsCommand.class.getDeclaredField("ess");
+    public void testHomeInWG() throws NoSuchFieldException {
+        final Field field = EssentialsCommand.class.getDeclaredField("ess");
         field.setAccessible(true);
         FieldSetter.setField(commandhome, field, essentials);
 
@@ -156,11 +158,11 @@ public void test_home_in_wg() throws NoSuchFieldException {
     }
 
     @Test
-    public void test_home_not_in_wg() throws NoSuchFieldException {
-        Field field = EssentialsCommand.class.getDeclaredField("ess");
+    public void testHomeNotInWG() throws NoSuchFieldException {
+        final Field field = EssentialsCommand.class.getDeclaredField("ess");
         field.setAccessible(true);
         FieldSetter.setField(commandhome, field, essentials);
 
         assertFalse(commandhome.isUserHomeInWorldGroupWorld("world", "world_the_end"));
     }
-}
\ No newline at end of file
+}

From 3da872b1f99180982b9aa97eeb867887e2b5690b Mon Sep 17 00:00:00 2001
From: Tadeas Drab <tadeas.drab@student.tuke.sk>
Date: Sun, 17 Dec 2023 10:50:49 +0100
Subject: [PATCH 5/5] Issue 2393 - Added missing check for home

---
 .../essentials/commands/Commandhome.java      | 23 ++++++++----
 .../com/earth2me/essentials/HomeTest.java     | 35 +++++++++++++++++--
 2 files changed, 48 insertions(+), 10 deletions(-)

diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java
index 8087f286536..fb5d5728863 100644
--- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java
+++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandhome.java
@@ -133,7 +133,7 @@ public void goHome(final User user, final User player, final String home, final
         if (user.getWorld() != loc.getWorld() && ess.getSettings().isWorldHomePermissions() && !user.isAuthorized("essentials.worlds." + loc.getWorld().getName())) {
             throw new Exception(tl("noPerm", "essentials.worlds." + loc.getWorld().getName()));
         }
-        if(!isUserHomeInWorldGroupWorld(user.getWorld().getName(), Objects.requireNonNull(loc.getWorld()).getName())) {
+        if(!isUserHomeInWorldOrWorldGroupWorld(user.getWorld().getName(), Objects.requireNonNull(loc.getWorld()).getName())) {
             throw new Exception(tl("teleportNotPossible"));
         }
         final UserTeleportHomeEvent event = new UserTeleportHomeEvent(user, home, loc, UserTeleportHomeEvent.HomeType.HOME);
@@ -148,14 +148,23 @@ public void goHome(final User user, final User player, final String home, final
         }
     }
 
-    public boolean isUserHomeInWorldGroupWorld(String worldFrom, String worldTo) {
-        final Set<String> worldGroups = ess.getSettings().getHomesPerWorldGroup();
+    public boolean isUserHomeInWorldOrWorldGroupWorld(String worldFrom, String worldTo) {
+        final boolean isHomeLimitPerWorldEnabled = ess.getSettings().isHomeLimitPerWorldEnabled();
+        final boolean isHomeLimitPerWorldGroupEnabled = ess.getSettings().isHomeLimitPerWorldGroupEnabled();
+        if(!isHomeLimitPerWorldEnabled) {
+            return true;
+        }
+        if(isHomeLimitPerWorldGroupEnabled) {
+            final Set<String> worldGroups = ess.getSettings().getHomesPerWorldGroup();
 
-        for(String wGroup : worldGroups) {
-            final Set<String> worldsPerWG = ess.getSettings().getWorldGroupHomeList(wGroup);
+            for (String wGroup : worldGroups) {
+                final Set<String> worldsPerWG = ess.getSettings().getWorldGroupHomeList(wGroup);
 
-            if(worldsPerWG.contains(worldFrom) && worldsPerWG.contains(worldTo))
-                return true;
+                if (worldsPerWG.contains(worldFrom) && worldsPerWG.contains(worldTo))
+                    return true;
+            }
+        }else{
+            return worldFrom.equalsIgnoreCase(worldTo);
         }
         return false;
     }
diff --git a/Essentials/src/test/java/com/earth2me/essentials/HomeTest.java b/Essentials/src/test/java/com/earth2me/essentials/HomeTest.java
index 3a09da4cf67..4408ff0c6ed 100644
--- a/Essentials/src/test/java/com/earth2me/essentials/HomeTest.java
+++ b/Essentials/src/test/java/com/earth2me/essentials/HomeTest.java
@@ -51,7 +51,7 @@ public void before() throws Exception {
 
         when(essentials.getSettings()).thenReturn(settings);
         commandhome.setEssentials(essentials);
-        when(commandhome.isUserHomeInWorldGroupWorld(any(String.class), any(String.class))).thenCallRealMethod();
+        when(commandhome.isUserHomeInWorldOrWorldGroupWorld(any(String.class), any(String.class))).thenCallRealMethod();
     }
 
     @Test
@@ -150,19 +150,48 @@ public void testHomeLimitWithWorldGroupHomeLimitOutsideDefault() {
 
     @Test
     public void testHomeInWG() throws NoSuchFieldException {
+        when(settings.isHomeLimitPerWorldEnabled()).thenReturn(true);
+        when(settings.isHomeLimitPerWorldGroupEnabled()).thenReturn(true);
+
         final Field field = EssentialsCommand.class.getDeclaredField("ess");
         field.setAccessible(true);
         FieldSetter.setField(commandhome, field, essentials);
 
-        assertTrue(commandhome.isUserHomeInWorldGroupWorld("world", "world_nether"));
+        assertTrue(commandhome.isUserHomeInWorldOrWorldGroupWorld("world", "world_nether"));
     }
 
     @Test
     public void testHomeNotInWG() throws NoSuchFieldException {
+        when(settings.isHomeLimitPerWorldEnabled()).thenReturn(true);
+        when(settings.isHomeLimitPerWorldGroupEnabled()).thenReturn(true);
+        final Field field = EssentialsCommand.class.getDeclaredField("ess");
+        field.setAccessible(true);
+        FieldSetter.setField(commandhome, field, essentials);
+
+        assertFalse(commandhome.isUserHomeInWorldOrWorldGroupWorld("world", "world_the_end"));
+    }
+
+    @Test
+    public void testHomeInWorld() throws NoSuchFieldException {
+        when(settings.isHomeLimitPerWorldEnabled()).thenReturn(true);
+        when(settings.isHomeLimitPerWorldGroupEnabled()).thenReturn(false);
+
+        final Field field = EssentialsCommand.class.getDeclaredField("ess");
+        field.setAccessible(true);
+        FieldSetter.setField(commandhome, field, essentials);
+
+        assertTrue(commandhome.isUserHomeInWorldOrWorldGroupWorld("world", "world"));
+    }
+
+    @Test
+    public void testHomeNotInWorld() throws NoSuchFieldException {
+        when(settings.isHomeLimitPerWorldEnabled()).thenReturn(true);
+        when(settings.isHomeLimitPerWorldGroupEnabled()).thenReturn(false);
+
         final Field field = EssentialsCommand.class.getDeclaredField("ess");
         field.setAccessible(true);
         FieldSetter.setField(commandhome, field, essentials);
 
-        assertFalse(commandhome.isUserHomeInWorldGroupWorld("world", "world_the_end"));
+        assertFalse(commandhome.isUserHomeInWorldOrWorldGroupWorld("world", "world_nether"));
     }
 }