传送到下一位玩家

时间:2018-06-07 01:57:05

标签: java bukkit

我正在制作一个Spigot 1.8.9插件,我正在尝试添加一项功能,当工作人员右键单击一个项目时,它会将它传送给下一个没有消失的玩家而不是他们自己,如果没有任何它应该返回null。

点击后,我尝试使用

将所有可能的用户添加到列表中
public static List<User> getPossibleUsers(User user){
    List<User> result = new ArrayList<>();
    for(User target : users)
        if(!target.isVanished() && !user.getUUID().equals(target.getUUID()))
            result.add(target);
    return result;
}

还为工作人员分配了一个名为nextPlayer的int,登录时设置为0。然后当他们点击我添加一个到int时,所以下次他们点击它可以得到下一个用户。

private User getNextPlayer(User user) {
    int next = user.nextPlayer;
    List<User> users = getPossibleUsers(user);
    if(users.size() == 0)
        return null;
    int current = 0;
    for(User target : users) {
        if(current == next){
           return target;
        }
        current++;
    }
    user.nextPlayer = next;
}

问题是我不知道如何正确制作getNextPlayer方法并使其高效。我也想这样做,一旦它击中最后一个玩家,它就会循环回到第一个玩家。

1 个答案:

答案 0 :(得分:1)

如果你希望它的效率高,我建议完全不同地考虑你的问题,但在这种情况下,效率确实不是很重要,所以我选择不进行预先优化和而是使用您已有的代码。

public static List<User> getPossibleUsers(User user){
    List<User> result = new ArrayList<>();
    for(User target : users)
        if(!target.isVanished() && !user.getUUID().equals(target.getUUID()))
            result.add(target);
    return result;
}

目前以相同的顺序返回用户,因为它们是在用户上定义的。

这更好地具有自然排序顺序,否则当人们加入/离开服务器时你会遇到问题,因为它会导致人们改变他们在列表中的排序。

现在让我们回到第一位校长。

    int next = user.nextPlayer;

您似乎将玩家的索引存储在“用户”所在的列表中。

完成后,您可以直接从列表中访问该索引。

https://docs.oracle.com/javase/8/docs/api/java/util/List.html#get-int-

E get(int index)

因此,只需执行users.get(next++);即可修复&#39;你上面的代码。接下来递增,并让用户在该位置(假设排序是一致的,并且没有改变)但是,如果它超出了列表的范围,它可能会抛出异常,所以我们将它包装起来在

if(next <= users.length) {
    users.get(next++);
} else return null;

这会将其更改为返回null,否则会抛出异常。

但是所有这些仍有一个致命的缺陷,如果列表在调用之间发生变异,那么您可能会跳过或更改订单。

更好的解决方案是改为访问受访用户以及上次访问的用户。

如果订购了用户,并且存储了上次访问的用户而不是索引,那么您存储的数据对于更改更具弹性,并且更符合您所需的行为。

为了更贴近您的需求,您要问的是。

  1. 生成一个可预测的,有序的用户列表,其中不包含管理员或其他任何已消失的用户,以帮助管理员预测他们的目的地。

  2. 通过右键单击工具旋转此列表,(注意这是异步,因此需要保存所有状态)

  3. 确保在重复序列之前访问所有访问过的用户。

  4. public class TeleportTooldata {
        private ListIterator<UUID> cursor;
        private List<UUID> cachedOrder;
    
        public TeleportTooldata(List<UUID> applicableUsers) {
            cachedOrder = applicableUsers;
        }
    
        @Nullable
        public UUID next() {
            if (!cursor.hasNext()) return null;
            UUID next = cursor.next();
            if (!cachedOrder.contains(next)) {
                cachedOrder.add(next);
            }
            return next;
        }
    
        public void Update(List<UUID> applicableUsers) {
            applicableUsers.removeAll(cachedOrder);
            cachedOrder.addAll(applicableUsers);
        }
    }
    
    public class TeleportToolUtil {
        YourPluginUserRepo repo;
        Map<User, TeleportTooldata> storage; //This could be a cache, make sure to remove if they log out, or maybe timed as well.
    
        public List<UUID> getApplicableUsers() {
            return repo.getOnlineUsers().stream()
                    .filter(User::isVanish)
                    .sorted(Comparator.comparing(User::getId)) // You can change the sort order
                    .map(User::getId)
                    .collect(Collectors.toList());
        }
    
        public void onToolUse(User user) {
            TeleportTooldata data = storage.computeIfAbsent(user, x -> new TeleportTooldata(getApplicableUsers()));
            UUID next = data.next();
            if (next == null) {
                data.Update(getApplicableUsers());
                next = data.next();
                    if(next == null) {
                    storage.put(user, new TeleportTooldata(getApplicableUsers()));
                    next = data.next();
                }
            }
            user.teleportTo(next);
        }
    }
    

    public class TeleportTooldata { private ListIterator<UUID> cursor; private List<UUID> cachedOrder; public TeleportTooldata(List<UUID> applicableUsers) { cachedOrder = applicableUsers; } @Nullable public UUID next() { if (!cursor.hasNext()) return null; UUID next = cursor.next(); if (!cachedOrder.contains(next)) { cachedOrder.add(next); } return next; } public void Update(List<UUID> applicableUsers) { applicableUsers.removeAll(cachedOrder); cachedOrder.addAll(applicableUsers); } } public class TeleportToolUtil { YourPluginUserRepo repo; Map<User, TeleportTooldata> storage; //This could be a cache, make sure to remove if they log out, or maybe timed as well. public List<UUID> getApplicableUsers() { return repo.getOnlineUsers().stream() .filter(User::isVanish) .sorted(Comparator.comparing(User::getId)) // You can change the sort order .map(User::getId) .collect(Collectors.toList()); } public void onToolUse(User user) { TeleportTooldata data = storage.computeIfAbsent(user, x -> new TeleportTooldata(getApplicableUsers())); UUID next = data.next(); if (next == null) { data.Update(getApplicableUsers()); next = data.next(); if(next == null) { storage.put(user, new TeleportTooldata(getApplicableUsers())); next = data.next(); } } user.teleportTo(next); } }

    一些变化。

    1. 我们现在正在缓存订购,因此您可以从概念上让用户在列表中向后移动。
    2. 我们正在使用ListIterator。 ListIterator是一个循环遍历列表的对象,并为您存储当前位置!就像你之前做的一样,但没有索引。
    3. 我们现在有可能更新数据,如果玩家加入的时间很晚,或者有人小便,如果他们不在里面,他们将被放在列表的后面。
    4. 当我们用完用户时,我们会尝试更新,如果我们真的出局,我们会重新开始一个全新的列表。 (请注意,这不会保证每次都保持相同的顺序(如果之前已经附加了,那么人们将在更新时正确排序,但它对于此用例来说足够接近)
    5. 然而!我们仍然需要注意内存泄漏。使用UUID而不是玩家或用户,意味着这个类非常轻,我们应该非常安全地避免UUID AS LONG列表中的内存泄漏,因为TeleportTooldata不会活得太久。

      您可以使用缓存(可能来自Guava?)替换TeleportTooldata地图,以便在管理员离开游戏后的某个时间删除数据。

      如果TeleportTooldata预计会长寿,我们会认真考虑从历史中删除UUID。

      另外,在我的示例中没有处理,是在缓存订单后用户可能会脱机。

      为了解决这个问题,在传送播放器之前,检查uuid是否在线,否则请转到下一个播放器。并再次遵循所有相同的逻辑。